Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Linux, Systemprogrammierung C C++ C/C++ ANSI C Linux Linuxsystemprogrammierung Semigrafik curses.h ncurses curses Linuxsystemprogrammieren Semigrafik curses.h ncurses curses 10. Semigrafik (curses.h)

10.1. Initialisierung           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

ncurses steht für new curses und ist ein freier Klon der Library SVR4 UNIX von Bell Lab. Für Umsteiger von M$-DOS Systemen ist curses eine der Alternativen zu conio.h. Umsteiger von den eben genannten System werden nach etwas Eingewöhnungszeit mit einer gewaltigen und funktionsreichen Lib belohnt.

Als Alternative zu curses wäre noch termcap und terminfo zu nennen. Da die Tendenz heute immer mehr auf Vollgrafischen Anwendungen geht, will ich hier nur curses besprechen, damit Sie wenigstens eine Möglichkeit kennen, eine Halbgrafische Terminalanwendung zu erstellen.

Da sich die curses.h-Lib (auf manchen System auch ncurses.h), nicht auf jedem System Standardmäßig befindet (unglaublich nicht wahr), müssen Sie diese Library beim Compilieren hinzulinken. Beispielsweise mit ...

gcc -o programmname programmname.c -lncurses /*oder auch -lcurses*/

Ein wenig zum Begriff Window
Eine kurze Einführung über das Fenster unter curses, da es häufig Verständigungsprobleme dazu gibt.

Einen ersten Überblick zu curses können Sie sich unter ...

man ncurses oder man curses

...verschaffen. Diese Manual-Seite dient auch für weitere Recherchen zu dieser Lib, sofern Sie auf dieser Webseite mal nichts über ein bestimmte Funtkion finden. Nun wollen wir Beginnen und das Grundgerüst zu curses ansehen.

Mit der Funktion ...

WINDOW *initscr(void);

...initialisieren Sie ncurses, und bekommen einen Zeiger auf stdscr zurück bei Erfolg, oder im Fehlerfall NULL.

Jetzt könnten Sie mit ncurses-Funktionen arbeiten. Wenn Sie das Programm wieder beenden wollen, machen Sie dies mit der Funktion ...

int endwind(void);

...und das aktuelle Terminal wird wieder freigegeben. Diese Funktion liefert die Konstante OK zurück, wenn alles klar ging, oder bei Fehler die Konstante ERR.

Somit sieht das Grundgerüst zur curses-Programmierung folgendermaßen aus ...

/*Übersetzen bitte mit : gcc -o cur1 cur1.c -lncurses*/
#include <curses.h>

int main()
{
/*curses initialisieren*/
 initscr();
 /*..........*/
 /*curses-Funktionen usw.*/
 /*.........*/
 /*curses beenden*/
 endwin();
}

Logischerweise passiert nach dem Übersetzen und Starten des Programms nichts.

10.2. Tastaturmodus und Ein-und Ausgabe           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Wollen wir uns nun Möglichkeiten ansehen, wie wir mittes ncurses, Zeichen von der Tastatur einlesen und wieder ausgeben können.

Der Tastaturmodus

int cbreak(void);
int nobreak(void);

Mit diesen beiden Funktionen können Sie das Puffern (Zeilenweise unter Linux) abstellen (mit cbreak) oder wieder aktivieren (mit nobreak). Mit der Funktion cbreak() bearbeitet der Tastaturtreiber nur noch die Tastenkombinationen CTRL+S und CTRL+Q und CTRL+C. Standardmäßig ist unter Linux logischweise (auch unter ncurses) der Puffer eingeschalten.

Wollen Sie auch das noch unterbinden, gibt es die Funktion ...

int raw(void);

Damit wäre Ihre Tastatur quasi nicht mehr einsetzbar. Rückgängig machen Sie die Funktion raw(), mit der Funktion ...

int noraw(void)

Damit keine Tasturcodes oder Zeichen mehr auf dem Bildschirm ausgegeben werden, verwendet ncurses die Funktion ...

int noecho(void);

...welche übrigens die Voreinstellung ist. Rückgängig machen können Sie die Funktion noecho() (keine Ausgabe) mit dem Gegenstück ...

int echo(void);

Jetzt nur noch die Funktion ...

int keypad(WINDOW *win, bool bf);

...womit sie testen können, ob die Tasten wie z.B. Cursor UP, Page down, Home, Cursor DOWN, End, F1 ... usw. betätigt haben. Die Variable WINDOW *win haben Sie bereits am Anfang bei initscr() initialisiert (stdscr) und mit bool bf können Sie die Konstanten TRUE für wahr oder FALSE für falsch einsetzen. Mit ...

