Creare una finestra mediante le funzioni del API di SDL

Da Gambas-it.org - Wikipedia.

Va precisato che ormai la Libreria SDL è obsoleta: al suo posto va utilizzata la Libreria SDL2.


SDL mette a disposizione alcune librerie contenenti varie funzioni che consentono, fra l'altro, anche la creazione di finestre ove visualizzare testo, immagini ed altro.


Per creare e gestire in modo adeguato le finestre, bisognerà servirsi delle seguenti attuali librerie condivise .so:

  • libSDL-1.2.so.0.11.4 per la gestione generale delle finestre;
  • libSDL_image-1.2.so.0.8.4 per il caricamento e la gestione delle immagini nelle finestre;
  • libSDL_ttf-2.0.so.0.10.1 per la gestione del testo.

Le predette librerie andranno dichiarate ovviamente nelle forme previste dal protocollo Gambas.


Inizializzazione della libreria generale SDL

Prima ancora che si possa utilizzare ogni altra funzione esterna, bisognerà inizializzare la libreria libSDL-1.2.so.0.11.4 mediante la funzione:

int SDL_Init(Uint32 flags)

che in Gambas sarà così dichiarata:

Private Extern SDL_Init(flags As Integer) As Integer

ed in routine potrà essere richiamata come segue:

err = SDL_Init(SDL_INIT_VIDEO)

laddove l'argomento contenuto è un valore numerico intero uguale a 32, che rappresenta il sub-sistema di SDL in particolare da inizializzare (in questo caso il sub-sistema video), e che potrà essere dichiarato come una costante al difuori ovviamente della routine:

 Private Const SDL_INIT_VIDEO As Byte = 32

La funzione ritorna zero se l'inizializzazione ha avuto successo, altrimenti ritorna un valore negativo.


Impostazione della modalità video

Si procederà quindi ad impostare la modalità video, e quindi di visualizzazione effettiva della finestra che si sta andando a creare, specificando la lunghezza, la larghezza ed i bits-per-pixel.
Si utilizzerà la funzione:

SDL_Surface* SDL_SetVideoMode(int width, int height, int bitsperpixel, Uint32 flags)

che in Gambas sarà così dichiarata:

Private Extern SDL_SetVideoMode(width As Integer, height As Integer, bitsperpixel As Integer, flags As Integer) As Pointer

ed in routine potrà essere richiamata - ad esempio - come segue:

 video = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE Or SDL_DOUBLEBUF)

V'è da precisare in via generale riguardo ai parametri previsti da questa funzione che:

  • bits-per-pixel rappresenta i bit desiderati per pixel della modalità video da impostare. Se BitsPerPixel è 0, viene assunta la modalità video uguale a quella corrente. Per la più comune modalità di 4 byte per pixel, è preferibile utilizzare un valore di BitsPerPixel uguale a 32.
  • flags rappresenta una modalità di impostazione video. Per una completa descrizione dei possibili valori che tale argomento può assumere, si rinvia alla specifica documentazione in internet. I valori specifici potranno essere rintracciati nel file: /usr/include/SDL/SDL_video.h.

La funzione restituisce un Puntatore allo specifico framebuffer SDL, con il quale gestire successivamente gli elementi e la vita della finestra.

Ottenere l'Id della finestra

Dopo la creazione della finestra è possibile ottenere qualche informazione, come il suo Id mediante la funzione:

int SDL_GetWMInfo(SDL_SysWMinfo *info)

laddove il suo unico parametro è un puntatore ad una Struttura così definita nel file /usr/include/SDL/SDL_syswm.h:

typedef struct {
  SDL_version version;
  int wimpVersion;    /* Wimp version running under */
  int taskHandle;     /* The RISCOS task handle */
  int window;         /* The RISCOS display window */
} SDL_SysWMinfo;

e che in Gambas sarà dichiarata come segue:

Public Struct info
  version As Pointer
  wimpVersion As Integer  '; / * Wimp version running under * /
  taskHandle As Integer    '/ * The RISCOS task handle * /
  windowId As Integer
End Struct

e che sarà gestita dalla apposita variabile del tipo di detta Struttura

Dim infoStrutt As New info

Il parametro della Struttura utile per conoscere l'Id della finestra sarà l'ultimo dei quattro, al cui valore si dovrà sottrarre un valore di 10.

