FreeBASIC – configurare la console

Esiste ancora tutto un mondo di applicazioni per console (console application), alcune delle quali si permettono anche il lusso di fare a meno del mouse.

Per esempio ne (the nice editor) è un noto editor di testo per console sviluppato da Sebastiano Vigna dell’Università degli Studi di Milano.

Potrà sembrare una cosa da inguaribili nostalgici – e forse lo è – ma in questo blog trovano posto anche questo tipo di applicazioni.

Seguendo il metodo imparare facendo (learning by doing), approfondiremo passo passo le procedure per la console messe a disposizione dal compilatore FreeBASIC cercando di realizzare un semplice editor di testo per console che avrà quindi una interfaccia utente basata sul solo testo (text-based user interface). Un editor di testo che magari possa anche interagire con il compilatore FreeBASIC in modo tale che possa essere utilizzato anche come semplice IDE (Integrated Development Environment).

Il nostro piccolo progetto si chiamerà CLUMSY che in inglese suona più o meno come oggetto mal costruito. 😉

CLUMSY è ospitato su GitHub in questo repository.

Configurare la console

Come prima cosa dobbiamo informare il compilatore FreeBASIC che abbiamo intenzione di aprire una finestra grafica in modalità console. Per farlo dobbiamo impostare il valore della modalità grafica (parametro mode della istruzione screen) al valore 0 (zero).

Imposteremo poi il colore del primo piano e dello sfondo con l’istruzione color sfruttando la tavolozza dei colori di default.

Faremo uso dell’istruzione cls per pulire lo schermo e rendere attiva l’impostazione dei colori precedentemente scelta.

L’istruzione cls può essere fatta seguire da un parametro che può assumere i seguenti tre valori:

  • 0 -> pulisce l’intero schermo;
  • 1 -> pulisce l’area grafica se definita con l’istruzione view (se lo schermo è stato settato in modalità console questa istruzione non ha alcun effetto);
  • 2 -> pulisce l’area di stampa se definita con l’istruzione view print.

Se si utilizza l’istruzione cls senza alcun parametro, nel caso sia stata definita un’area grafica o di stampa, questa verra pulita, altrimenti viene pulito l’intero schermo.

Utilizzeremo poi l’istruzione width in combinazione con le istruzioni hiword e loword, per conoscere rispettivamente il numero massimo di righe e di colonne messe a disposizione di default dalla nostra finestra terminale (o prompt dei comandi in ambiente Windows).

Per posizionare il testo sfrutteremo l’istruzione locate.

Infine, per definire l’area di testo su cui andremo a scrivere utilizzaremo l’istruzione view print specificando la riga iniziale e quella finale.

''project: clumsy | pre-alpha version
''description: text editor for console
''author: Luca Evangelisti
''last update: 3th February 2020
''GitHub: https://github.com/lucaevangelisti/clumsy
''license: https://github.com/lucaevangelisti/clumsy/blob/master/LICENSE

dim as uinteger max_rows, max_cols

screen 0 ''console-mode functionality

max_rows = hiword(width) ''max number of rows
max_cols = loword(width) ''max number of columns

color 7, 0 ''text grey, background black
cls 0 ''clears the entire screen

''footer info
locate max_rows, 1
print "clumsy is a silly text editor for console"; _
      " | pre-alpha version" _
      " | ^H for Help";

''sets the printable area of the console screen
view print 1 to (max_rows - 1)

color 0, 7 ''text black, background grey
cls 2 ''clear only the text viewport

''text
print "Hello, world!";

sleep

Nel prossimo articolo vedremo come ottenere informazioni talla tastiera sfruttando la funzione getkey.

FreeBASIC – tastiera

Dopo aver visto come lavorare con il mouse, oggi impareremo a lavorare con la tastiera (keyboard).

Funzione multikey

Il nome della funzione che utilizzeremo è multikey.

In quest’altro articolo viene invece descritto l’uso della funzione getkey.

