Allo scopo di usare la libreria è necessario che siano definiti certi tipi e variabili. Perciò, il programmatore deve avere la linea:
#include <curses.h>
all'inizio del programma sorgente.
Il pacchetto screen utilizza la libreria di I/O Standard, così
<curses.h>
include <stdio.h>
;
<curses.h>
include anche <termios.h>
,
<termio.h>
, o <sgtty.h>
a seconda del
vostro sistema.
E` ridondante (ma non dannoso) per il programmatore ripetere
questi include.
Nel linkare con le curses
avete bisogno di aggiungere
-lncurses
nel vostri LDFLAGS o nella riga di comando.
Non c'è bisogno di altre librerie.
Allo scopo di aggiornare lo schermo nel modo ottimale, è necessario
che le routine sappiano cosa lo schermo mostri correntemente e cosa
il programmatore vuole che mostri in seguito.
Per questo motivo si definisce un certo tipo di dati
chiamato WINDOW
, che descrive alle routine una finestra,
includendo la sua posizione iniziale sullo schermo (le coordinate
(y,x) dell'angolo superiore sinistro) e la sua misura.
Una di queste strutture (chiamata curscr
, per current screen
(schermo attuale)), è una immagine di ciò che il terminale
attualmente mostra.
Un altro schermo (chiamato stdscr
, per standard screen
(schermo standard)) viene fornito automaticamente per ricevere
i cambiamenti.
Una WINDOW
(finestra) è una rappresentazione puramente interna.
Viene usata per contenere l'immagine potenziale di una porzione del
terminale.
Non è necessariamente in relazione con quello che si vede realmente
sullo schermo del terminale; è piuttosto come un foglio di appunti
o un buffer di scrittura.
Per far sì che la sezione dello schermo fisico corrispondente ad
una finestra rifletta il contenuto di una struttura WINDOW
si esegue la routine refresh()
(o wrefresh()
se la finestra
non è stdscr
).
Una data sezione dello schermo fisico può essere interessata da un qualunque numero di finestre sovrapposte. Inoltre, cambiamenti possono venire apportati alle finestre in ogni ordine, senza rispetto per l'efficienza. Quindi il programmatore, quando vuole, può effettivamente dire ''fà che mostri questo'', e lasciare che il pacchetto determini il modo più efficiente per ridisegnare lo schermo.
Come consigliato sopra, le routine possono usare parecchie finestre,
ma due vengono create automaticamente: curscr
, che sa cosa
mostra il terminale, e stdscr
, che è ciò che il programmatore
vuole che mostri in seguito.
L'utente non deve mai accedere direttamente a curscr
.
I cambiamenti vanno fatti attraverso la API, e quindi eseguendo
la routine refresh()
(o wrefresh()
).
Sono definite molte funzioni che utilizzano stdscr
come schermo.
Per esempio, per aggiungere un carattere a stdscr
si esegue
addch()
passando il carattere desiderato per argomento.
Per scrivere su una finestra diversa è fornita la routine
waddch()
(per `w'indow addch()).
Questa convenzione di premettere ai nomi di funzione un 'w'
quando si accede ad una specifica finestra è coerente.
Le sole routine che non la seguono sono quelle per cui deve
sempre essere specificata una finestra.
Allo scopo di spostare le coordinate attuali (y,x) da un punto
ad un altro, sono fornite le routine move()
e wmove()
.
Comunque è spesso desiderabile prima eseguire il movimento e
successivamente le operazioni di I/O.
Per evitare goffaggini, la maggior parte delle routine di I/O
possono venir precedute dal prefisso 'mv' e le coordinate
desiderate (y,x) anteposte agli argomenti della funzione.
Per esempio, le chiamate
move(y, x);
addch(ch);
possono venir sostituite da
mvaddch(y, x, ch);
e
wmove(win, y, x);
waddch(win, ch);
possono venir sostituite da
mvwaddch(win, y, x, ch);
Nota che il puntatore alla descrizione della finestra (win) è posto prima delle coordinate (y,x). Se una funzione richiede un puntatore ad una finestra, questo viene sempre passato come primo parametro.
La libreria curses
dispone di alcune variabili
che descrivono le capacità del terminale.
tipo nome descrizione ------------------------------------------------------------------ int LINES numero di linee del terminale int COLS numero di colonne del terminale
Il file curses.h
dichiara alcune costanti #define
ed alcuni tipi di utilità generale:
bool
tipo booleano, attualmente char (es., bool finito;
)
TRUE
valore booleano vero (1).
FALSE
valore booleano falso (0).
ERR
valore di una funzione che è fallita (-1).
OK
valore di una funzione correttamente eseguita.
Ora vi descriveremo come usare il pacchetto screen.
L'assunto è che tutte le operazioni si applichino a stdscr
.
Queste istruzioni possono operare su una qualunque finestra cambiando
i nomi delle funzioni ed i loro parametri come menzionato prima.
Ecco un semplice programma per stimolare la discussione:
#include <curses.h>
#include <signal.h>
static void fine(int sig);
main(int argc, char *argv[])
{
/* inizializzate qui le vostre strutture di dati 'non-curses' */
(void) signal(SIGINT, fine); /* termina al segnale di interruzione */
(void) initscr(); /* inizializza la libreria curses */
keypad(stdscr, TRUE); /* abilita la mappatura della tastiera */
(void) nonl(); /* non convertire NL->CR/NL in output */
(void) cbreak(); /* prende i caratteri in input uno alla volta, senza attendere il \n */
(void) noecho(); /* nessuna echo dell'input */
if (has_colors())
{
start_color();
/*
* Semplici colori, di solito tutto ciò che serve.
*/
init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
}
for (;;)
{
int c = getch(); /* rinfresca il video ed accetta un solo tasto in input */
/* Qui potete elaborare il tasto premuto */
}
fine(0); /* abbiamo fatto */
}
static void fine(int sig)
{
endwin();
/* Qui mettete il vostro codice di terminazione non-curses */
exit(0);
}
Allo scopo di usare il pacchetto screen, le routine devono essere
informate delle caratteristiche del terminale, e bisogna allocare
spazio per curscr
e stdscr
.
La funzione initscr()
fa entrambe queste cose.
Poichè deve allocare spazio per le finestre, potrebbe esaurire
la memoria nel tentativo.
Nelle rare occasioni in cui questo succede, initscr()
farà
terminare il programma con un messaggio d'errore.
initscr()
va sempre chiamata prima di ogni altra routine
che agisca sulle finestre.
Se non lo si facesse, il programma terminerebbe con un
core dump non appena curscr o stdscr venissero utilizzate.
Comunque, di solito è meglio attendere di essere sicuri di averne
bisogno, prima di eseguire questa chiamata, ad esempio dopo il
controllo iniziale degli errori.
Routine di modifica dello stato del terminale quali nl()
e
cbreak()
vanno chiamate dopo initscr()
.
Una volta allocate le finestre dello schermo, potete predisporle
per il vostro programma.
Se voi volete, diciamo, permettere allo schermo di scrollare,
userete scrollok()
.
Se vorrete che il cursore venga lasciato nella posizione
dell'ultima modifica, userete leaveok()
.
Se questo non avviene, refresh()
muoverà il cursore alle
coordinate (y,x) attuali della finestra dopo averla aggiornata.
Potete creare nuove finestre a piacere usando le funzioni
newwin()
, derwin()
e subwin()
.
La routine delwin()
vi permetterà di sbarazzarvi delle
vecchie finestre.
Tutte le opzioni descritte prima possono essere applicate
a qualunque finestra.
Ora che abbiamo messo le cose a posto, vorremo aggiornare il terminale.
Le funzioni di base usate per cambiare quello che va su una finestra
sono addch()
e move()
.
addch()
aggiunge un carattere alle coordinate attuali.
move()
cambia le coordinate attuali (y, x) in quelle
che vorrete voi.
Ritorna ERR
se proverete a muovervi fuori della finestra.
Come accennato sopra, potete combinare le due funzioni in
mvaddch()
per fare entrambe le cose in una volta.
Le altre funzioni di output come addstr()
e printw()
,
chiamano tutte addch()
per aggiungere caratteri alla finestra.
Dopo che avrete messo sulla finestra quello che vi volevate,
quando vorrete che la parte del terminale coperta dalla finestra
mostri il contenuto della finestra, dovrete chiamare refresh()
.
Allo scopo di ottimizzare l'aggiornamento, refresh()
presuppone
che ogni parte della finestra che non è cambiata dall'ultima
esecuzione di refresh()
, non sia cambiata neppure
sul terminale, cioè che non abbiate rinfrescato una parte del terminale
che ha una finestra sovrapposta.
Se non è questo il caso, la routine touchwin()
viene
fornita per fingere che l'intera finestra sia cambiata,
facendo sì che refresh()
aggiorni l'intera sezione del terminale.
Se eseguite wrefresh()
con curscr
come argomento,
farà sì che lo schermo mostri quello che curscr
ritiene
dovrebbe mostrare. Questo viene comodo nell'implementare un comando
che ridisegni lo schermo in caso si sia pasticciato.
La funzione complementare a addch()
è getch()
che,
se echo è attiva, chiamerà addch()
per eseguire la echo
del carattere.
Poichè il pacchetto screen ha bisogno di sapere in ogni momento
che cosa c'è sul terminale, se bisogna fare la echo dei caratteri,
la tty dovrà essere in modalità raw o cbreak.
Poichè inizialmente il terminale ha la echo abilitata e si trova
nell'ordinaria modalità cooked l'una o l'altra dovrà essere
cambiata prima di chiamere getch()
;
altrimenti l'output del programma sarà indefinito.
Quando avete bisogno di accettare input per linee in una finestra,
sono disponibili le funzioni wgetstr()
e compagne.
C'è addirittura la funzione wscanw()
che può analizzare
l'input della finestra in stile scanf()(3).
Queste funzioni orientate alla linea attivano la echo durante
l'esecuzione.
Il codice d'esempio di prima utilizza la chiamata
keypad(stdscr, TRUE)
per abilitare il supporto alla
mappatura dei tasti funzione.
Con questa caratteristica, la getch()
esamina lo stream di
input alla ricerca di sequenze di caratteri che corrispondano
alle freccie ed ai tasti funzione.
Queste sequenze sono trasformate in valori pseudo-caratteri.
Le #define
di questi valori solo elencate in curses.h
.
Le sequenze di caratteri da mappare nei valori delle #define
sono determinati dalle capacità key_
nel database terminfo
del terminale.
La funzione addch()
(ed alcune altre, incluse box()
e
border()
) possono accettare come argomento alcuni pseudo-caratteri
definiti dalle ncurses
. Si tratta di valori #define
impostati
nell'header curses.h
; guardate lì per un elenco completo
(cercate il prefisso ACS_
).
Le più utili tra le definizioni ACS sono i caratteri scatola.
Si possono usare per disegnare sullo schermo scatole (appunto)
e semplici grafici.
Qualora il terminale non avesse tali caratteri, curses.h
li mapperebbe in una serie di caratteri ASCII riconoscibili
(benchè sgradevoli).
Il pacchetto ncurses
supporta le evidenziazioni inclusi il
contrasto (standout), il rovescio (reverse-video), il sottolineato
e il lampeggiante. Inoltre supporta il colore che è trattato
come un ulteriore evidenziatore.
Gli evidenziatori sono codificati internamente nei bit alti del tipo
pseudo-carattere (chtype
) che curses.h
usa per
rappresentare il contenuto delle celle di uno schermo.
Guardate l'header curses.h
per una completa lista
dei valori maschera degli evidenziatori (cercate il prefisso A_
).
Ci sono due modi per evidenziare.
Uno consiste nell'eseguire un OR logico tra il valore
dell'evidenziatore ed il carattere argomento della
addch()
, o di qualunque altra chiamata di output che
accetti per argomento il tipo chtype
.
L'altro consiste nell'impostare il valore di evidenziazione corrente.
Questo viene aggiunto (OR logico) ad ogni altro valore impostato prima.
Per far questo si usano le funzioni attron()
, attroff()
,
e attrset()
; vedi le pagine del manuale per maggiori dettagli .
Il colore è una forma speciale di evidenziazione.
Il pacchetto attualmente ragiona in termini di coppie di colori,
combinazioni di colori di superficie e di sottofondo.
Il codice di esempio di prima impostava fino ad otto coppie di colori,
tutti quelli sicuramente disponibili.
Nota che ogni coppia di colori è, in effetti, data dal nome
del suo colore di superficie.
Ogni altra gamma di otto valori non-discordanti avrebbe potuto
essere usata come primo argomento delle init_pair()
.
Una volta eseguita una init_pair()
che crea la coppia di
colori N, potrete usare COLOR_PAIR(N)
come un evidenziatore
che fa apparire quella particolare combinazione di colori.
Notate che COLOR_PAIR(N)
, con N costante, è a sua volta una
costante di compilazione e può venire usata negli inizializzatori.
La libreria ncurses
fornisce inoltre una interfaccia
verso il mouse.
Notate che questa è una caratteristica originale delle ncurses
,
e non fa parte nè dello standard XSI curses, nè delle curses
System V Release 4, nè BSD.
Perciò ti raccomandiamo di assoggettare il codice relativo al mouse
ad una #ifdef
usando la macro NCURSES_MOUSE_VERSION
(fornita)
in modo da non venir compilato e linkato su sistemi non-ncurses.
Al momento, il rilevamento degli eventi del mouse funziona solo
sotto xterm(1).
In futuro, ncurses
rileverà la presenza di gpm(1),
il server del mouse per sistemi Linux scritto da
Alessandro Rubini, ed accetterà eventi
del mouse attraverso di questo.
L'interfaccia del mouse è davvero semplice.
Per attivarla si usa la funzione mousemask()
, passandole
come primo argomento una maschera di bit che specifichi i generi
di eventi che volete che il vostro programma sia capace di vedere.
Ritornerà la maschera di bit degli eventi che sono divenuti visibili,
che differirà dall'argomento parametro se il dispositivo mouse non
fosse in grado di rilevare alcuni degli eventi che avevate specificato.
Una volta attivato il mouse, il ciclo di comando della
vostra applicazione dovrà attendersi di ritorno dalla
wgetch()
il valore KEY_MOUSE
.
Quando lo riceverà vorrà dire che un evento del mouse è
stato accodato.
Per estrarlo dalla coda, usate la funzione getmouse()
(dovrete farlo prima della successiva wgetch()
,
altrimenti un altro evento del mouse potrebbe arrivare
rendendo inaccessibile il precedente ).
Ogni chiamata alla getmouse()
riempie una struttura
(il cui indirizzo le avrete fornito) con i dati dell'evento del mouse.
I dati dell'evento includono le coordinate della cella di
carattere puntata dal mouse, relative allo schermo con origine a zero.
E` anche inclusa una maschera di eventi. In questa maschera troverete
settati i bit corrispondenti al tipo di evento riportato.
La struttura mouse contiene due campi addizionali che potrebbero
essere significativi in futuro quando ncurses
interfaccerà
nuovi generi di dispositivi di puntamento.
In aggiunta alle coordinate x
e y
, c'è posto per la
coordinata z
; potrebbe essere utile con quei touchscreen
che possono riferire un valore di pressione o durata.
C'è anche un campo identità del dispositivo, che potrebbe essere
usato per distinguere tra dispositivi di puntamento multipli.
La classe degli eventi visibili può essere cambiata in ogni momento
via mousemask()
.
Gli eventi che sono rilevati includono la pressione, il rilascio,
il singolo, doppio e triplo click (potete impostare il massimo tempo
di pressione del tasto per un click).
Se non rendete visibili i click, verranno rilevati come coppie
di premuto e lasciato.
In alcuni ambienti la maschera degli eventi include bit di
rilevamento dello stato dei tasti shift, alt e control sulla
tastiera durante l'evento.
E' inoltre fornita una funzione per verificare se un evento del mouse avviene in una data finestra. Potete usarla per vedere se una data finestra dovrà considerare pertinente un certo evento del mouse.
Poichè il rilevamento degli eventi del mouse non sarà disponibile in
tutti gli ambienti, sarebbe poco saggio costruire applicazioni ncurses
che richiedano l'uso del mouse.
Piuttosto dovreste usare il mouse come una scorciatoia per comandi
di tipo punta-e-spara che la vostra applicazione accetterà
normalmente da tastiera.
Due dei giochi di prova contenuti nella distribuzione di
ncurses
(bs
e knight
) contengono codice che
illustra come si possa fare ciò.
Vedi la pagina del manuale curs_mouse(3X)
per completi dettagli
sulle funzioni dell'interfaccia al mouse.
Allo scopo di ripulire quando si è finito con le ncurses
, è
fornita la routine endwin()
.
Ripristina le modalità tty come erano prima della chiamata alla
initscr()
, e sposta il cursore nell'angolo inferiore sinistro.
Perciò prima di uscire, ogni volta che si sia usata la chiamata
initscr()
, dovrà venir chiamata la endwin()
.
Descriviamo qui il comportamento dettagliato di alcune importanti funzioni delle curses, come supplemento alle descrizioni delle pagine del manuale.
initscr()
La prima funzione chiamata deve quasi sempre essere initscr()
.
Questa determinerà il tipo del terminale ed inizializzerà le
strutture di dati delle curses. initscr()
inoltre fa sì che la
prima chiamata a refresh()
pulisca lo schermo. Se accade un errore
un messaggio viene scritto su standard error ed il programma esce.
Altrimenti ritorna un puntatore a stdscr. Poche funzioni possono essere
chiamate prima di initscr() (slk_init()
, filter()
,
ripofflines()
, use_env()
,
e, se usate più terminali, newterm()
.)
endwin()
Il vostro programma dovrà sempre chiamare endwin()
prima di uscire
o prima di lanciare una shell.
Questa funzione ripristina le modalità tty, muove il cursore
nell'angolo inferiore sinistro dello schermo, riporta il terminale
nel proprio modo non-visuale.
Chiamando refresh()
o doupdate()
dopo una uscita temporanea
dal programma ripristinerà lo schermo ncurses precedente all'uscita.
newterm(type, ofp, ifp)
Un programma che invia il suo output a più di un terminale deve
usare newterm()
invece di initscr()
.
newterm()
va chiamata una volta per ogni terminale.
Ritorna una variabile di tipo SCREEN *
che andrà salvata
come riferimento a quel terminale.
Gli argomenti sono il tipo di terminale (una stringa) e puntatori
a FILE
per l'input e per l'output del terminale.
Se il tipo è NULL allora la variabile d'ambiente $TERM
viene usata.
In chiusura endwin()
andrà chiamata una volta per ciascun
terminale aperto con questa funzione.
set_term(new)
Questa funzione si usa per passare ad un diverso terminale
aperto precedntemente da newterm()
. Si passa come parametro il
riferimento al nuovo terminale. La funzione ritorna il riferimento
al precedente terminale. Tutte le altre chiamate agiscono solo
sul terminale attivo.
delscreen(sp)
L'opposto di newterm()
; rilascia le strutture di dati
associate ad un dato riferimento a SCREEN
.
refresh()
e wrefresh(win)
Queste funzioni vanno chiamate per visualizzare l'output sul
terminale, in quanto le altre routine manipolano solamente
dati in memoria. wrefresh()
copia la finestra indicata sullo
schermo fisico del terminale, tenendo presente quanto vi si trova già
allo scopo di ottimizzare. refresh()
rinfresca lo stdscr()
.
A meno che leaveok()
non sia stato abilitato, il cursore fisico
del terminale è lasciato alla locazione del cursore della finestra.
doupdate()
e wnoutrefresh(win)
Queste due funzioni gestiscono aggiornamenti multipli con maggior
efficienza di wrefresh()
. Per usarle è importante comprendere come
funzionano le curses. In aggiunta a tutte le strutture delle
finestre, le curses mantengono due strutture di dati che
rappresentano lo schermo del terminale: uno schermo fisico
che descrive quanto si trova in quel momento sullo schermo, e
uno schermo virtuale che descrive quel che il programmatore vuole
sullo schermo. wrefresh()
funziona prima copiando la finestra data
sullo schermo virtuale (wnoutrefresh()
), e quindi chiamando la
routine per aggiornare lo schermo reale (doupdate()
).
Se il programmatore desidera mostrare diverse finestre insieme,
una serie di chiamate a wrefresh
causerà alternate chiamate a
wnoutrefresh()
e doupdate()
, causando differenti lampi di
output sullo schermo.
Chiamando wnoutrefresh()
per ciascuna finestra, è poi possibile
chiamare doupdate()
una volta sola, causando una unica
visualizzazione, trasmettendo un minore numero di caratteri (ciò
evita anche un noioso lampeggio ad ogni aggiornamento).
setupterm(term, filenum, errret)
Questa routine viene chiamata per inizializzare la descrizione
del terminale senza creare le strutture degli schermi o
cambiare i bit delle modalità della tty. term
è la stringa di
caratteri che rappresenta il nome del terminale in uso.
filenum
è il descrittore di file UNIX del terminale da usare
per l'output. errret
è un puntatore ad un intero in cui verrà
inserita indicazione di successo o fallimento. I valori possono
essere 1 (tutto bene), 0 (non c'è un terminale così), o -1
(qualche altro problema relativo al database terminfo).
Il valore di term
puòessere NULL, causando l'uso del valore
contenuto nella variabile d'ambiente TERM
.
Il puntatore errret
può anch'egli valere NULL,
indicando che il valore di errore non è richiesto.
Se errret
manca e qualcosa va storto, setupterm()
mostrerà un
appropriato messaggio d'errore ed uscità invece di ritornare.
Così un semplice programma può chiamare setupterm(0, 1, 0)
senza preoccuparsi degli errori di inizializzazione.
Dopo la chiamata a setupterm()
, la variabile globale cur_term
punta alla attuale struttura di capacità del terminale.
Chiamando setupterm()
per ciascun terminale e salvando e
ripristinando cur_term
, un programma può riuscire ad usare due o
più terminali insieme. setupterm()
inoltre immagazzina la sezione
dei nomi di una descrizione del terminale in un array di caratteri
globale ttytype[]
.
Successive chiamate a setupterm()
sovrascrivono
questo array, così dovrete salvarvelo se ne avete bisogno.
NOTA: QUESTE FUNZIONI NON FANNO PARTE DELLA API STANDARD DELLE CURSES!
trace()
Questa funzione può essere usata per impostare esplicitamente
un livello di tracciamento. se il livello di tracciamento è
diverso da zero, l'esecuzione del vostro programma genererà
un file chiamato 'trace' nel direttorio corrente contenente
un rapporto sulle azioni della libreria.
Più elevati livelli di tracciamento abilitano un rapporto più
dettagliato (e verboso) -- per dettagli vedi i commenti acclusi
alla definizione di TRACE_
nel file curses.h
.
E` inoltre possibile assegnare un livello di tracciamento
assegnando un valore alla variabile d'ambiente NCURSES_TRACE
.
_tracef()
Questa funzione può essere usata per mostrare informazioni
di debug. E` disponibile solo linkando con -lncurses_g
.
Può essere usata nello stesso modo di printf()
, solo che
emette un newline alla fine degli argomenti. L'output va
in un file chiamato trace
nel direttorio corrente.
Le informazioni di trace possono essere difficili da interpretare a
causa del solo volume di dati inseriti in essi.
Esiste uno script chiamato tracemunch incluso nella distribuzione
ncurses
che può alleggerire in qualche modo questo problema;
compatta lunghe sequenze di simili operazioni in pseudo-operazioni
di una riga. Queste pseudo-operazioni sono riconoscibili
dal fatto che hanno nomi maiuscoli.
Le pagine del manuale delle ncurses
sono un completo riferimento
per questa libreria.
Nel resto di questo documento discuteremo diversi utilizzi che
potrebbero non sembrare ovvii leggendo le pagine del manuale.
Se ti dovessi ritrovare a pensare di aver bisogno di usare noraw()
o nocbreak()
, ripensaci e muoviti con cautela.
E' probabilmente meglio progettare l'uso di getstr()
o una delle sue consimili per simulare la modalità cooked.
Le funzioni noraw()
e nocbreak()
provano a ripristinare
la modalità cooked, ma potrebbero finire maltrattando alcuni
bit di controllo settati prima che partisse la vostra applicazione.
Inoltre sono sempre state malamente documentate, e
probabilmente danneggerebbero la portabilità della vostra applicazione
con altre implementazioni delle librerie curses.
Tenete bene in mente che refresh()
è sinonimo di
wrefresh(stdscr)
, non cercate di mescolare l'uso di
stdscr
con l'uso di finestre dichiarate da newwin()
;
una chiamata a refresh()
può farle sparire dallo schermo.
Il modo giusto per gestire questo è di usare subwin()
,
oppure di non toccare stdsc
r per nulla e riempire il vostro
schermo di finestre dichiarate che poi voi rinfrescherete chiamando
wnoutrefresh()
da qualche parte del vostro programma, con una
sola chiamata a doupdate()
per far scattare l'aggiornamento.
E' molto meno probabile finire nei guai se progetterete schermate
usando finestre accostate piuttosto che sovrapposte.
Storicamente il supporto delle curses per le finestre sovrapposte è
stato debole, fragile e poveramente documentato.
La libreria ncurses
non è ancora un'eccezione a questa regola.
C'è una libreria freeware di panel (lastre, pannelli) inclusa nella
distribuzione ncurses
che fa piuttosto un buon lavoro nel
rinforzare le capacità di sovrapposizione delle finestre.
Provate ad evitare l'uso delle variabili globali LINES
e COLS
.
Invece usate getmaxyx()
su stdscr.
Il motivo: il vostro codice potrebbe finire per girare in un
ambiente con finestre ridimensionabili, nel qual caso parecchi
schermi potrebbero avere misure diverse.
ncurses
Alle volte vorrete scrivere un programma che passa la maggior parte del
suo tempo in modalità schermo, ma occasionalmente nella modalità
ordinaria cooked.
Una ragione comune per questo è il supporto per lanciare una shell.
Questo comportamento è semplice da organizzare nelle ncurses
.
Per lasciare la modalità ncurses
, chiamate endwin()
come
foste intenzionati a terminare il programma.
Questo riporterà lo schermo in modalità cooked;
potete lanciare la vostra shell.
Quando vorrete ritornare in modalità ncurses
,
semplicemente chiamate refresh()
o doupdate()
.
Questo ridisegnerà il vostro schermo.
C'è una funzione booleana, isendwin()
, che può essere usata
per verificare se la modalità ncurses
è attiva.
Ritorna TRUE
nell'intervallo tra una endwin()
e la
successiva refresh()
, altrimenti ritorna FALSE
.
Ecco un pò di codice esempio per lanciare una shell:
addstr("Lancio shell...");
def_prog_mode(); /* salva le modalità tty correnti */
endwin(); /* ripristina le modalità tty originali */
system("sh"); /* lancia la shell */
addstr("di ritorno.\n"); /* prepara il messagio */
refresh(); /* ripristina le modalità salvate, */
/* ridisegna lo schermo */
ncurses
sotto xterm(1) In X una operazione di cambio delle dimensioni invia il segnale
SIGWINCH
all'applicazione che gira sotto xterm(1).
La libreria ncurses
non cattura questo segnale, perchè in
generale non può sapere in qual modo voi volete ridisegnare lo schermo.
Dovrete scrivervi il vostro gestore di SIGWINCH.
Il modo più facile di scrivere il vostro gestore di SIGWINCH
è di chiamare endwin()
, seguito da initscr()
e da un
ridisegno dello schermo che farete voi. initscr()
si troverà
le nuove dimensioni dello schermo dall'ambiente xterm(1).
La funzione initscr()
attualmente chiama una funzione di nome
newterm()
per fare il più del suo lavoro.
Se state scrivendo un programma che
apre più terminali, usate newterm()
direttamente.
Per ciascuna chiamata, dovrete specificare un tipo terminale
ed una coppia di puntatori a file; ciascuna chiamata ritornerà
un riferimento ad uno schermo, e stdscr
verrà impostato
all'ultimo allocato.
Vi sposterete tra gli schermi con la chiamata set_term
.
Notate che dovrete inoltre chiamare def_shell_mode
e
def_prog_mode
su ciascuna tty.
Alle volte vorrete scrivere programmi che saggino la presenza di varie
capacità prima di decidere se entrare in modalità ncurses
.
Un modo facile di far questo è di chiamare setupterm()
,
e poi usare le funzioni tigetflag()
, tigetnum()
, e
tigetstr()
per le vostre prove.
Un caso particolarmente utile di questo viene quando volete verificare
se un dato tipo di terminale deve essere trattato come capace
(di muovere il cursore) oppure stupido.
Il modo corretto di provare questo è di vedere se il valore ritornato
da tigetstr(''cup'')
è non-NULL.
Alternativamente, potete includere il file term.h
e saggiare
il valore della macro cursor_address
.
Usate la famiglia di funzioni addchstr()
per una veloce
scrittura di testo quando sapete che il testo non contiene alcun
carattere di controllo.
Provate a fare rari cambi di attributi sui vostri schermi.
Non usate l'opzione immedok()
!
ncurses
Quando gira sui cloni PC, ncurses
ha supporto accresciuto
per i set di caratteri IBM.
L'evidenziatore A_ALTCHARSET
abilita la visualizzazione sia
della grafica della metà superiore che della grafica 0-31 della
PC ROM che normalmente sono interpretati come caratteri di controllo.
Nonostante tutti i nostri sforzi, esistono alcune differenze tra le
ncurses
ed il comportamento (non documentato!) di precedenti
implementazioni delle curses. Ciò nasce da ambiguità ed omissioni
nella documentazione della API.
Se definite due finestre A e B che si sovrappongano, e quindi le
scambiate e rinfrescate, gli aggiornamenti alla regione dove si
sovrappongono non è spesso precisamente documentata nelle versioni
storiche delle curses
.
Per capire perchè ciò sia un problema, vi ricordo che gli aggiornamenti dello schermo vengono calcolati confrontando due rappresentazioni dell'intero schermo. La documentazione dice che quando si rinfresca una finestra, viene prima copiata sullo schermo virtuale, quindi i cambiamenti vengono calcolati per aggiornare lo schermo fisico (ed inviati al terminale). Ma ''copiata su'' non specifica molto, e sottili differenze nel come si esegue la copia può produrre diversi risultati nel caso che due finestre sovrapposte vengano rinfrescate ciascuna a inervalli indeterminati.
Quello che succede alla regione dove si sovrappongono dipende da ciò
che la wnoutrefresh()
fa con i suoi argomenti -- quali porzioni
della finestra argomento viene copiata sullo schermo virtuale.
Alcune implementazioni eseguono una ''copia dei cambiamenti'',
copiando solo le parti della finestra che sono cambiate (o che sono
state segnalate come cambiate dalla wtouchln
e compagne).
Altre implementazioni eseguono una ''intera copia'' copiando
tutta la finestra sullo schermo virtuale, sia che fosse
cambiata che no.
La stessa libreria ncurses
non è stata sempre coerente su questo
punto. A causa di un baco, le versioni dalla 1.8.7 alla 1.9.8a eseguiva
l'intera copia. Le versioni fino alla 1.8.6 e le versioni 1.9.9 e
successive eseguono la copia dei cambiamenti.
Per molte implementazioni commerciali delle curses, non è documentato
e non è noto con certezza (almeno non ai manutentori delle ncurses
)
se eseguano la copia dei cambiamenti o la intera copia. Sappiamo che
le curses del System V release 3 contiene della logica che sembra un
tentativo di eseguire una copia dei cambiamenti, ma la logica
circostante e le rappresentazioni dei dati sono sufficientemente
complesse, e la nostra conoscienza sufficientemente indiretta, che è
duro sapere quanto questo sia affidabile.
Non è chiaro cosa intenda la documentazione della SVr4 e dello standard XSI. Lo standard delle curses XSI accenna appena alla wnoutrefresh(); i documenti SVr4 sembrano stare descrivendo la intera copia, ma è possibile, con qualche sforzo e stortura leggere nel modo opposto.
E` comunque poco saggio confidare in uno di questi comportamenti in
programmi che debbano essere linkati con altre implementazioni delle
curses. Invece, potete fare una esplicita touchwin()
prima di
chiamare la wnoutrefresh()
per garantire ovunque una copia
dell'intero contenuto.
la via veramente pulita per gestire questo è di usare la libreria
panel. Se, quando volete un aggiornamento dello schermo, fate una
update_panels()
, eseguirà tutte le necessarie chiamate alla
wnoutrefresh()
per qualunque ordine di sovrapposizione abbiate
definito. Quindi potrete fare una doupdate
e ci sarà una
sola raffica di I/O fisico che eseguirà tutti i vostri aggiornamenti.
Se avete usato versioni di ncurses
molto vecchie (1.8.7 o
precedenti) potreste restare sorpresi dal comportamento delle
funzioni di cancellazione.
In versioni più vecchie, le aree cancellate di una finestra erano
riempite di spazi modificati dagli attributi correnti della finestra
(come settati da wattrset()
, wattron()
, wattroff()
e
compagne).
In nuove versioni, non è più così. Invece, l'attributo degli spazi
cancellati è normale a meno che e finchè non viene modificato dalle
funzioni bkgdset()
o wbkgdset()
.
Questo cambiamento nel comportamento conforma le ncurses
alla
Release 4 di System V ed allo standard XSI Curses.
La libreria ncurses
intende essere conforme a livello base con le
standard Curses XSI da X/Open.
Molte caratteristiche di livello esteso (infatti almeno tutte le
caratteristiche non direttamente connesse coi caratteri estesi e
con l'internazionalizzazione) sono già supportate.
Un effetto della conformità XSI è il cambiamento di comportamento descritto in Cancellazione del Fondo -- Compatibilità con le Vecchie Versioni .
Inoltre, ncurses
onora la richiesta XSI che ogni macro abbia
una funzione corrispondente che possa essere linkata (e il cui
prototipo venga controllato) se la definizione della macro
viene disabilitata con #undef
.