Tornando alla funzione in questione, in Gambas essa sarà dichiarata con Extern come segue:

Private Extern SDL_GetWMInfo(SDL_SysWMinfo As Info) As Integer

ed utilizzata in routine ad esempio così:

SDL_GetWMInfo(inf)

Print infStrutt.windowId - 10


Caratterizzare la finestra

Così come creata sin ora, la finestra avrà le dimensioni impostate e sarà semplicemente di colore nero con un bordo grigio.
E' possibile apportare alcune modifiche per variare qualche attributo.

Impostare l'intestazione della finestra

Per impostare la critta che apparirà nell'intestazione della finestra, ossia sul lato superiore del suo bordo, si utilizzerà la seguente funzione:

void SDL_WM_SetCaption(const char *title, const char *icon)

laddove i parametri:

  • title è appunto una stringa del testo che sarà mostrato nell'intestazione;
  • icon è il percorso della eventuale icona collegata alla finestra medesima.

Va innanzitutto precisato che questa funzione è contenuta nella libreria libSDL_ttf-2.0.so.0.10.1, e pertanto in Gambas verrà posta dopo la dichiarazione della predetta libreria:

Library "libSDL_ttf-2.0:0.10.1"

e quindi sarà così dichiarata con Extern:

Private Extern SDL_WM_SetCaption(title As String, icon As String)

ed in routine potrà essere così richiamata:

SDL_WM_SetCaption("Intestazione della finestra", "/percorso/della/eventuale/immagine/icona.xxx")

Colorare la finestra

Sarà possibile colorare totalmente o in parte la superficie della finestra mediante la funzione:

int SDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color)

laddove i parametri:

  • *dst è il puntatore al framebuffer ottenuto dalla precedente funzione: "SDL_SetVideoMode";
  • *dstrect è una variabile di tipo Struttura contenente le coordinate e le dimensioni del riquadro colorato all'interno della superficie della finestra;
  • color è un valore numerico intero che rappresenta il colore prescelto.

In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_FillRect(sdlsurface As Pointer, dstrect As SDL_Rect, color As Integer)

ed in routine potrà essere così richiamata:

SDL_FillRect(video, riquadro, numero_colore)

La Struttura per impostare coordinate e dimensioni del riquadro colorato

Come detto, alla funzione "SDL_FillRect" andrà passata una variabile di tipo Struttura contenente le coordinate e le dimensioni del riquadro colorato che apparirà all'interno della superficie della finestra. Tale Struttura, che definisce un'area quadrangolare, è del tipo SDL_Rect come previsto e specificato nell'API di SDL, sarà dichiarato in Gambas nel modo seguente:

Public Struct SDL_Rect 
  x As Short
  y As Short
  w As Short
  h As Short
End Struct

ovviamente tale Struttura sarà come di consueto gestita attraverso un'apposita variabile che potrà - ad esempio - essere dichiarata (in routine se locale) come segue:

Dim riquadro As New SDL_Rect

Al di sotto della funzione, affinché il risultato sia effettivo, si dovrà utilizzare la seguente funzione:

int SDL_Flip(SDL_Surface* screen)

laddove il parametro screen è il puntatore al framebuffer ottenuto dalla precedente funzione: "SDL_SetVideoMode".
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_Flip(sdlsurface As Pointer)

ed in routine potrà essere così richiamata:

SDL_Flip(video)

Da sottolineare che, per poter utilizzare questa funzione, è indispensabile che sia stato impostato il valore SDL_DOUBLEBUF come modalità video nella precedente funzione "SDL_SetVideoMode", che consente di eseguire l'hardware flipping.


Caricare una immagine nella finestra

La finestra può contenere anche un'immagine. Bisognerà innanzitutto utilizzare la funzione:

SDL_RWops *SDL_RWFromFile(const char *file, const char *mode)

laddove i parametri:

  • *file è il percorso del file immagine da caricare;
  • *mode è una delle consuete modalità per aprire un file.

La funzione restituisce un puntatore al flusso di dati del file aperto.
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_RWFromFile($file As String, mode As String) As Pointer

ed in routine potrà essere così richiamata:

fl = SDL_RWFromFile("/percorso/della/immagine.bmp", "rb")