Funzionamento

Questa funzione necessita come argomento del valore del codice del tasto che si vuole verificare se è stato premuto. Se il tasto viene effettivamente premuto la funzione restituisce il valore -1 (true), altrimenti 0 (false).

Sul sito ufficiale di FreeBASIC è presente la lista dei codici dei tasti.

A ciascun codice corrisponde una costante.

La collezione dei nomi di queste costanti rientra nello spazio dei nomi (namespace) identificato dalla sigla FB che va quindi anteposta al nome della costante attraverso la notazione puntata. Per esempio: FB.SC_ESCAPE (per indicare il tasto ‘Esc’).

Questi valori costanti sono definiti all’interno del file di intestazione fbgfx.bi che fa parte della libreria grafica di FreeBASIC.

Ciò comporta che all’inizio del modulo dovremo includere la libreria grafica attraverso l’istruzione #include.

Esempio

#include "fbgfx.bi"

dim as integer x, y
screen 19
x = 300: y = 200

''continua il ciclo sino alla pressione del tasto 'Esc' (esci)
do
  ''verifica se vengono premuti i tasti freccia
  if multikey(FB.SC_LEFT) then x = x - 1
  if multikey(FB.SC_RIGHT) then x = x + 1
  if multikey(FB.SC_UP) then y = y - 1
  if multikey(FB.SC_DOWN) then y = y + 1
  ''pulisce lo schermo e disegna il cerchio alle nuove coordinate
  cls
  circle(x, y), 30
  sleep 15, 1
loop until multikey(FB.SC_ESCAPE)

Il secondo parametro (1) della funzione sleep comunica al compilatore FreeBASIC che la pausa di 15 ms non può essere interrotta dalla pressione di un qualsiasi tasto.

Bene. Anche oggi abbiamo fatto un piccolo passo in avanti. 🙂

FreeBASIC – Grafica [4]

Concludiamo l’esplorazione dell’istruzione screen analizzando il suo quarto attributo che nella documentazione ufficiale in inglese viene definito flags.

Questo attributo serve per impostare diverse proprietà dello schermo, ma per lo scopo didattico della nostra guida ci interesseremo soltanto di quelle relative ad alcune delle proprietà della finestra grafica, ovvero:

  1. la modalità a schermo intero (fullscreen mode);
  2. la presenza o meno dei bordi;
  3. se deve apparire sempre in primo piano o no.

I valori ammissibili per questo attributo sono predefiniti come valori costanti all’interno del file di intestazione fbgfx.bi che deve quindi necessariamente essere incluso nel file sorgente del nostro programma.

Se l’attributo flag viene omesso, verrà aperta una normale finestra (che corrisponde, come vedremo poco più sotto, alla costante GFX_WINDOWED).

Vediamo ora i nomi e il significato di queste costanti:

  • GFX_WINDOWED: apre una normale finestra grafica secondo le caratteristiche specificate dai primi tre attributi dell’istruzione screen (ovvero la modalità grafica, la profondità di colore e il numero di pagine video);
  • GFX_FULLSCREEN: se la modalità è supportata dal sistema operativo del proprio computer forza l’apertura di una finestra grafica a schermo intero, altrimenti verrà impostato di default il valore della costante GFX_WINDOWED;
  • GFX_NO_FRAME: crea una finestra grafica senza bordi;
  • GFX_ALWAYS_ON_TOP: crea una finestra grafica che rimane sempre in primo piano.

Ora non ci resta che provare con un semplice programma dove per testare le diverse proprietà della finestra grafica non dovremo far altro che trasformare in commento l’istruzione attiva, anteponendo il simbolo apice (‘), e poi trasformare in istruzione quella commentata che volete provare cancellando il simbolo apice:

'include il file contenente le costanti per gestire
'le proprietà dello schermo
#include "fbgfx.bi"

