Ncurses
Der ein oder andere kennt bestimmt noch die "alten Dos-Fenster", sprich ASCII-orientierte Fenster, die unter DOS relativ haeufig anzutreffen waren, und unter Linux immer noch sehr verbreitet sind, da man mit ihnen ohne ein lauffaehiges X ein einigermaßen übersichtliches Programm schreiben kann. Als Beispiel möchte ich hier den Midnight-Commander oder Yast1 nennen. (Ich kann nicht 100%-ig behaupten die waeren mit ncurses programmiert, aber mit ncurses geht sowas ;-)

Die ncurses Libraries sind Bibliotheken, welche Funktionen zur verfügung stellen, mit denen man leicht

  • Terminalunabhängige,
  • Farbige,
  • Mausgestützte und
  • Ereignisorientierte

Programme realisieren kann. Diese Programme sind Zeichenorientiert und laufen somit auch auf Systemen ohne X, auf virtuellen konsolen oder über eine Netzwerkverbindung. Sie laufen somit auch problemlos über eine Telnetverbindung.

Ich möchte in diesem Tutorial nicht auf alle einzelnen Punkte eingehen, sondern anhand eines Programmbeispiels einige Dinge erklären, und den Einstieg in die Ncurses Programmierung erleichtern. Ich kann allerdings NICHT den Aufbau der ncurses beschreiben.


Voraussetzung


Die "new curses" (ncurses) Libraries müssen installiert sein.
Dieses sind u.a. die:

  • curses.h
  • panel.h - um verschiedene Fenster anzuzeigen
  • menu.h - um Menüs einzubauen
  • form.h - um Formulare zu realisieren

Dokumentationsquellen

Die wichtigsten Quellen sind auf jeden Fall hier die man-Pages der Libraries:
man curses, man panel, man form, und man menu.
Hier sind alle Funktionen oder weitere man-Pages beschrieben.
 
Auf http://invisible-island.net/ncurses/ncurses-intro.html#introduction gibts auch noch ein Dokument, das den Einstieg erleichtern soll. Aus diesem Script habe ich viel gelernt; allerdings mangelt es etwas an Beispielen, so dass die programmtechnische Umsetztung nicht unbedingt erleichtert wird.
 
Auf http://www.paulgriffiths.net/program/c/curworm.html gibts noch Quellcode eines Worms-Spieles, das mit ncurses programmiert wurde.
 


Aufbau eines Ncurses Programms


Bevor ich groß anfange zu erklären erstmal der Quellcode von paneltest.c

#include <stdio.h>
#include <ncurses.h>
#include <panel.h>

int main(void){
                 

//Variablen vom Typ WINDOW deklarieren
WINDOW *win1, *winok, *wincancel;
//Variablen vom Typ PANEL deklarieren
PANEL *panel1, *panelok, *panelcancel;
char command, lines[100], cols[100];
void ende();

initscr();
noecho();
cbreak();


/*Programm fuer Farbdarstellung vorbereiten*/
if(has_colors){	

  start_color();          
  init_pair(1, COLOR_YELLOW, COLOR_GREEN);
  //Farbpaare (Vorder/Hintergrund) definieren
  init_pair(2, COLOR_GREEN, COLOR_BLACK);
                	
}


/*Fenstervariablen inititalieren*/
//FullScreenWindow
win1=newwin(0,0,0,0);
//Ein OK-Fenster, Ausrichtung Mittig
winok=newwin(3,8,LINES/2-1, COLS/2-10);
//Ein Cancel-Fenster, Ausrichtung Mittig
wincancel=newwin(3,8,LINES/2-1, COLS/2+10-8);

/*Panels initialisieren und mit Fenstern verbinden*/
//verbinde win1 mit panel1 um es anzeigen/verstecken zu koennen
panel1=new_panel(win1);
panelok=new_panel(winok);		//dito
panelcancel=new_panel(wincancel);	//dito


/*Fenster mit Inhalt fuellen*/
mvwaddstr(win1,1,1,lines);
mvwaddstr(win1,2,1, cols);
mvwaddstr(winok,1,1, "ok");
mvwaddstr(wincancel,1,1,"Cancel");
wattrset(winok, COLOR_PAIR(1));
wattrset(wincancel, COLOR_PAIR(1));
wborder(win1,0,0,0,0,0,0,0,0);		
wborder(winok,0,0,0,0,0,0,0,0);
wborder(wincancel,0,0,0,0,0,0,0,0);

show_panel(panel1);
update_panels();
doupdate();

        
show_panel(panelok);
update_panels();
doupdate();

show_panel(panelcancel);
update_panels();
doupdate();


/*Warten auf Ereignisse*/
for(;;){
  command=getch();
  if(command=='q' || command=='Q') {
        endwin();
        exit(0);
  }
                  
  if( command=='o' || command=='O') {
        if(panel_hidden(panelok)){
        show_panel(panelok);
        }
        else hide_panel(panelok);
        update_panels();
        doupdate();
  }
                
}

endwin();	//Normalzustand wieder herstellen, Fenster entfernen
return 0;
}