laddove la variabile fl sarà stata dichiarata come puntatore.


Quindi si richiamerà a seguire la funzione:

SDL_Surface * SDL_LoadBMP_RW(SDL_RWops *src, int freesrc)

laddove i parametri:

  • *src è il puntatore al file ottenuto dalla precedente funzione "SDL_LoadBMP_RW";
  • freesrc è un valore che se posto ad 1 chiuderà il file automaticamente dopo essere stato caricato.

La funzione ritorna un puntatore, con il quale poter gestire successivamente l'immagine.
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_LoadBMP_RW(image As Pointer, freesrc As Integer) As Pointer

ed in routine potrà essere così richiamata:

imago = SDL_LoadBMP_RW(fl, 1)

laddove la variabile imago sarà stata dichiarata come puntatore.


Dopo ciò, per far mostrare effettivamente l'immagine sulla superficie della finestra, si dovrà copiare sostanzialmente la superficie dell'immagine all'interno della finestra medesima, utilizzando la seguente funzione:

int SDL_UpperBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)

laddove i parametri:

  • *src è il puntatore ottenuto dalla precedente funzione "SDL_LoadBMP_RW";
  • *srcrect è la variabile di tipo Struttura, contenente le sole dimensioni del riquadro dell'immagine, e del tipo SDL_Rect (gia vista per la precedente funzione "SDL_FillRect'"). Se tale parametro è posto a Null, viene copiata l'intera superficie dell'immagine;
  • *dst è il puntatore al framebuffer ottenuto dalla precedente funzione: "SDL_SetVideoMode";
  • *dstrect è la variabile di tipo Struttura, contenente le sole coordinate del riquadro dell'immagine, e del tipo SDL_Rect. Se tale parametro è posto a Null, allora posizione di destinazione è: 0, 0 (ossia nell'angolo sinistro in alto).

Da sottolineare che questa funzione è contenuta nella libreria libSDL_image-1.2.so.0.8.4.
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_UpperBlit(image As Pointer, srcrect As Pointer, dst As Pointer, dstrect As SDL_Rect) As Integer In "libSDL_image-1.2:0.8.4"

ed in routine potrà essere - ad esempio - così richiamata:

SDL_UpperBlit(imago, Null, video, Null)

Aggiornare le modifiche apportate alla superficie della finestra

E' buona norma assicurarsi che ogni cambiamento apportato alla superficie della finestra (come appunto ad esempio il caricamento di un'immagine) sia consolidato con la funzione:

void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h)

laddove i parametri:

  • *screen è il puntatore al framebuffer ottenuto dalla precedente funzione: "SDL_SetVideoMode";
  • Sint32 x, Sint32 y, Sint32 w, Sint32 h sono le coordinate e le dimensioni dell'area che si va ad aggiornare.

Va sottolineato che se tali quattro ultimi parametri sono posti tutti a zero, la funzione aggiornerà l'intera superficie della finestra.
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_UpdateRect(sdlsurface As Pointer, x As Integer, y As Integer, w As Integer, h As Integer)

ed in routine sarà in astratto - ad esempio - così richiamata:

SDL_UpdateRect(video, 0, 0, valore W, valore H)

Ottenere alcune informazioni sull'immagine

E' possibile ottenere alcune informazioni sull'immagine caricata, in partticolar modo i valori in pixel delle sue dimensioni con almeno due modalità.

Uso dei Memory-Stream

Mediante la funzione Memory-Stream si andrà a leggere all'interno del puntatore ottenuto dalla funzione "SDL_LoadBMP_RW", cose segue:

Dim imago As Pointer
Dim ist As Stream
Dim iW, iH As Integer 

ist = Memory imago For Read
 Seek #ist, 16   ' Si legge al 17° byte e poi al 21° byte
 Read #ist, iW
  Print iW
 Read #ist, iH
  Print iH

Uso di una Struttura

In realtà l'API di SDL prevede che la variabile riempita dalla funzione "SDL_LoadBMP_RW" punti ad una Struttura, denominata "SDL_Surface", e dichiarata nel file /usr/include/SDL/SDL_video.h. Tale Struttura contiene anche i campi w e y che rappresentano appunto le dimensioni dell'immagine caricata.
Per poter leggere i suddetti due campi, si dovrà dichiarare la Struttura, anche parzialmente almeno fino a comprendere i due campi delle dimensioni:

Public Struct surface
  flags As Integer
  pixelformat As Pointer
  w As Integer
  y As Integer
End Struct

quindi dichiarare e creare un'apposita variabile del tipo di questa Struttura:

Dim imago As New surface

e procedere in routine alla lettura dei due valori:

With imago
  Print .w
  Print .y
End With


Inserire e gestire il testo all'interno della finestra

Fra le varie risorse è possibile far mostrare sulla superficie della finestra anche del testo.

Va precisato innanzitutto che si dovrà richiamare l'attuale libreria:

Library "libSDL_ttf-2.0:0.10.1"

e che andrà debitamente inizializzata con l'apposita funzione:

int TTF_Init()

In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern TTF_Init() As Integer

ed in routine potrà essere così richiamata:

If TTF_Init() < 0 Then Error.Raise("Errore alla funzione 'TTF_Init()' !")


Di seguito va impostato lo specifico font, che si intende utilizzare per il testo, mediante la seguente fuzione:

TTF_Font *TTF_OpenFont(const char *file, int ptsize)

laddove i parametri:

  • *file è il percorso del file font ttf;
  • ptsize è la dimensione che dovranno avere i caratteri.

La funzione ritorna un puntatore, con il quale poter gestire successivamente il font.
E' possibile attualmente caricare font di tipo TTF e FON. In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern TTF_OpenFont(percorsofont As String, ptsize As Integer) As Pointer

ed in routine sarà in astratto così richiamata:

font = TTF_OpenFont("/percorso/del/font.ttf", dimensione)

Impostare il testo

Per impostare il testo che dovrà essere mostrato sulla superficie della finestra, si potranno utilizzare varie funzioni, come ad esempio:

SDL_Surface *TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg)

ed altre simili, per le quali si rinvia alla documentazione in internet.
Dai test compiuti è possibile dire che testo con un aspetto migliore, più gradevole ed aggraziato, si otterrà mediante la funzione:

SDL_Surface *TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg)

e sue consimili, per le quali si rinvia alla documentazione in internet.
Riguardo ai suoi parametri:

  • *font è il puntatore al font, ottenuto dalla precedente funzione "TTF_OpenFont";
  • *text è l'effettivo testo da mostrare sulla superficie della finestra;
  • fg è un puntatore alla Struttura SDL_Color dichiarata nel file: /usr/include/SDL/SDL_video.h, e che specifica il colore del testo in RGB.


La funzione ritorna un puntatore alla nuova superficie della finestra, specificatamente contenente il testo, e che sarà utile alla gestone successiva del testo. In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern TTF_RenderText_Blended(fontP As Pointer, text As String, fg As Integer) As Pointer

ed in routine sarà in astratto così richiamata:

testo = TTF_RenderText_Blended(font, "Testo qualsiasi", valore numerico del colore)

Riguardo al terzo parametro di tale funzione va precisato che in Gambas va passato il valore numerico del colore (ad esempio per il colore rosso: 16711680 oppure in esadecimale: &FF0000).

Qualora si intenda inserire del testo con uno sfondo colorato, si dovrà inserire la seguente funzione:

SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg, SDL_Color bg)

sue consimili, per le quali si rinvia alla documentazione in internet.
Riguardo al terzo ed al quarto parametro della funzione va precisato che:

  • fg specifica il colore del testo;
  • bg specifica il colore dell'obreggiatura sottostante il testo.
  • entrambi i parametri in Gambas devono essere espressi nel valore numerico del rispettivo colore prescelto.

Al termine andrà richiamata la funzione, già vista sopra:

int SDL_UpperBlit(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)

laddove, però, in questo caso il primo parametro è il puntatore ritornato dalla funzione "TTF_RenderText_Blended".
Andrà inoltre richiamata la funzione:

int SDL_Flip(SDL_Surface* screen)

che aggiorna la superficie della finestra (è in vero possibile utilizzare anche la funzione già vista in precedenza "SDL_UpdateRect").
Il parametro * screen è il puntatore al framebuffer ottenuto dalla precedente funzione: "SDL_SetVideoMode".
Da precisare che questa funzione è contenuta nella libreria generale libSDL-1.2.so.0.11.4. In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_Flip(sdlsurface As Pointer)