'specifica che le costanti rientrano nello spazio
'dei nomi di FreeBASIC (argomento che affronteremo
'più avanti)
#if __fb_lang__ = "fb"
  using fb
#endif

'Imposta una pagina video con la modalità grafica 19 per
'una risoluzione dello schermo di 800x600 pixels e
'la profondità di colore a 32bpp e poi imposta la
'relativa proprietà dello schermo
screen 19, 32, 1, GFX_WINDOWED
'screen 19, 32, 1, GFX_FULLSCREEN
'screen 19, 32, 1, GFX_NO_FRAME
'screen 19, 32, 1, GFX_ALWAYS_ON_TOP

print "Ciao mondo!"

sleep 'rimane in pausa

Bene. Con l’istruzione screen possiamo fermarci qui.

Al prossimo articolo. 🙂

FreeBASIC – Grafica [3]

Dopo aver parlato della risoluzione dello schermo (modalità grafica) e della risoluzione grafica (profondità di colore), in questo articolo continueremo l’esplorazione della istruzione screen giocando con il suo terzo attributo: quello che specifica il numero di pagine video con cui si vuole lavorare.

Pagine video

In FreeBASIC è possibile lavorare con più pagine video.

Quindi, oltre alla normale procedura che prevede di disegnare su una pagina mentre questa è già visibile a schermo, avendo a disposizione più pagine video, si può per esempio disegnare su una pagina mentre se ne sta rendendo visibile un’altra su cui si era disegnato in precedenza.

Qual è il vantaggio?

Lo “spettatore” si gode lo spettacolo sul palcoscenico (pagina visibile), mentre nel retropalco (pagina di lavoro) si prepara la scenografia successiva. 😉

Come definire il numero di pagine video

Per definire il numero delle pagine video che necessitano al nostro programma basta scriverlo come terzo attributo dell’istruzione screen:

'Imposta 3 pagine video con la modalità grafica 19 per
'una risoluzione dello schermo di 800x600 pixels e
'la profondità di colore a 32bpp.
screen 19, 32, 3

Se questo attributo viene omesso, il nostro programma avrà comunque una pagina video di default.

Come specificare la pagina di lavoro e quella visibile

Se il nostro programma ha una sola pagina video, le istruzioni per il disegno agiranno e saranno visibili su questa pagina, ma se possiamo disporre di più pagine video, allora dovremo gestire sia la pagina di lavoro che la pagina visibile.

Per farlo si usa la subroutine screenset che ha due attributi: il primo per definire la pagina di lavoro, il secondo quella visibile. Vediamo alcuni esempi:

screen 19, 32, 2 'per definire due pagine video
screenset 0, 0 'disegna e visualizza la I p.v.
screenset 0, 1 'disegna sulla I p.v. mentre visualizza la II
screenset 1, 0 'disegna sulla II p.v. mentre visualizza la I
screenset 1, 1 'disegna e visualizza la II p.v.

Come è facile notare da questa porzione di codice, l’identificativo delle schede video parte dal valore 0 (zero).

Nel programma dei 3 cerchi colorati riportato più sotto ne vedremo un possibile utilizzo.

Un po’ di grafica 2D

Iniziamo ad avvicinarci alla grafica 2D utilizzando una delle funzioni per il disegno 2D: la funzione circle.

Funzione circle

La funzione circle permette di disegnare cerchi ed ellissi.

Sintassi

Un uso semplificato è questo:

circle (coordinata x, coordinata y), raggio, [colore]

I valori relativi alle coordinate x e y del centro del cerchio e quello del raggio sono obbligatori, mentre l’attributo del colore è opzionale: se omesso sarà quello del primo piano corrente impostabile con la funzione color().

Nell’esempio che vedremo più sotto ci baseremo sul modello di colori RGB utilizzando la funzione rgb() passandole tre attributi che rappresentano rispettivamente la componente rossa, quella verde e quella blu. Il range dei valori ammessi per questi attributi va da 0 a 255.