keypad(stdscr,TRUE);

...legen Sie das Verhalten für den gesamten Bildschirm fest und gibt außerdem den Wert für die Funktions- oder Cursortasten zurück.

Ein- und Ausgabe
Um Ausgabefunktionen von Curses zu aktivieren muss die Routine ...

refresh();

...aufgerufen werden. Aber dazu später mehr. Bei Curses haben fast alle Routinen zur Ein.- und Ausgabe zwei Varianten. Zum einen die normale und zum anderen die mit einem mv-Präfix beginnen. Beide Schreibweisen bedeuten das selbe, nur das Sie mit dem mv-Präfix (mv=move) den Cursor erst noch auf die Position y,x stellen können. Achtung, falls Sie die Koordinaten von conio.h noch im Kopf haben (x,y), bei curses geben Sie zuerst die y-Koordinate und dann die x-Koordinaten an.

int getch(void);
int mvgetch(int y, int x);

Mit der Funktion getch() lesen Sie ein Zeichen von der Tastatur ein. mvgetch(y,x) hat den selben Effekt, nur eben noch mit den Koordinaten y und x.(Zeile und Spalte). getch() ist natürlich auch abhängig von den Funktionen cbreak(),raw(), nocbreak() und noraw(). Mit getch() können auch Werte jenseits von 0...255 zurückgegeben werden. Dadurch können Sie, auch wenn Sie die Funktionstastenerkennung keypad(stdscr,TRUE) aktiviert haben, den Integerwert zurückgeben ...

#include 

int main()
{
        int c;
        initscr();   /*curses initialisieren*/
        keypad(stdscr,TRUE);
        while((c=getch()) == KEY_DOWN || c==KEY_UP || c==KEY_LEFT || c==KEY_RIGHT)
                  ;
        endwin();
        return 0;
}

Wenn sie das Programm ausführen, aktivieren Sie mit keypad(stdscr,TRUE) die Cursor- und Funktionstasten. In unserem Fall können Sie so lange die Tasten Pfeilnachunten, Pfeilnachoben,Pfeilnachrechts und Pfeilnachlinks (KEY_DOWN,KEY_UP,KEY_RIGHT,KEY_LEFT) drücken und es wird nichts passieren. Sollten Sie aber eine andere Taste außer den vier eben genannten Tasten betätigen, wird das Programm beendet. Unter anderem sind außer den Tasten KEY_DOWN,KEY_UP,KEY_RIGHT,KEY_LEFT noch folgende Tastaturcodes in der Headerdatei curses.h definier ...

#define KEY_CODE_YES 0400     /* A wchar_t contains a key code */
#define KEY_MIN 0401          /* Minimum curses key */
#define KEY_BREAK 0401        /* Break key (unreliable) */
#define KEY_DOWN 0402         /* Down-arrow */
#define KEY_UP 0403           /* Up-arrow */
#define KEY_LEFT 0404         /* Left-arrow */
#define KEY_RIGHT 0405        /* Right-arrow */
#define KEY_HOME 0406         /* Home key (upward+left arrow) */
#define KEY_BACKSPACE 0407    /* Backspace (unreliable) */
#define KEY_F0 0410           /* Function keys. Space for 64 */
#define KEY_F(n) (KEY_F0+(n)) /* Value of function key n */
#define KEY_DL 0510           /* Delete line */
#define KEY_IL 0511           /* Insert line */
#define KEY_DC 0512           /* Delete character */
#define KEY_IC 0513           /* Insert char or enter insert mode */
#define KEY_EIC 0514          /* Exit insert char mode */
#define KEY_CLEAR 0515        /* Clear screen */
#define KEY_EOS 0516          /* Clear to end of screen */
#define KEY_EOL 0517          /* Clear to end of line */
#define KEY_SF 0520           /* Scroll 1 line forward */
#define KEY_SR 0521           /* Scroll 1 line backward (reverse) */
#define KEY_NPAGE 0522        /* Next page */
#define KEY_PPAGE 0523        /* Previous page */
#define KEY_STAB 0524         /* Set tab */
#define KEY_CTAB 0525         /* Clear tab */
#define KEY_CATAB 0526        /* Clear all tabs */
#define KEY_ENTER 0527        /* Enter or send (unreliable) */
#define KEY_SRESET 0530       /* Soft (partial) reset (unreliable) */
#define KEY_RESET 0531        /* Reset or hard reset (unreliable) */
#define KEY_PRINT 0532        /* Print */
#define KEY_LL 0533           /* Home down or bottom (lower left) */

Natürlich waren das noch nicht alle. Weiter Funktionstasten können Sie unter /usr/include/curses.h selbst nachlesen.