ed in routine sarà così richiamata:

SDL_Flip(video)

Impostare la posizione del testo sulla superficie della finestra

Come già visto in precedenza, il parametro *dstrect della funzione "SDL_UpperBlit" si riferisce alle coordinate della posizione del riquadro (in questo caso il riquadro contenente il testo) ed è il puntatore ad una Struttura del tipo dichiarato SDL_Rect.
E' possibile scegliere il punto ove il testo sarà mostrato impostando le sue coordinate attraverso quella Struttura. In routine in Gambas, tenendo conto della dichiarazione della Struttura precedentemente vista, si potrà ad esempio scrivere come segue:

Dim riquadro As New SDL_Rect

  ......
  ......

  With riquadro
     .x = 130
     .y = 50
  End With

  SDL_UpperBlit(testo, Null, video, riquadro)
  
  SDL_Flip(video)


Distruggere una finestra

Per distruggere una finestra precedentemente creata, si farà uso della funzione:

void SDL_Quit(void)

contenuta nella libreria generale libSDL-1.2.so.0.11.4. In Gambas detta funzione sarà così dichiarata con Extern: Private Extern SDL_Quit() ed in routine sarà così richiamata:

SDL_Quit()


Sarà buona norma richiamare, prima della predetta funzione e quindi prima della chiusura della finestra, la funzione: void SDL_FreeSurface(SDL_Surface* surface) che libera le risorse del sistema utilizzate per la inizializzazione e generazione della finestra medesima.
Il parametro *surface è il puntatore al framebuffer ottenuto dalla precedente funzione: "SDL_SetVideoMode".
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_FreeSurface(surface As Pointer)

ed in routine sarà così richiamata:

SDL_FreeSurface(video)

Si richiamerà, qualora sia stata inizializzata anche la libreria libSDL_ttf-2.0.so.0.10.1, la funzione:

void TTF_Quit()

contenuta nella libreria libSDL_ttf-2.0.so.0.10.1, e che chiude ogni riferimento al font ttf impostato in precedenza.
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern TTF_Quit()

ed in routine sarà così richiamata:

TTF_Quit()


Impostare pause

Nel processo del codice relativo alle funzioni di SDL è possibile impostare delle pause mediante la funzione:

void SDL_Delay(Uint32 ms)

laddove l'unico paramentro previsto è un valore intero che rappresenta la durata in millisecondi della pausa.
In Gambas detta funzione sarà così dichiarata con Extern:

Private Extern SDL_Delay(ms As Integer)

ed in routine sarà in astratto così richiamata:

SDL_Delay(millisecondi)


Codice esemplificativo della creazione di una finestra

Mostriamo di seguito un esempio di codice per la creazione e caratterizzazione di una finestra con l'API di SDL. E' necessario impostare l'immagine di formato bmp da mostrare, nonché il percorso di un file font ttf.

Public Struct SDL_Rect 
  x As Short
  y As Short
  w As Short
  h As Short
End Struct

Private Const SDL_INIT_VIDEO As Byte = 32
Private Const SDL_HWSURFACE As Byte = 1
Private Const SDL_DOUBLEBUF As Integer = 1073741824
Private video As Pointer

Library "libSDL-1.2:0.11.4"
Private Extern SDL_Init(flags As Integer) As Integer
Private Extern SDL_SetVideoMode(width As Integer, height As Integer, bitsperpixel As Integer, flags As Integer) As Pointer
Private Extern SDL_FreeSurface(surface As Pointer)
Private Extern SDL_Quit()
Private Extern SDL_RWFromFile($file As String, mode As String) As Pointer
Private Extern SDL_LoadBMP_RW(image As Pointer, freesrc As Integer) As Pointer
Private Extern SDL_UpperBlit(image As Pointer, srcrect As Pointer, dst As Pointer, dstrect As SDL_Rect) As Integer In "libSDL_image-1.2:0.8.4"
Private Extern SDL_UpdateRect(sdlsurface As Pointer, x As Integer, y As Integer, w As Integer, h As Integer)
Private Extern SDL_Flip(sdlsurface As Pointer)
Private Extern SDL_FillRect(sdlsurface As Pointer, dstrect As SDL_Rect, color As Integer)