Programma dei 3 cerchi colorati

'Imposta 3 pagine video con la modalità grafica 19 per
'una risoluzione dello schermo di 800x600 pixels e
'la profondità di colore a 32bpp.
screen 19, 32, 3

'Imposta come pagina di lavoro la prima (valore 0).
screenset 0
'Disegna 255 cerchi al centro dello schermo, di
'raggio e tono di colore rosso crescenti.
for i as integer = 1 to 255
  circle (400,300), i, RGB(i,0,0)
next i

'Imposta come pagina di lavoro la seconda (valore 1).
screenset 1
'Disegna 255 cerchi al centro dello schermo, di
'raggio e tono di colore verde crescenti.
for i as integer = 1 to 255
  circle (400,300), i, RGB(0,i,0)
next i

'Imposta come pagina di lavoro la terza (valore 2).
screenset 2
'Disegna 255 cerchi al centro dello schermo, di
'raggio e tono di colore blu crescenti.
for i as integer = 1 to 255
  circle (400,300), i, rgb(0,0,i)
next i

'Doppio ciclo per rendere alternativamente visibili,
'una volta al secondo, le tre pagine. Il ciclo si
'ripete per 3 volte.
for i as integer = 1 to 3
  for j as integer = 0 to 2
    screenset ,j
    sleep 1000 'pausa di un secondo
  next j
next i

Un po’ ipnotico. No? 🙂

Nel prossimo articolo vedremo come impostare alcune proprietà della finestra grafica.

FreeBASIC – Grafica [2]

Nel precedente articolo abbiamo parlato della risoluzione dello schermo: ovvero di come impostare le dimensioni digitali delle nostre finestre in termini di quanti pixels si possono disegnare in orizzontale (larghezza) e di quanti in verticale (altezza).

Il rapporto tra la larghezza e l’altezza di una finestra grafica rappresenta il cosiddetto rapporto d’aspetto.

In questo nuovo articolo parleremo invece della risoluzione grafica: ovvero della qualità delle immagini che si possono disegnare a video.

Profondità di colore

In FreeBASIC per definire la qualità delle immagini occorre impostare nella istruzione screen il parametro della profondità di colore (color depth): ovvero il numero di bits utilizzati per descrivere il colore di un singolo pixel. L’unità di misura di questo parametro è quindi espressa in bpp (bits per pixel).

Questo parametro ha effetto soltanto per le modalità grafiche dalla 14 alla 21 e può assumere i valori: 8, 16 o 32 bpp.

Più bits vengono utilizzati per un singolo pixel, più la qualità dell’immagine sarà migliore.

Scheda video

Arrivati a questo punto occorre fare una precisazione:

Le caratteristiche tecniche della scheda video del nostro computer limitano le possibilità di scelta della modalità grafica in relazione alla profondità di colore da noi scelta.

Come facciamo quindi a sapere quale modalità grafica (parametro mode) possiamo scegliere con uno specifico valore di profondità di colore (parametro depth)?

L’istruzione screenlist

La risposta alla domanda che ci siamo posti più sopra ce la fornisce l’istruzione screenlist.

Vediamo nel dettaglio i passi da seguire per utilizzare al meglio questa istruzione:

  1. Nella prima chiamata alla funzione screenlist è necessario specificare il valore della profondità di colore desiderata. La funzione restituirà la più bassa risoluzione dello schermo possibile in relazione alla profondità di colore specificata.
  2. Le successive chiamate alla funzione non necessiteranno più di alcun argomento: ciascuna chiamata restituirà le possibili risoluzioni dello schermo in maniera crescente.
  3. La restituzione del valore 0 (zero) starà a significare che non ci sono risoluzioni dello schermo più grandi dell’ultima restituita.

Scriviamo ora un semplice programma per scoprire quali risoluzioni dello schermo sono possibili con la scheda video del nostro computer in base alle tre diverse profondità di colore gestite dal FreeBASIC.