Zwei Funktionen zur Eingabe von Strings währen ...

int getstr(char *string);
int getnstr(char *string, int n);

getstr() entspricht der Version die Sie mit gets() bereits kennen. Und getnstr() ist ähnlich von fgets(). Es wird empfohlen getnstr() zu benutzten, da durch den zusätzlichen Parameter n ein Pufferüberlauf verhindert werden kann. Als Alternativen zur genauen Cursorpositionierung gibt es auch hier die mv-Funktionen ...

int mvgetstr(char *string);
int mvgetstr(int y, int x, char *string, int n);

Hierzu noch ein kurzes Programmbeispiel ...

#include <curses.h>
#define MAX 100

int main()
{
 char string[MAX];
 char string2[MAX];

 initscr();    /*curses initialisieren*/
 getnstr(string,MAX);
 mvgetnstr(10,20,string2,MAX);
 endwin();     /*und beenden*/

 printf("Die 1.Eingabe lautet : %s \n",string);
 printf("Die 2.Eingabe lautet : %s \n",string2);

 return 0;
}

Dann gibt es eigentlich nur noch die Funktion ...

int ungetch(char zeichen);

...womit Sie ein Zeichen wieder zurück in den Eingabepuffer schicken können.

Kommen wir jetzt zu den Ausgabe-Funktionen von curses.h. Alle Ausgabe-Funktionen müssen mit ...

int refresh();

...aktiviert werden um auf dem Bildschirm etwas anzuzeigen.

int move(int y, int x);

Mit der Funktion move() positionieren Sie den Cursor in der y-ten Zeile und x-ten Spalte.

int deleteln(void);

Mit der Funktion deleteln() löschen Sie die Zeile in der der Cursor gerade steht. Die unteren Zeilen werden dabei nach oben gezogen.

int insertln(void);

Mit der Funktion insertln() fügen Sie eine Leerzeile über die aktuellen Zeile der Cursorposition ein. Die unterste Zeile geht dabei verloren.

int insdelln(int anzahl);

Mit insdelln() fügen Sie anzahl Zeilen über die aktuelle Cursorposition ein und schieben die unteren Zeilen nach unten. Dadurch gehen die untersten anzahl Zeilen verloren.

int printw(char *format, ...);
int mvprintw(int y, int x, char *format, ...);

Die Funktion printw() ist gleichwertig mit der Funktion printf() aus der C-Standartbibliothek und mit mvprintw ersparen Sie sich die Funktion move(). Mit printw() sind keine ACS_ Sonderzeichen darstellbar, da char nur 8 Bit breit ist. Was ACS_ Sonderzeichen sind erfahren sie gleich. Wird wieder mal Zeit für ein kleines Programmlisting ...

#include <curses.h>
#define MAX 100

int main()
{
   char string[MAX],p[1];
   char string2[MAX];
   char string3[] = "Dies ist der neue String3\n";
   int c;

   initscr();    /*curses initialisieren*/
   printw("Bitte Eingabe für String machen : ");
   getnstr(string,MAX);
   mvprintw(3,0,"Bitte Eingabe für 2. String machen : ");
   getnstr(string2,MAX);
   mvprintw(7,20,"String ist gleich : %s\n",string);
   mvprintw(8,20,"Diese Zeile wird nun mit deleteln() gelöscht\n");
   mvprintw(9,20,"String 2 ist gleich : %s\n",string2);

   mvprintw(14,20,"Weiter mit TASTE");
   noecho();
   c=getch();

   move(8,20);
   deleteln();    /*Zeile löschen*/
   noecho();
   c=getch();
   move(8,20);
   insertln();    /*Leerzeile einfügen*/
   mvprintw(8,20,string3);
   noecho();
   c=getch();

   clear();     /*Bildschirm löschen*/
   refresh();
   mvprintw(12,38,"ENDE");
   halfdelay(20);    /*Wartet 20 Zehntelsec. auf getch()*/
/*ansonsten beendet sich das Progr. selber*/

   c=getch();

   endwin();
   return 0;
}
int addch(chtype zeichen);

Mit der Funktion addch() geben Sie ein Zeichen an der aktuellen Position aus. Der Typ chtype kann 32-Bit Werte aufnehmen und dadurch können Sie Attribute setzen mit dem Bitweisen ODER-Operator '|'. Das heißt Sie können Attribute mit dem Zeichen verknüpfen. Folgende Sonderzeichen lassen sich damit darstellen ...