Library "libSDL_ttf-2.0:0.10.1"
Private Extern TTF_Init() As Integer
Private Extern TTF_OpenFont(nomefont As String, ptsize As Integer) As Pointer
Private Extern TTF_RenderText_Blended(fontP As Pointer, text As String, fg As Integer) As Pointer
Private Extern SDL_WM_SetCaption(title As String, icon As String)
Private Extern TTF_Quit()


Public Sub Main()

 Dim fl, imago, font, testo As Pointer
 Dim riquadro As New SDL_Rect
 Dim s As String


  SDL_Init(SDL_INIT_VIDEO)

  video = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE Or SDL_DOUBLEBUF)

' Carica un'immagine nella finestra:
  fl = SDL_RWFromFile("/percorso/dell'immagine.bmp", "rb")
  If fl = 0 Then Error.Raise("Impossibile caricare il file immagine '.bmp' !")

  imago = SDL_LoadBMP_RW(fl, 1)

  SDL_UpperBlit(imago, Null, video, Null)

  SDL_UpdateRect(video, 0, 0, 0, 0)


' Viene imposta una intestazione testuale alla finestra:
  SDL_WM_SetCaption("Intestazione della finestra", Null)


' Si procede con l'impostazione di un testo sulla superficie della finestra:
  TTF_Init()

  font = TTF_OpenFont("/percorso/del/font.ttf", 15)
  If font = 0 Then Error.Raise("Impossibile caricare il font !")

  testo = TTF_RenderText_Blended(font, "Testo qualsiasi", 3329329)


' Si procede con l'impostazione di un riquadro 300x300 pixel all'interno della finestra ed al di sopra dell'immagine:
  With riquadro
    .x = 0
    .y = 0
    .w = 300
    .h = 300
  End With

' Il riquadro sarà di colore giallo:
  SDL_FillRect(video, riquadro, &FFFF00)

' Il riquadro contenente il testo, invece, sarà posto sulla superficie della finestra
' ad una posizione determinata dalle coordinate x ed y:
  With riquadro
     .x = 30
     .y = 50
  End With

  SDL_UpperBlit(testo, Null, video, riquadro)
  
  SDL_Flip(video)

' Il seguente ciclo consente di tenere aperta e visibile la finestra.
' Esso terminerà quando verrà inviato un qualsiasi carattere dal Terminale.
  While True
    Line Input #File.In, s
    If s <> Null Then Break
  Wend


  TTF_Quit()

  SDL_FreeSurface(video)

  SDL_Quit()

End


Intercettare gli eventi del mouse

E' possibile intercettare gli eventi prodotti dal mouse all'interno della finestra creata con SDL.

Di seguito viene mostrato un codice esemplificativo per la gestione degli eventi generati dal mouse sulla finestra.
Per intercettare gli eventi generati dal mouse si potrà utilizzare la funzione:

int SDL_WaitEvent(SDL_Event* event)

che resta in attesa |1| della sollevazione di un evento, ed alla quale viene passato un puntatore ad apposita Struttura da riempire con l'evento. In Gambas potremo omettere di creare un'apposita Struttura, ed utilizzare soltanto una variabile di tipo Puntatore.
Dichiareremo questa funzione esterna in Gambas così:

Private Extern SDL_WaitEvent(event As Pointer) As Integer

Si aggiungeranno alcune costanti, ciascuna relativa ad un evento:

Private Const SDL_MOUSEMOTION As Byte = 4
Private Const SDL_BUTTONDOWN As Byte = 5
Private Const SDL_BUTTONUP As Byte = 6

si aggiungerà anche una costante per la chiusura della finestra, nel caso si clicchi sulla X posta sull'angolo in alto a destra della finestra medesima:

Private Const SDL_QUITEVENT As Byte = 12

La seguente routine verrà processata dopo aver generato la finestra:

Private Procedure eventiMouse()
 
 Dim evento As Pointer
 Dim evS As Stream
 Dim b As Byte
 Dim s As Short
  
' Creiamo una variabile di tipo puntatore per gestire l'evento:
  evento = Alloc(16)
 
  While True

' Poniamo la funzione, con la quale attendere la sollevazione dell'evento,
' e le passiamo un puntatore all'area di memoria riservata, precedentemente creata,
' affinché la riempia con i dati dell'evento intercettato:
    SDL_WaitEvent(evento)
    