Per realizzare il nostro programma utilizzeremo due strutture di controllo:

  1. un ciclo for-next per selezionare di volta in volta le diverse profondità di colore;
  2. un ciclo do-loop per eseguire le diverse chiamate alla funzione screenlist.
'dimensiona e poi crea un array con i tre valori della
'profondità di colore disponibili in FreeBASIC
dim prof_col (1 to 3) as long
prof_col(1) = 8
prof_col(2) = 16
prof_col(3) = 32

'dimensiona una variabile per ricevere il
'risultato della chiamata alla funzione screenlist
dim risultato as long

'dimensiona una variabile stringa per la
'risoluzione dello schermo
dim risoluzione as string

'doppio ciclo per ottenere tutte le risoluzioni dello schermo
'gestite dalla scheda video del computer in base alle
'diverse profondità di colore
for i as integer = lbound(prof_col) to ubound(prof_col)
  print "Lista risoluzioni dello schermo disponibili"
  print "con la profondita' di colore "; prof_col(i)
  risultato = screenlist(prof_col(i)) 'prima chiamata
    do while risultato <> 0
      print risultato
      risultato = screenlist()
    loop
  print "---"
next i

Ecco il risultato a video:

Che dire? Ciò che abbiamo ottenuto è veramente poco soddisfacente: una serie di numeri che sembrano non dirci nulla. Perché?

La risposta ci permetterà di conoscere una tecnica che potrà tornarci utile in molte occasioni.

Lavorare con le codifiche binarie

Il risultato della funzione screenlist è codificato in un valore a 32 bit: dove i 16 bits più alti (ovvero quelli a sinistra) rappresentano la larghezza della finestra, mentre i 16 bits più bassi (ovvero quelli a destra) rappresentano l’altezza della finestra.

Il compilatore FreeBASIC ci mette a disposizione due utili funzioni per estrarre queste informazioni: l’istruzione hiword e l’istruzione loword.

Modifichiamo quindi il ciclo do-loop del nostro programma in questo modo:

do while risultato <> 0
      risoluzione = hiword(risultato) & "x" & loword(risultato)
      print risoluzione
      risultato = screenlist()
loop

Vediamo ora il nuovo risultato a video:

Molto meglio. No? 🙂

Ora non ci resta che fare un confronto con le modalità grafiche disponibili in FreeBASIC per verificare quali sono disponibili.

Modifichiamo quindi il ciclo do-loop inserendo una istruzione select case:

do while risultato <> 0
  risoluzione = hiword(risultato) & "x" & loword(risultato)
  select case risoluzione
    case "320x240"
      print hiword(risultato); "x"; loword(risultato); " -> "; "14"
    case "400x300"
      print hiword(risultato); "x"; loword(risultato); " -> "; "15"
    case "512x384"
      print hiword(risultato); "x"; loword(risultato); " -> "; "16"
    case "640x400"
      print hiword(risultato); "x"; loword(risultato); " -> "; "17"
    case "640x480"
      print hiword(risultato); "x"; loword(risultato); " -> "; "18"
    case "800x600"
      print hiword(risultato); "x"; loword(risultato); " -> "; "19"
    case "1024x768"
      print hiword(risultato); "x"; loword(risultato); " -> "; "20"
    case "1280x1024"
      print hiword(risultato); "x"; loword(risultato); " -> "; "21"
    case else
      print hiword(risultato); "x"; loword(risultato)
  end select
  risultato = screenlist()
loop

Ecco il risultato:

Ora possiamo dirci soddisfatti! 🙂

Abbiamo finalmente la lista delle risoluzioni dello schermo gestibili dalla scheda video del nostro computer con evidenziate quelle che hanno una corrispondenza con le modalità grafiche gestite dal compilatore FreeBASIC.

Nel prossimo articolo continueremo ad approfondire l’istruzione screen testando un altro dei suoi attributi: quello che gestisce le pagine video.