Um dieses Programm zu übersetzen, müssen wir explizit die benutzen Libraries lpanel und lncurses linken. Der Befehl lautet:

user@sonne> gcc paneltest.c -o paneltest -lpanel -lncurses

Ein ncurses Programm muss folgenden Aufbau haben:


initscr() Dieses sollte unbedingt der erste Aufruf sein, da er für die Initialisierung sorgt.

Danach kommen meist noch einige Aufrufe, um das Verhalten des Programmes anzupassen wie z.B.:
noecho() - Tastatureingaben werden nicht ausgegeben
cbreak() - getch() wartet nur auf ein einzelnes Zeichen (nicht auf Return)
Diese sind aber nicht zwingend nötig. (Weitere Routinen siehe man curses)

Bevor ein Ncurses Programm beendet wird, muss man dafür sorgen, dass der Normalzustand wieder hergestellt und der Speicher freigegeben wird:
endwin()

Das reicht prinzipiell schon, um ein ncurses Programm zu programmieren. Solange keine weiteren Fenster oder Unterfenster benutzt werden, heißt das einzige Fenster stdscr. Dieses wird von initscr() erstellt.
 
In diesem Beispiel werden allerdings mehrere Fenster benutzt. Ich empfehle dann nicht mehr mit stdscr zu arbeiten, obwohl es ihn trotzdem gibt.


Einige Funktionen


Ich möchte hier noch einige wichtigeTeile aus dem Code beschreiben:

winok=newwin(3,8,LINES/2-1, COLS/2-10);
Mit dem Aufruf newwin wird einer WINDOW - Variablen ein neues Fenster zugewiesen. newwin erwartet folgende Parameter: newwin(X-Koordinate, Y-Koordinate, Höhe, Breite)

panel1=new_panel(win1)
Ein Panel ist nötig, um ein Window anzuzeigen oder zu verstecken oder zu verschieben.

mvwaddstr(win1,1,1,lines)
Fügt an einer bestimmten Stelle eines Fensters einen String ein. Erwartet folgende Parameter: mvwaddstr(fenstername, X-koordinate, Y-koordinate , String). X/Y-Koordinaten sind realtiv zum Fenster. Wenn nur mit stdscr gearbeitet wird ist auch mvaddstr(X-koordinate, Y-koordinate , String) möglich.

wattrset(winok, COLOR_PAIR(1));
Setzt Attribute eines Fensters, in diesem Fall die Farben, die vorher mit init_pair(1, COLOR_YELLOW, COLOR_GREEN) definiert wurden. Es gibt allerdings noch andere Attribute wie z.B. blink. Näheres in den Manpages.

wborder(win1,0,0,0,0,0,0,0,0)
Zeichnet einen Rand um das Fenster win1. Erwartet folgende Parameter: wborder(fenstername , 0,0,0,0,0,0,0,0) - wenn nur Nullen angegeben werden, wird ein normaler Rand gezogen, ansonsten kann man für die Ecken und die Ränder ein beliebiges Zeichen eingeben, mit diesem dann der Rand gezeichnet wird.

show_panel(panel1);
update_panels();
doupdate();

Mit show_panel(panelname) wird das Panel angezeigt. Genauer: zuoberst auf einen Stapel mehrerer Panels gelegt.
update_panels zeichnet die Panels virtuell neu (entspricht einem Refresh z.B. in einem Grafikprogramm)
doupdate(); zeigt den virtuellen Panel auf dem Screen an.
Nach Änderungen wie z.B. hinzufügen eines Strings, Rahmens o.ä. ist immer ein solcher Refresh nötig. Es gibt auch noch andere Routinen wie z.B. refresh(), die man aber mit Panels nur bedingt einsetzten sollte. (Dazu sollte man wieder mal die Manpages konsultieren ).

Ich möchte noch auf eine Besonderheit hinweisen, wenn man mit ncurses arbeitet:
getch() - Diese Funktion erlaubt mir eine komfortable ereignisgesteuerte Bedienung meines Programms. Sie gibt eine Tastatureingabe (sowohl Steuer- als auch normale Zeichen) zurück, die man dann auswerten kann.

Autor: Baitronic