#define ACS_ULCORNER (acs_map['l'])     /* upper left corner */
#define ACS_LLCORNER (acs_map['m'])     /* lower left corner */
#define ACS_URCORNER (acs_map['k'])     /* upper right corner */
#define ACS_LRCORNER (acs_map['j'])     /* lower right corner */
#define ACS_LTEE (acs_map['t'])         /* tee pointing right */
#define ACS_RTEE (acs_map['u'])         /* tee pointing left */
#define ACS_BTEE (acs_map['v'])         /* tee pointing up */
#define ACS_TTEE (acs_map['w'])         /* tee pointing down */
#define ACS_HLINE (acs_map['q'])        /* horizontal line */
#define ACS_VLINE (acs_map['x'])        /* vertical line */
#define ACS_PLUS (acs_map['n'])         /* large plus or crossover */
#define ACS_S1 (acs_map['o'])           /* scan line 1 */
#define ACS_S9 (acs_map['s'])           /* scan line 9 */
#define ACS_DIAMOND (acs_map['`'])      /* diamond */
#define ACS_CKBOARD (acs_map['a'])      /* checker board (stipple) */
#define ACS_DEGREE (acs_map['f'])       /* degree symbol */
#define ACS_PLMINUS (acs_map['g'])      /* plus/minus */
#define ACS_BULLET (acs_map['~'])       /* bullet */

Bei folgenden Typen kann es passieren, dass diese nicht richtig dargestellt werden. Als Alternative werden stattdessen einfache ASCII-Zeichen dargestellt. Z.B. für Pfeil nach Links '<' oder dem Pfeil nach rechts '>' ...

/* Teletype 5410v1 symbols begin here */
#define ACS_LARROW (acs_map[','])    /* arrow pointing left */
#define ACS_RARROW (acs_map['+'])    /* arrow pointing right */
#define ACS_DARROW (acs_map['.'])    /* arrow pointing down */
#define ACS_UARROW (acs_map['-'])    /* arrow pointing up */
#define ACS_BOARD (acs_map['h'])     /* board of squares */
#define ACS_LANTERN (acs_map['i'])   /* lantern symbol */
#define ACS_BLOCK (acs_map['0'])     /* solid square block */

Zur Demonstration will ich Ihnen ein kleines Beispiel schreiben. Wir zeichnen einen einfachen Würfel und schreiben mitten in den Würfel ein Wort ...

#include <curses.h>

int main()
{
   int i,j;

   initscr();
   addch(ACS_ULCORNER);
   for(i=0; i<20; i++)
     addch(ACS_HLINE);
   addch(ACS_URCORNER);
   addch('\n');

   for(i=0; i<10; i++)
     for(j=0; j<=21; j++)
       if(j==0)
         addch(ACS_VLINE);
       else if(j==21)
         {
           addch(ACS_VLINE);
           addch('\n');
         }
       else
         addch(' ');

   addch(ACS_LLCORNER);
   for(i=0; i<20; i++)
     addch(ACS_HLINE);
   addch(ACS_LRCORNER);
   addch('\n');

   mvprintw(5,7,"<TASTE>");
   getch();
   endwin();
   return 0;
}

So siehts aus:

curses - ACS_ Zeichen

Natürlich gibt es außer der Funtkion addch() noch ...

int addstr(char *string);

...womit Sie einen ganzen String ausgeben können oder die Funktion ...

int addnstr(char *string, int anzahlzeichen);

Zum Beispiel ...

addstr("Hallo Welt");
addnstr("Hallo Welt",5);    //Wird nur Hallo ausgegeben

Natürlich gibt es auch bei diesen Funktionen weitere mit dem vorangestellten mv-Präfix zum Positionieren. Es gibt dazu noch mehr Funktionen dieser Art. Lesen Sie bitte dazu ...

man addstr

...die Man-Pages dazu, falls Sie noch weitere benötigen. Ehemalige MS-DOS´ler sehen schon, dass dort wo conio.h aufhört, fängt curses.h erst an!

int clrtobot(void);

Mit clrtobot() löschen Sie den Bildschirm von der aktuelle Position aus.

int clrtoeol(void);

Mit der Funktion clrtoeol() löschen Sie den Rest (nicht die ganze Zeile) der Zeile in der der Cursor gerade steht.

int delch(void);
int mvdelch(int y, int x);

Mit diesen beiden Funktionen können Sie das Zeichen löschen auf dem der Cursor gerade steht. Alle weiteren Zeichen in der gleichen Zeile werden dabei um ein Position nach links versetzt ...

#include <curses.h>