FreeBASIC – Grafica [1]

E così siamo finalmente arrivati a parlare di grafica.

Disegnare delle semplici figure 2D sullo schermo e interagire con queste sarà l’obiettivo principale di questa nuova serie di articoli.

Come è ormai nostra abitudine, partiamo subito con qualcosa di concreto.

Come creare una finestra grafica

La prima cosa che ci serve è creare una finestra grafica su cui poi andremo a disegnare.

Per farlo si utilizza l’istruzione screen.

screen 16
print "Ecco la mia prima finestra grafica in"
print "modalita' 16: ovvero con risoluzione 512x384 pixels."
sleep 'istruzione per sospendere l'esecuzione del programma

Dare un titolo alla finestra grafica

Per abbellire la nostra finestra grafica diamole un titolo sfruttando la subroutine windowtitle:

windowtitle("La mia prima finestra")
screen 19
sleep

Modalità grafica

Il numero subito dopo l’istruzione screen definisce la modalità grafica (mode) sulla base delle seguenti due tabelle:

Le modalità dalla 1 alla 13 sono compatibili anche con i vecchi programmi scritti con il glorioso QuickBASIC, mentre le nuove modalità dalla 14 alla 21 sono specifiche del FreeBASIC. Sarà proprio su queste ultime che ci concentreremo.

Le due tabelle ci mostrano con chiarezza che più la modalità scelta sarà alta, più grandi saranno le dimensioni digitali della nostra finestra: ovvero la risoluzione dello schermo (resolution).

La dimensione digitale di uno schermo (nel nostro caso della finestra) è espressa come numero di pixels orizzontali e verticali (p.e. 512×384).

Scriviamo ora un semplice programma per visualizzare una finestra in tutte e sedici le diverse modalità. Per farlo sfrutteremo un array e un ciclo for.

'dimensiona un array con tutte le modalità grafiche
 dim mode_array (1 to 16) as integer

 'assegna i valori
 mode_array(1) = 1
 mode_array(2) = 2
 mode_array(3) = 7
 mode_array(4) = 8
 mode_array(5) = 9
 mode_array(6) = 11
 mode_array(7) = 12
 mode_array(8) = 13
 mode_array(9) = 14
 mode_array(10) = 15
 mode_array(11) = 16
 mode_array(12) = 17
 mode_array(13) = 18
 mode_array(14) = 19
 mode_array(15) = 20
 mode_array(16) = 21

 'visualizza con un ciclo le varie finestre
 for i as integer = lbound(mode_array) to ubound(mode_array)
   screen mode_array(i)
   print "Finestra grafica in modalita': "; mode_array(i)
   print "---"
   print "premi un taso per continuare…"
   sleep 'sospende l'esecuzione del programma
 next i

Funzione screenres

Le modalità grafiche che abbiamo visto hanno delle dimensioni digitali preimpostate, ma se volessimo creare una finestra grafica di dimensioni diverse?

Niente panico! FreeBASIC viene in nostro aiuto con la funzione screenres che ci permette proprio di definire le dimensioni digitali che vogliamo.

Questa funzione restituisce il valore 0 (zero) se le dimensioni digitali specificate sono realizzabili dal nostro computer, altrimenti viene restituito un numero che specifica il tipo di errore.

Per approfondire l’argomento vai all’articolo dedicato alla gestione degli errori.

Dopo aver provato il codice più sotto, provate a specificare per esempio un numero negativo come larghezza o come altezza:

''crea una finestra grafica larga 800px e alta 50px 
select case screenres(800,50)
  case 0 ''tutto OK
    print "Ecco una strana finestra grafica!"
  case else ''errore
    beep
    print "Non posso creare la finestra grafica che desideri."
end select
sleep ''rimani in pausa

Bene. Per oggi ci fermiamo qui.

Nel prossimo articolo parleremo della risoluzione grafica: ovvero della qualità delle immagini. 🙂