' Dereferenziamo il "puntatore" mediante i "Memory-Stream,
' e ne raccogliamo così i dati leggibili:
    evS = Memory evento For Read
     Read #evS, b
     Print "Tipo: "; b
    
     Select Case b
       Case SDL_MOUSEMOTION
         Seek #evS, 4
         Read #evS, s
         Print "X rel. = "; s
         Read #evS, s
         Print "Y rel. = "; s
    
       Case SDL_BUTTONDOWN
         Seek #evS, 1
         Read #evS, b
         Print "which = "; b
         Read #evS, b
         Print "button = "; b
         Select Case b
           Case 1
             Print "Tasto sinistro"
           Case 2
             Print "Tasto centrale"
           Case 3
             Print "Tasto destro"
           Case 4
             Print "Rotellina in su"
           Case 5
             Print "Rotellina in giù"
         End Select
         Read #evS, b
         Print "state = "; b
         Read #evS, s
         Print "x = "; s
         Read #evS, s
         Print "y = "; s
         
       Case SDL_BUTTONUP
         Seek #evS, 1
         Read #evS, b
         Print "which = "; b
         Read #evS, b
         Print "button = "; b
         Select Case b
           Case 1
             Print "Tasto sinistro"
           Case 2
             Print "Tasto centrale"
           Case 3
             Print "Tasto destro"
         End Select
         Read #evS, b
         Print "state = "; b
         Read #evS, s
         Print "x = "; s
         Read #evS, s
         Print "y = "; s
         
       Case SDL_QUITEVENT
         SDL_Quit()
     End Select
           
  Wend

End

Il seguente esempio, simile al precedente per la creazione e caratterizzazione di una finestra con l'API di SDL, implementa ora l'intercettazione degli eventi del mouse.

Public Struct SDL_Rect 
  x As Short
  y As Short
  w As Short
  h As Short
End Struct


Private Const SDL_INIT_VIDEO As Byte = 32
Private Const SDL_DOUBLEBUF As Integer = &40000000
Private Const SDL_SWSURFACE As Byte = 0 
Private Const SDL_ANYFORMAT As Integer = 268435456
Private Const SDL_HWSURFACE As Byte = 1
Private Const SDL_MOUSEMOTION As Byte = 4
Private Const SDL_BUTTONDOWN As Byte = 5
Private Const SDL_BUTTONUP As Byte = 6
Private Const SDL_QUITEVENT As Byte = 12
Private video As Pointer

Library "libSDL-1.2:0.11.4"

Private Extern SDL_Init(flags As Integer) As Integer
Private Extern SDL_SetVideoMode(width As Integer, height As Integer, bitsperpixel As Integer, flags As Integer) As Pointer
Private Extern SDL_FreeSurface(surface As Pointer)
Private Extern SDL_Quit()

Private Extern SDL_RWFromFile($file As String, mode As String) As Pointer
Private Extern SDL_LoadBMP_RW(image As Pointer, freesrc As Integer) As Pointer
Private Extern SDL_UpperBlit(image As Pointer, srcrect As Pointer, dst As Pointer, dstrect As SDL_Rect) As Integer In "libSDL_image-1.2:0.8.4"
Private Extern SDL_UpdateRect(sdlsurface As Pointer, x As Integer, y As Integer, w As Integer, h As Integer)
Private Extern SDL_Flip(sdlsurface As Pointer)
Private Extern SDL_FillRect(sdlsurface As Pointer, dstrect As SDL_Rect, color As Integer)

Private Extern SDL_WaitEvent(ev As Pointer) As Integer


Library "libSDL_ttf-2.0:0.10.1"

Private Extern TTF_Init() As Integer
Private Extern TTF_OpenFont(nomefont As String, ptsize As Integer) As Pointer
Private Extern SDL_WM_SetCaption(title As String, icon As String)
Private Extern TTF_Quit()
Private Extern TTF_RenderText_Blended(fontP As Pointer, text As String, fg As Integer) As Pointer
 

Public Sub Main()

 Dim fl, imago, font, testo As Pointer
 Dim ist As Stream
 Dim riquadro As New SDL_Rect
 Dim sW, sH As Integer


  SDL_Init(SDL_INIT_VIDEO)
 
  video = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE Or SDL_DOUBLEBUF)