int main()
{
   initscr();
   mvprintw(5,5,"Diese Zeile enthält einen Fehhler");
   mvprintw(6,5,"Bitte Taste drücken für Fehlerkorrektur");
   getch();
   mvdelch(5,33);
   mvprintw(7,5,"Fehler wurde koregiert! Bitte Taste drücken!");
   getch();
   endwin();
   return 0;
}

In diesem Programm haben wir das Wort 'Fehhler' mit zwei 'h' geschrieben und haben diesen Fehler mittels mvdelch(y,x) gelöscht.

int erase(void);
int clear(void);

Diese beiden Funktionen löschen den Bildschirm. Mit clear() wird bei dem nächsten refresh() Aufruf der Bildschirm komplett neu Ausgegeben.

10.3. Eigenschaften der Fenster           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

int clearok(WINDOW *win,bool bf);

Mit der Funktion clearok() wird beim nächsten refresh()-Aufruf (bf=TRUE) der komplette Bildschirm neu gezeichnet. Also nicht nur die letzte Änderung seit dem letzten refresh().

int scrollok(WINDOW *win,bool bf);

Falls bei der Funktion scrollok(), bf=TRUE ist, können Sie noch eine weitere Zeile scrollen auch wenn der Cursor schon in der letzten Zeile steht und durch ein Newline oder ein weiteres Zeichen in die nächste Zeile springt. Wenn Sie den Parameter bf auf FALSE setzten, bleibt der Cursor in der letzten Zeile stehen und der Bildschirminhalt bleibt unverändert. Dies in einem Beispiel zur Demonstration ...

#include <curses.h>

int main()
{
   int i;
   initscr();
   for(i=0;i<30; i++)
     {
       printw("Ohne Funktion scrollok()\n");
       halfdelay(2);
       getch();
     }
   clear();
   refresh();

   scrollok(stdscr,TRUE);
   for(i=0;i<30; i++)
     {
       printw("Mit Funktion scrollok()\n");
       halfdelay(2);
       getch();
     }
   getch();
   endwin();
   return 0;
}

Im ersten printw-Durchlauf wird die Ausgabe keinen Schritt tiefer gehen als bis in Zeile 25. Die restlichen fünf Zeilen werden einfach auf der gleichen Zeile rechts ausgegeben. Beim zweiten printw haben wir mit scrollok(stdsrc,TRUE), das scrollen in die nächste Zeile eingeschaltet und was bei der Ausgabe auch dazu führt, dass ab der Zeile 25 weiter etwas in Zeile 26,27,28.... ausgegeben wird.

Was curses als letzte Zeile betrachtet können Sie mit der Funktion ...

int setscrreg(int oben, int unten);

...beeinflussen. oben stellt die Zeile da, ab der gescrollt wird und unten stellt die letzte Zeile da. Die oberste Zeile hat in dem Programm dann den Wert 0. Wollen wir setscrreg() in dem Programm zuvor miteinbauen ...

#include <curses.h>

int main()
{
   int i;
   initscr();

   setscrreg(5,10);

   for(i=0;i<30; i++)
     {
       printw("Ohne Funktion scrollok()\n");
       halfdelay(2);
       getch();
     }
   clear();
   refresh();

   scrollok(stdscr,TRUE);
   for(i=0;i<30; i++)
     {
       printw("Mit Funktion scrollok()\n");
       halfdelay(2);
       getch();
     }
   getch();
   endwin();
   return 0;
}

Mit setscrreg(5,10) legen Sie die Bildschirmgröße auf 5 Zeilen fest. Die Bildschirmgröße beginnt in Zeile 5 und endet in Zeile 10.

void getyx(WINDOW *win, int y, int x);

Damit können Sie die Koordinaten der aktuellen Cursorpositionen abfragen. Die Koordinaten werden int y und x abgelegt. ACHTUNG: Es muss nicht der Adressoperator '&' an den beiden Koordinaten übergeben werden.

Hier nochmals das Programm mit der Funktion getyx() und noch ein bisschen verändert damit Sie die Funktionen setscrreg() und scrollok() ebenfalls besser verstehen ...

#include <curses.h>

int main()
{
   int i,y,x;
   initscr();

   setscrreg(0,10);

   for(i=0;i<30; i++)
     {
       getyx(stdscr,y,x);
       printw("%d. (%d/%d) Ohne Funktion scrollok()\n",i,y,x);
       halfdelay(2);
       getch();
     }
   clear();
   refresh();

   scrollok(stdscr,TRUE);
   for(i=0;i<30; i++)
     {
       getyx(stdscr,y,x);
       printw("%d. (%d/%d) Mit Funktion scrollok()\n",i,y,x);
       halfdelay(2);
       getch();
     }
   getch();
   endwin();
   return 0;
}

Weiter mit 10.4. Scrolling