' Carica un'immagine nella finestra:
  fl = SDL_RWFromFile("/percorso/del/file/immagine.bmp", "rb")
  If fl = 0 Then Error.Raise("Impossibile caricare il file immagine '.bmp' !")
   
  imago = SDL_LoadBMP_RW(fl, 1)
   
  ist = Memory imago For Read
  Seek #ist, 16
  Read #ist, sW
  Print sW
  Read #ist, sH
  Print sH
 
  SDL_UpperBlit(imago, Null, video, Null)
   
  SDL_UpdateRect(video, 0, 0, 0, 0)

  SDL_WM_SetCaption("Intestazione della finestra", Null)

  If TTF_Init() < 0 Then Error.Raise("Errore alla funzione 'TTF_Init()' !")

  font = TTF_OpenFont("/usr/share/fonts/truetype/freefont/FreeSerif.ttf", 15)

  testo = TTF_RenderText_Blended(font, "Testo qualsiasi", 3329329)

  With riquadro
    .x = 0
    .y = 0
    .w = 300
    .h = 300
  End With

  SDL_FillRect(video, riquadro, &FFFF00)
 
  With riquadro
    .x = 280
    .y = 50
  End With

  SDL_UpperBlit(testo, Null, video, riquadro)
  SDL_Flip(video)

  eventiMouse()
 

' Va in chiusura:
  TTF_Quit()
  SDL_FreeSurface(video)
  SDL_Quit()

End


Private Procedure eventiMouse()
 
 Dim evento As Pointer
 Dim evS As Stream
 Dim b As Byte
 Dim s As Short

' Creiamo una variabile di tipo puntatore per gestire l'evento:
  evento = Alloc(16)

  While True

' Poniamo la funzione, con la quale attendere la sollevazione dell'evento,
' e le passiamo un puntatore all'area di memoria riservata, precedentemente creata,
' affinché la riempia con i dati dell'evento intercettato:
  SDL_WaitEvent(evento)
   
' Dereferenziamo il "puntatore" mediante i "Memory-Stream,
' e ne raccogliamo così i dati leggibili:
  evS = Memory evento For Read
  Read #evS, b
  Print "Tipo: "; b
   
  Select Case b
    Case SDL_MOUSEMOTION
      Seek #evS, 4
      Read #evS, s
      Print "X rel. = "; s
      Read #evS, s
      Print "Y rel. = "; s
   
    Case SDL_BUTTONDOWN
      Seek #evS, 1
      Read #evS, b
      Print "which = "; b
      Read #evS, b
      Print "button = "; b
      Select Case b
        Case 1
          Print "Tasto sinistro"
        Case 2
          Print "Tasto centrale"
        Case 3
          Print "Tasto destro"
        Case 4
          Print "Rotellina in su"
        Case 5
          Print "Rotellina in giù"
      End Select
      Read #evS, b
      Print "state = "; b
      Read #evS, s
      Print "x = "; s
      Read #evS, s
      Print "y = "; s
       
    Case SDL_BUTTONUP
      Seek #evS, 1
      Read #evS, b
      Print "which = "; b
      Read #evS, b
      Print "button = "; b
      Select Case b
        Case 1
          Print "Tasto sinistro"
        Case 2
          Print "Tasto centrale"
        Case 3
' Se viene premuto il tasto destro, si esce dal ciclo:
          Print "Tasto destro"
          Free(evento)
          Exit
      End Select
      Read #evS, b
      Print "state = "; b
      Read #evS, s
      Print "x = "; s
      Read #evS, s
      Print "y = "; s
        
    Case SDL_QUITEVENT
      SDL_Quit()
  End Select

  Wend

End


Note

[1] La funzione SDL_WaitEvent, come già detto, resta in attesa (e pertanto ha un effetto bloccante all'interno del ciclo) sino a quando non viene sollevato l'evento. Qualora non si preferisca tale effetto bloccante, è possibile utilizzare la funzione: int SDL_PollEvent(SDL_Event* event) (sempre posta comunque all'interno di un ciclo While True), in quanto essa verifica semplicemente se un evento è stato sollevato e può essere quindi intercettato.


Riferimenti