Zum Inhalt springen

Ncurses: Druckversion

Aus Wikibooks
Ncurses



Wikibooks



Ncurses ist eine C-Bibliothek für die Steuerung von Textterminals. Hauptzweck dieser Bibliothek ist die Erstellung von TUIs (Text User Interfaces). Typische Beispiele für Programme, deren Benutzeroberflächen die ncurses-Bibliothek benutzen sind der Lynx-Browser (alternativ zur S-Lang-Bibliothek), der GNU Midnight Commander (alternativ zur S-Lang-Bibliothek), das Linux-Kernel-Konfigurationsprogramm in der ncurses-Variante oder das ncurses-Frontend des YaST-Installations- und -Konfigurationsprogramms bei der SuSE-Linux-Distribution.

Ncurses ist weitgehend kompatibel mit den SVR4-curses. Ncurses ist für diverse Unix-Plattformen erhältlich und steht unter der MIT-Lizenz.

Neben ncurses sind auch andere curses-Weiterentwicklungen erhältlich. Als Beispiel sei PDCurses genannt, welches für MS DOS, MS Windows, OS/2 und X11-Systeme verfügbar ist. Dieses Buch bezieht sich jedoch explizit auf ncurses, d.h. die erläuterten Grundlagen und Beispiele können, müssen jedoch nicht auf andere Curses-Bibliotheken gleichermaßen zutreffen.


Der Code wird mit einem beliebigen Texteditor als .c Datei gespeichert und dann mit:

gcc -lncurses <dateiname>.c -o <programmname>

compiliert. Vorher müssen natürlich auf dem Computer gcc und die ncurses Biblioteken installiert werden. Unter Debian GNU/Linux geht das z.B. sehr einfach mit

apt-get install build-essential libncurses5-dev

Dann kann man loslegen.



Installation

[Bearbeiten]

ncurses ist zumindest unter Linux (fast) immer Bestandteil der Distributionen und kann, wenn nicht bereits bei der initialen Linuxinstallation geschehen, nachträglich einfach vom Installationsmedium installiert werden. Eine ncurses-Downloadmöglichkeit wird im Kapitel Weblinks genannt.

Die Headerdateien

[Bearbeiten]

Die ncurses-Standard-Headerdateien sind

  • curses.h

oder

  • ncurses.h

ncurses.h ist meist nur ein symbolischer Link auf curses.h

Des Weiteren enthält ncurses für Spezialeinsatzfälle noch weitere Header-Dateien:

  • Panels: panel.h
  • Menüs: menu.h
  • Formulare: form.h

Die Bibliotheken

[Bearbeiten]

Die ncurses-Standardfunktionen sind in der Bibliothek

  • libncurses.xx

versammelt, wobei xx für so (shared object) oder a (statische Programmbibliothek) steht.

Äquivalent zu den Header-Dateien gibt es noch zusätzliche ncurses-Bibliotheken:

  • Panels: libpanel.xx
  • Menüs: libmenu.xx
  • Formulare: libform.xx

Die ncurses-man-Pages

[Bearbeiten]

Mit ncurses werden umfangreiche und ausführliche Manual-Seiten mitgeliefert.

Der Aufruf der ncurses-Übersichtsseite erfolgt mittels

man ncurses

oder

info ncurses

Auch die Beschreibungen der einzelnen ncurses-Funktionen lassen sich so abfragen, z.B.

man mvaddstr

oder

man 3ncurses mvaddstr

Fenster

[Bearbeiten]

Ein Fenster (Window) repräsentiert einen rechteckigen Bildschirmausschnitt. In einer WINDOW-Datenstruktur werden die notwendigen Attribute und Daten des entsprechenden Bildschirmausschnittes gespeichert. In curses.h findet sich dieses Konstrukt unter struct _win_st. WINDOW ist nur ein Synonym für _win_st (typedef struct _win_st WINDOW).

Bildschirme

[Bearbeiten]

Bildschirme (Screens) sind Zeiger auf eine WINDOW-Datenstruktur.

ncurses kennt beim Start zwei Screens:

  • Standardbildschirm (standard screen): stdscr
  • Aktueller Bildschirm (current screen): curscr

Der stdscr ist das Hauptfenster, welches vom Programmierer mittels ncurses-Anweisungen beeinflusst werden kann. Der curscr repräsentiert das, was momentan am Bildschirm angezeigt wird. Bei einem Refresh werden in diesen Screen die vorgenommenen Änderungen eingebracht und die Darstellung am physikalischen Bildschirm aktualisiert.

ncurses und I/O-Befehle von C

[Bearbeiten]

Befindet sich ein Programm im ncurses-Modus, so dürfen (oder sollen, können) die C-Standardfunktionen nicht zur Ein- und Ausgabe (z.B. printf, scanf) verwendet werden. Zu diesem Zweck stellt ncurses eigene Funktionen zur Verfügung.

Termcap und Terminfo

[Bearbeiten]

termcap steht für Terminal Capabilities und ist eine Datei, in der die Fähigkeiten (Zeilen-, Spaltenanzahl, ...) zahlreicher Terminals hinterlegt sind.

terminfo leistet prinzipiell das gleiche wie termcap. Allerdings liegen in der terminfo-Datenbank die Terminal-Fähigkeitsbeschreibungen nicht in einer einzigen Gesamtdatei vor, sondern als separate Dateien alphabetisch geordnet in Unterverzeichnissen.

Bei der ncurses-Initialisierung wird mittels der Environmentvariablen TERM die termcap/terminfo ausgewertet. Daraus kann ncurses die relevanten Daten des verwendeten Terminals ermitteln (z.B. Anzahl der Zeilen und Spalten).

Ncurses initialisieren und beenden

[Bearbeiten]

Die ncurses-Initialisierung geschieht mittels der Funktion

WINDOW *initscr (void);

initscr wird in den meisten Fällen die erste ncurses-Funktion sein, die in einem Programm aufgerufen wird.

Beendet wird der ncurses-Modus mit der Funktion

int endwin (void);

Ein ncurses-Programm soll unbedingt ordentlich mit der endwin-Funktion beendet werden.

Der ncurses-Modus kann durch endwin auch zeitweilig unterbrochen werden. Die Rückkehr zum ncurses-Modus erfolgt durch eine Refresh-Anweisung.

Mit der Funktion bool isendwin() kann abgefragt werden, ob der ncurses-Modus noch aktiv ist.

bool ist übrigens ein Boolean-Datentyp den ncurses mitbringt. Zulässige Werte sind TRUE (= 1) und FALSE (= 0). Dass C++-Compiler oder andere Bibliotheken ebenfalls einen bool-Datentyp kennen, wird in der ncurses-Headerdatei berücksichtigt.

Das ncurses-Hauptkoordinatensystem

[Bearbeiten]


Zwecks Abfrage von Fenster- und Cursorkoordinaten stehen folgende Makros zur Verfügung:

getyx(win, y, x);     // Koordinaten der aktuellen Cursorposition
getparyx(win, y, x);  // Koordinatenursprung von win bezogen auf das 
                      // übergeordnete Fenster
getbegyx(win, y, x);  // Koordinatenursprung
getmaxyx(win, y, x);  // Fenstergröße (Anzahl der Zeilen und Spalten)

Wobei win ein Zeiger auf das abzufragende WINDOW ist und die Koordinatenwerte als Integerwerte in y (Zeile) und x (Spalte) geliefert werden.

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h> //noetig fuer atexit()

void quit()
{
  endwin();
}

int main(void)
{
  int x, y;

  initscr();
  atexit(quit);
  curs_set(0);
  
  mvprintw(3, 5, "LINES: %d", LINES);
  mvprintw(4, 5, "COLS:  %d", COLS);

  getyx(stdscr, y, x);
  mvprintw(5, 5, "Momentane Cursorposition:  [%d, %d]", y, x);

  getbegyx(stdscr, y, x);
  mvprintw(6, 5, "Koordinatenursprung:       [%d, %d]", y, x);

  getmaxyx(stdscr, y, x);
  mvprintw(7, 5, "Fenstergröße:              [%d, %d]", y, x);

  mvaddstr(11, 2, "Taste drücken -> Ende");
  refresh();

  getch();
  return(0);
}

Die Bedeutung der einzelnen Bildschirmausgabefunktionen wird später näher beschrieben. Das Compilieren und Linken des Beispieles kann mit folgendem Kommando geschehen

gcc -o bsp bsp.c -lncurses

Voraussetzungen für das erfolgreiche Compilieren/Linken dieses Beispiels in der vorgeschlagenen Art und Weise:

  • Das Beispiel wurde unter dem Dateinamen bsp.c abgespeichert und der Compileraufruf erfolgt aus dem selben Verzeichnis in dem bsp.c gespeichert wurde.
  • Sie verwenden das C-Frontend der GNU Compiler Collection (GCC). Diese GCC muss für das Compilieren/Linken des Beispiels auf ihrem System installiert sein. Die Verwendung eines anderen Compilers wird einen etwas anderen Compileraufruf erfordern.
  • Sie geben das Kommando direkt nach dem Prompt in ein Terminal ein. Für andere Arten der Programmerstellung, z.B. via IDE, sei hier nur der allgemeine Hinweis "die Einbindung der ncurses-Bibliothek nicht vergessen" gegeben.

Das ausführbare Programm sollte nach erfolgreichem Compilerlauf unter dem Dateinamen bsp vorhanden sein. Der Programmstart kann mit

./bsp

direkt aus dem Speicherverzeichnis des Beispielprogramms erfolgen. Das Ergebnis sollte folgendem Screenshot ähneln.

Die konkret angezeigten Zahlenwerte hängen natürlich vom verwendeten Terminal ab.

Cursor

[Bearbeiten]

Zwecks Positionierung des Cursors steht die Funktion

int move(int y, int x);

zur Verfügung. Der Cursor bestimmt, an welcher Position die Textausgabe oder ein Echo erfolgt. ncurses-Text-I/O-Funktionen existieren meist zusätzlich als Variante mit einem mv-Präfix, sodass Cursorpositionierung und Daten-I/O in einem Rutsch erledigt werden können und nicht jeweils zwei Funktionen nacheinander angeschrieben werden müssen.

Die Cursoranzeige lässt sich mit der Funktion

int curs_set(int visibility);

modifizieren. Für visibility sind folgende Werte möglich:

  • 0 ... unsichtbar
  • 1 ... sichtbar
  • 2 ... "besonders" sichtbar.

Textausgabe

[Bearbeiten]

Die ncurses-Bibliothek kennt, ihrem Einsatzzweck entsprechend, eine Vielzahl von Textausgabefunktionen.

Hinzufügen von Einzelzeichen

[Bearbeiten]
int addch(const chtype ch);
int mvaddch(int y, int x, const chtype ch);
int echochar(const chtype ch);

echochar entspricht einem addch mit nachfolgendem refresh.

Einfügen eines Zeichens

[Bearbeiten]
int insch(chtype ch);
int mvinsch(int y, int x, chtype ch);

Beispiel: Unterschied zwischen addch() und insch()

[Bearbeiten]
#include <curses.h>
#include <stdlib.h> //noetig fuer atexit()

void quit()
{
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  curs_set(0);

  mvaddstr(3, 2, "Der Unterschied zwischen addch() und insch():");

  mvaddstr(5, 5, "ADDCH: Hallo, Welt");
  mvaddch(5, 13, 'A');

  mvaddstr(6, 5, "INSCH: Hallo, Welt");
  mvinsch(6, 13, 'A');    
  refresh();

  getch();
  return(0);
}

Hinzufügen einer Zeichenkette

[Bearbeiten]
int addstr(const char *str);
int addnstr(const char *str, int n);
int mvaddstr(int y, int x, const char *str);
int mvaddnstr(int y, int x, const char *str, int n);

Einfügen einer Zeichenkette

[Bearbeiten]
int insstr(const char *str);
int insnstr(const char *str, int n);
int mvinsstr(int y, int x, const char *str);
int mvinsnstr(int y, int x, const char *str, int n);

Formatierte Ausgabe

[Bearbeiten]
int printw(const char *fmt, ...);
int mvprintw(int y, int x, const char *fmt, ...);
int vwprintw(WINDOW  *win,  const char *fmt, va_list var glist);

Die Parameter, insbesondere fmt, von printw entsprechen der printf-Funktion in stdio.h. Nachfolgend sind auszugsweise einige Konvertierungszeichen zwecks Verwendung in fmt gegeben

%d, %i vorzeichenbehaftete Ganzzahl (int)
%o vorzeichenlose Ganzahl im Oktalformat
%u vorzeichenlose Ganzzahl (unsigned int)
%x vorzeichenlose Ganzahl im Hexadezimalformat
%f, %F Gleitkommazahl (double)
%e, %E Gleitkommazahl (double) in Exponentialdarstellung
%a, %A Gleitkommazahl (double) in Hexadezimaldarstellung
%s Zeichenkette (const char *)
%c Ein Zeichen (char)

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h> //noetig fuer atexit()

void quit()
{
  endwin();
}

int main(void)
{
  const int    i    = 23456;
  const double f    = -12345e-3;

  initscr();
  atexit(quit);
  curs_set(0);

  mvprintw(3, 2, "Eine Ganzzahl in Oktal- und Hexadezimaldarstellung: %o | %x\n", i, i);
  mvprintw(4, 2, "Eine Ganzzahl mit führenden Nullen: %010d\n", i);
  mvprintw(5, 2, "Eine Gleitkommazahl: %8.3f\n", f);
  mvprintw(6, 2, "Eine Gleitkommazahl: %8.3e\n", f);
  mvprintw(7, 2, "Eine Gleitkommazahl in Hexadezimaldarstellung: %a\n", f);

  refresh();

  getch();
  return(0);
}

Texteingabe

[Bearbeiten]

Eine Benutzeroberfläche ohne die Möglichkeit von Benutzereingaben ist in den meisten Fällen relativ nutzlos. Die ncurses-Bibliothek stellt eine Reihe von Funktionen für die Dateneingabe mittels Tastatur zur Verfügung.

Eingabe eines Zeichens

[Bearbeiten]
int getch(void);
int mvgetch(int y, int x);
int ungetch(int ch);
int wgetch(WINDOW *win);

getch entspricht in etwa der getchar-Funktion aus stdio.h. ungetch schreibt das Zeichen ch zurück in die Eingabe-Queue. wgetch ist eine Funktion für die Zeicheneingabe in eigens festgegelegten Fenstern. Die Programmierung von Fenstern wird aber erst in einem der folgenden Kapitel näher beschrieben.

Die Interpretation eines Return-Tastendrucks ist nicht einheitlich geregelt. Ncurses bietet zu diesem Zwecke zwei unterschiedliche Funktionen.

int nl(void);
int nonl(void);

Bei der Verwendung von nl() wird ein Return-Tastendruck (Zeilenendezeichen) als 0xA (10dec, LF, Line Feed) interpretiert. Bei der Verwendung von nonl() entspricht ein Return-Tastendruck nur einem 0xD (13dec, CR, Carriage Return).

Eingabe einer Zeichenkette

[Bearbeiten]
getstr(char *str);
getnstr(char *str, int n);
mvgetstr(int y, int x, char *str);
mvgetnstr(int y, int x, char *str, int n);

Formatierte Eingabe

[Bearbeiten]
int scanw(char *fmt, ...);
int mvscanw(int y, int x, char *fmt, ...);
int vwscanw(WINDOW *win, char *fmt, va_list varglist);

scanw ist vergleichbar mit dem konventionellen scanf.

Löschaktionen

[Bearbeiten]

Ab und zu wird auch eine "tabula rasa"-Aktion fällig.

int clear(void);
int clrtobot(void);
int clrtoeol(void);

clear löscht den gesamten Standardscreen. clrtobot löscht die Inhalte von der aktuellen Cursorposition bis zum Ende des Screens. clrtoeol löscht die Inhalte von der aktuellen Cursorposition bis zum Zeilenende.

Die Funktion

int deleteln(void);

löscht die gesamte aktuelle Zeile. Alle darauffolgenden Zeilen werden um eine Zeile nach oben verschoben. Andererseits fügt die Funktion

int insertln(void);

eine Leerzeile an der aktuellen Cursorposition ein. Alle folgenden Zeilen werden um eine Zeile nach unten verschoben.

Refresh

[Bearbeiten]

Damit die Änderungen im stdscr in den curscr übernommen werden und somit der Bildschirm aktualisiert wird, ist folgende Anweisung vorhanden:

int refresh(void);

Scrolling

[Bearbeiten]
int scroll(WINDOW *win);
int scrl(int n);

scroll verschiebt den Fensterinhalt um eine Zeile nach oben. scrl verschiebt den Fensterinhalt von stdscr um n Zeilen nach oben.

Damit Scrolling funktioniert, muss vorab folgende Funktion aufgerufen werden.

int scrollok(WINDOW *win, bool bf);

Der Parameter bf muss für die Aktivierung der Scrollfähigkeit natürlich auf TRUE gesetzt werden.

Echo ein-/ausschalten

[Bearbeiten]

Die Anzeige der Eingabedaten (das Echo) kann mittels der Funktion

noecho()

unterdrückt werden.

echo()

schaltet diese Anzeige wieder ein. Dies ist das Standardverhalten von ncurses.

Pieps und Blitz

[Bearbeiten]

Manchmal kann auch ein kurzes Warnsignal an den Benutzer sinnvoll sein. Ncurses bietet zu diesem Zweck zwei standardmäßig vorhandene Funktionen:

int beep(void);
int flash(void);

beep() soll einen kurzen akustischen Ton und flash() einen kurzen optischen "Flash" erzeugen. Das Ganze ist aber stark abhängig von den Fähigkeiten und Einstellungen des verwendeten Terminals. Besitzt das verwendete Terminal weder Audio- noch Flashfähigkeiten, dann erfolgt weder eine akustische noch eine visuelle Warnung. Teilweise können diese Signale vom Benutzer auch deaktiviert werden, so z.B. bei der KDE-Konsole. Der Gebrauch dieser Funktionen ist deshalb nur von eingeschränktem Nutzen.

gcc -lcurses

[Bearbeiten]

Falls ncurses nicht funktioniert, dann sollte man die Compileroption -lcurses versuchen. Beispiel gcc main.c -o demo -lcurses



Damit ncurses-Programme in ihrer ganzen Farbenpracht erstrahlen können, muss im Programmcode die Funktion

int start_color(void);

initial aufgerufen werden.

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

void quit(void)
{
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  start_color();
  clear();

  mvaddstr(5, 5, "Hallo");
  mvaddstr(6, 10, "Welt!");
  mvaddstr(15, 1, "Programm beenden durch Drücken einer Taste"); 
  refresh();
  getch(); 
  return(0);
}


Das Rechteck rechts-unten auf diesem und den folgenden Screenshots stellt übrigens den Cursor dar. Die Anzeige des Cursors kann mit der curs_set-Funktion ein-/ausgeschaltet werden.

Farben wählen

[Bearbeiten]
int init_pair(short pair, short f, short b);

Parameter:

  • pair: Paarnummer; 1 <= pair < COLOR_PAIRS
  • f, b: foreground-color, background-color; 1 <= (f bzw. b) < COLOR

Diese Funktion ist nur dann sinnvoll einsetzbar, wenn das Terminal Farben unterstützt, was aber auch häufig der Fall ist. Zwecks Abfrage der Farbfähigkeit gibt es die Funktion

bool has_colors(void);

Basisfarben

[Bearbeiten]

Nach der Initalisierung mittels start_color sind bei farbfähigen Terminals unmittelbar die acht ncurses-Basisfarben verwendbar:

COLOR_BLACK = 0
COLOR_RED = 1
COLOR_GREEN = 2
COLOR_YELLOW = 3
COLOR_BLUE = 4
COLOR_MAGENTA = 5
COLOR_CYAN = 6
COLOR_WHITE = 7

Textvorder- und -hintergrundfarbe

[Bearbeiten]
int color_set(short color_pair_number, void* opts);

Farben werden immer paarweise (Vorder-, Hintergrundfarbe) gesetzt (init_pair). Der Parameter opts ist ein Null-Pointer (0).

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

void quit(void)
{
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  start_color();
  clear();

  init_pair(1, COLOR_GREEN, COLOR_RED);
  color_set(1, 0);

  mvaddstr(5, 5, "Hallo");
  mvaddstr(6, 10, "Welt!");
  mvaddstr(15, 1, "Programm beenden durch Drücken einer Taste"); 
  refresh();
  getch(); 
  return(0);
}

Fensterhintergrund

[Bearbeiten]
int bkgd(chtype ch);

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

void quit(void)
{
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  start_color();
  clear();

  init_pair(1, COLOR_GREEN, COLOR_RED); 
  bkgd(COLOR_PAIR(1));

  mvaddstr(5, 5, "Hallo");
  mvaddstr(6, 10, "Welt!");
  mvaddstr(15, 1, "Programm beenden durch Drücken einer Taste"); 
  refresh();
  getch(); 
  return(0);
}

Zusätzliche Textattribute

[Bearbeiten]

Zusätzliche Textattribute lassen sich mit den Funktionen

int attrset(int attrs); // setzt Attribute für nachfolgende Texte
int attron(int attrs);  // schaltet zusätzliche Attribute für nachfolgende Texte ein
int attroff(int attrs); // schaltet die angegebenen Attribute wieder aus
int standend(void);     // attrset(0)
int standout(void);     // attrset(A_STANDOUT)

einstellen. Einzelattribute lassen sich mittels der OR-Bitoperation ( | ) verknüpfen. Als Attribute stehen zur Verfügung:

A_NORMAL normal
A_STANDOUT Highlight-Modus
A_UNDERLINE unterstrichen
A_REVERSE revertiert
A_BLINK blinkend
A_DIM gedimmt
A_BOLD fett
A_PROTECT geschützt
A_INVIS unsichtbar
A_ALTCHARSET alternatives Character-Set

Die genauen Auswirkungen dieser Attribute sind teilweise abhängig von den Fähigkeiten des eingesetzten Terminals.

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

void quit(void)
{
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  start_color();
  clear();
 
  init_pair(1, COLOR_YELLOW, COLOR_BLUE); 
  init_pair(2, COLOR_GREEN, COLOR_BLUE); 
  bkgd(COLOR_PAIR(1));

  attrset(A_UNDERLINE);
  mvaddstr(5, 5, „Hallo“);
  attrset(A_BOLD);
  mvaddstr(6, 10, „Welt!“);
  attrset(A_DIM | COLOR_PAIR(2));
  mvaddstr(15, 1, "Programm beenden durch Drücken einer Taste");  
 
  refresh();
  getch(); 
  return(0);
}
KDE-Konsole (Farbschema: Konsole-Standard)
KDE-Konsole (Farbschema: XTerm-Farben)
rxvt, aterm, xterm


Farben ändern

[Bearbeiten]
int init_color(short color, short r, short g, short b);

Parameter:

  • color: Farb-Nummer bzw. -Name (COLOR_BLACK, etc.) der Basisfarbe, die geändert werden soll
  • r, g, b: ... RGB; 0 <= (r, g bzw. b) <= 1000

Diese Funktion ist nur dann sinnvoll einsetzbar, wenn das Terminal Farbänderungen unterstützt, was nicht immer der Fall ist (Software Terminals können für diesen Zweck z.b. in den Modus "xterm-256color" geschalten werden). Je nach Terminal wird nur ein kleinerer Wertebereich unterstützt, z.B.: 0 <= (r, g bzw. b) <= 999 . Zwecks Abfrage der Fähigkeit zur Farbänderung gibt es die Funktion

 bool can_change_color(void);

Da die Funktion nur sehr selten sinnvoll angewendet werden kann, wird hier auf ein Beispiel verzichtet.




Damit Spezialtasten (z.B. Pfeiltasten, Funktionstasten, Backspace-Taste) korrekt angesprochen werden, müssen mittels

int keypad(WINDOW *win, bool bf);

deren ncurses-Escapesequenzen aktiviert werden.

Nachfolgend die Bezeichnung einiger häufig benötigter Spezialtasten. Zwecks vollständiger Auflistung wird auf die ncurses-Header-Datei curses.h hingewiesen:

KEY_DOWN
KEY_UP
KEY_LEFT
KEY_RIGHT
KEY_BACKSPACE
KEY_F(1), ... Funktionstaste F1, ...
KEY_END
KEY_PRINT

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

void quit(void)
{
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, TRUE);

  mvaddstr(5, 5, "Hallo");
  mvaddstr(6, 10, "Welt!");
  mvaddstr(10, 1, "Programm beenden durch Drücken der Taste F1");
  refresh();

  while(getch() != KEY_F(1))
  {
  }

  return(0);
}


Damit dieses Beispiel ordnungsgemäß funktioniert, muss die Zeilenpufferung und teilweise Einzelzeichenbearbeitung für das Textterminal ausgeschaltet sein. Sicherheitshalber sollte dieser "cbreak"-Modus explizit aktiviert werden, denn es ist nicht garantiert, dass sich das Terminal automatisch in diesem Modus befindet. Testweise kann in diesem Beispiel die cbreak-Funktion durch die nocbreak-Funktion ersetzt werden. Das Programm lässt sich dann nicht mehr durch Drücken der F1-Funktionstaste beenden. nocbreak() versetzt das Terminal wieder in den sogenannten "cooked"-Modus. Zusätzlich kann das Terminal auch noch mittel raw() in den "raw"-Modus versetzt werden. Dieser unterscheidet sich vom "cbreak"-Modus dadurch, dass die Einzelzeichenbearbeitung komplett ausgeschaltet wird, dem Prozess werden alle Zeichen unverarbeitet zur Verfügung gestellt. Im "cbreak"-Modus ließe sich das Programm notfalls auch noch durch die Tastenkombination CRTL-C beenden. Im "raw"-Modus würde dieses Zeichen keinen Interrupt mehr auslösen, sondern wie jedes normale Zeichen an das Programm weitergeleitet werden.




Bisher wurde bei den Programmen nur der Standardscreen stdscr als Fenster verwendet. Mit ncurses können aber auch eigene Fenster erzeugt und manipuliert werden. Die Funktionen für Fenster sind sehr ähnlich wie die bisher verwendeten. Allerdings sind die speziellen Fenster-Befehle durch den zusätzlichen Buchstaben w markiert, z.B.:

int wrefresh(WINDOW *win);
int wscanw(WINDOW *win, char *fmt, ...);
int mvwaddstr(WINDOW *win, int y, int x, const char *str);

Tatsächlich ist es sogar so, dass die bisher verwendeten Standardscreen-Funktionen nur als Makros definiert sind, z.B.:

#define addch(ch)  waddch(stdscr,ch)
#define attron(at) wattron(stdscr,at)
#define bkgd(ch)   wbkgd(stdscr,ch)
#define clear()    wclear(stdscr)

Zum Erzeugen von Fenstern gibt es mehrere Möglichkeiten:

  • Neue Fenster erzeugen: newwin
  • Abgeleitete Fenster erzeugen: subwin, derwin
  • Fenster duplizieren: dupwin

Neue Fenster

[Bearbeiten]
WINDOW *newwin(int nlines, int ncols, int begin_y, int begin_x);

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

WINDOW *win;

void quit(void)
{
  delwin(win);
  endwin();
}

int main(void)
{

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, 1);

  start_color();
  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  init_pair(2, COLOR_BLUE, COLOR_WHITE);

  win = newwin(5, 20, 10, 10);
  
  bkgd(COLOR_PAIR(1));
  wbkgd(win, COLOR_PAIR(2));

  mvaddstr(5,5, "Hallo stdscr");
  mvwaddstr(win, 3, 3, "Hallo win");
  mvwaddstr(win, 7, 3, "Diese Zeichenkette wird nicht angezeigt!"); 
                     // da ausserhalb des win-Anzeigebereichs

  refresh();
  wrefresh(win);
 
  while(getch() != KEY_F(1))
  {
  } 

  return(0);
}


Abgeleitete Fenster

[Bearbeiten]

Ein subwin (untergeordnetes Fenster) erbt Eigenschaften vom übergeordneten Fenster.

WINDOW *subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x);

Beispiel: Aufzeigen des Unterschieds von newwin und subwin

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

WINDOW *win1, *win2;

void quit(void)
{
  delwin(win1);
  delwin(win2);
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, 1); 

  start_color();
  init_pair(1, COLOR_YELLOW, COLOR_BLUE);

  win1 = newwin(5, 20, 10, 10);
  win2 = subwin(stdscr, 5, 20, 10, 35);
  
  bkgd(COLOR_PAIR(1));

  mvaddstr(5,5, "Hallo stdscr");
  mvwaddstr(win1, 3, 3, "Hallo newwin");
  mvwaddstr(win2, 3, 3, "Hallo subwin");
 
  refresh();
  wrefresh(win1);
  wrefresh(win2);

  while(getch() != KEY_F(1))
  {
  } 

  return(0);
}


derwin ist im Prinzip das selbe wie subwin. Während die Position (begin_y, begin_x) des Fensters bei der Funktion subwin aber relativ zum Screen festgelegt wird, ist die Position bei derwin relativ zum orig-Fenster.

Fensterverzierungen

[Bearbeiten]

Fenster können auch mit Rahmen oder Begrenzungslinien versehen werden. Diese Verzierungen werden mittels Einzelzeichen aufgebaut. Diese Zeichen können gewöhnliche Buchstaben oder Zahlen sein. Schöner wird das Ganze aber wenn spezielle Zeichen verwendet werden. ncurses kennt "form characters" (ACS, Alternative Character Set), die sich für dieses Aufgabengebiet anbieten. Zu beachten ist, dass Rahmen und Linien wie normaler Text geschrieben werden. Aus diesem Grund können diese Rahmen durch unvorsichtig platzierten Text überschrieben werden. Das bringt zwar keine funktionellen Nachteile, sieht aber nicht schön aus. Dies sollte bei der Verwendung von Rahmen und Linien beachtet werden.

Rahmen

[Bearbeiten]
int border(chtype ls, chtype rs, chtype ts, chtype bs, chtype tl, chtype tr, 
           chtype bl, chtype br);
int wborder(WINDOW *win, chtype ls, chtype rs, chtype ts, chtype bs, chtype tl, 
            chtype tr, chtype bl, chtype br);
int box(WINDOW *win, chtype verch, chtype horch);

Erklärung der Parameterbezeichnungen:

  • ver ... vertikal
  • hor ... horizontal
  • l ... left
  • r ... right
  • b ... bottom
  • t ... top
  • s ... side

Z.B. bedeuten

  • ls ... left side, Kante links
  • tr ... top right, Ecke oben-rechts

Wird 0 übergeben, so wird jeweils das in der Bibliothek festgelegte Standardzeichen verwendet.

Linien

[Bearbeiten]
int hline(chtype ch, int n);
int vline(chtype ch, int n);
int whline(WINDOW *win, chtype ch, int n);
int wvline(WINDOW *win, chtype ch, int n);
int mvhline(int y, int x, chtype ch, int n);
int mvvline(int y, int x, chtype ch, int n);
int mvwhline(WINDOW *, int y, int x, chtype ch, int n);
int mvwvline(WINDOW *, int y, int x, chtype ch, int n);

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

WINDOW *win;

void quit(void)
{
  delwin(win);
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, 1);

  start_color();
  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  init_pair(2, COLOR_BLUE, COLOR_WHITE);

  win = newwin(5, 20, 10, 10);
  
  bkgd(COLOR_PAIR(1));
  wbkgd(win, COLOR_PAIR(2));

  mvaddstr(5,5, "Hallo stdscr");
  mvwaddstr(win, 3, 3, "Hallo win");
  mvwaddstr(win, 7, 3, "Diese Zeichenkette wird nicht angezeigt!"); 
                    // da ausserhalb des win-Anzeigebereichs

  box(win, 0, 0);
  mvhline(7, 1, ACS_BULLET, 20);

  refresh();
  wrefresh(win);
 
  while(getch() != KEY_F(1))
  {
  } 

  return(0);
}

ACS (Liniengrafik, form characters)

[Bearbeiten]

Obige ACS-Bilder wurden als Screenshoots dieses Programmes erstellt (KDE-Konsole, xterm)

#include <curses.h>
#include <stdlib.h>

void write_pages(void)
{ 
  chtype acs_symbol[] = {
    ACS_ULCORNER, ACS_LLCORNER, ACS_URCORNER,
    ACS_LRCORNER, ACS_LTEE,     ACS_RTEE,    
    ACS_BTEE,     ACS_TTEE,     ACS_HLINE,   
    ACS_VLINE,    ACS_PLUS,     ACS_S1,      
    ACS_S9,       ACS_DIAMOND,  ACS_CKBOARD, 
    ACS_DEGREE,   ACS_PLMINUS,  ACS_BULLET,  
    ACS_LARROW,   ACS_RARROW,   ACS_DARROW,  
    ACS_UARROW,   ACS_BOARD,    ACS_LANTERN, 
    ACS_BLOCK,    ACS_S3,       ACS_S7,      
    ACS_LEQUAL,   ACS_GEQUAL,   ACS_PI,
    ACS_NEQUAL,   ACS_STERLING
  };

  char acs_name[][20] = {
    "ACS_ULCORNER", "ACS_LLCORNER", "ACS_URCORNER",
    "ACS_LRCORNER", "ACS_LTEE",     "ACS_RTEE",    
    "ACS_BTEE",     "ACS_TTEE",     "ACS_HLINE",   
    "ACS_VLINE",    "ACS_PLUS",     "ACS_S1",      
    "ACS_S9",       "ACS_DIAMOND",  "ACS_CKBOARD", 
    "ACS_DEGREE",   "ACS_PLMINUS",  "ACS_BULLET",  
    "ACS_LARROW",   "ACS_RARROW",   "ACS_DARROW",  
    "ACS_UARROW",   "ACS_BOARD",    "ACS_LANTERN", 
    "ACS_BLOCK",    "ACS_S3",       "ACS_S7",      
    "ACS_LEQUAL",   "ACS_GEQUAL",   "ACS_PI",
    "ACS_NEQUAL",   "ACS_STERLING"
  };
 
  int rows = 5, page=0, i, j, flag=0;
  int acs_nr = sizeof(acs_symbol) / sizeof(chtype);

 
  for(j=0; j<=acs_nr/rows; j++)
  {
    clear ();

    for(i=0; i<rows; i++)
    {
      if(page*rows+i <= acs_nr-1)
      { 
        mvaddch(i*2+1, 3, acs_symbol[page*rows + i]);
        mvaddstr(i*2+1, 8, acs_name[page*rows + i]);
      }
      else 
      {
        flag=1;
      }
    } 
  
    if(!flag)
    {
      mvaddstr(rows*2 +2, 1, "Taste drücken -> nächste Seite");
    }
    else
    {
      mvaddstr(rows*2 +2, 1, "Taste drücken -> Ende");
    }

    refresh();
    page++;
    getch();
  } 
}

void quit(void)
{
  endwin();
}

int main(void)
{
  initscr();
  atexit(quit);
  noecho();
  curs_set(0);
  
  write_pages();

  return(0);
}

Fenster löschen

[Bearbeiten]

Der durch ein Fenster belegte Speicherplatz kann über die Funktion

int delwin(WINDOW *win);

wieder freigegeben werden. Auf die Bildschirmdarstellung hat das vorerst keinen Einfluss. Natürlich sollte danach nicht mehr auf das gelöschte Fenster zugegriffen werden, da dies in aller Regel einen Programmabsturz infolge "Speicherzugriffsfehler" auslöst.

Fenster refreshen

[Bearbeiten]

Zum Refreshen eines Fensters sind diese Funktionen vorgesehen:

int wrefresh(WINDOW *win);
int wnoutrefresh(WINDOW *win);
int doupdate(void);

Welche Funktion soll wann Verwendung finden?

Die einfachste Möglichkeit ist der Aufruf von wrefresh. Diese Funktion bringt den gewünschten Fensterinhalt auf den real existierenden Bildschirm. wrefresh besteht im Prinzip aus der sequentiellen Abfolge der Funktionen:

  1. wnoutrefresh ... kopiert den gewünschten Fensterinhalt in den virtuellen Bildschirmspeicher.
  2. doupdate ... gleicht virtuellen Bildschirmspeicher mit dem realen Bildschirminhalt ab und vollzieht das Update.

Sind viele Fenster gleichzeitig zu refreshen, dann ist die wrefresh-Funktion ineffizient. In diesem Fall ist es besser, zuerst alle Fenster mit einem wnoutrefresh zu aktualisieren und am Ende nur einmal die doupdate-Funktion aufzurufen.

Touch und Untouch

[Bearbeiten]

Wird der Fensterinhalt geändert, dann wird das Fenster automatisch als "touched" (berührt) markiert. Die refresh-Funktion erkennt daran, dass das Fenster aktualisiert werden muss. Als "untouched" markierte Fenster werden bei Refreshs nicht aktualisiert, da aus Performancegründen virtueller und physikalischer Screen abgeglichen und nur die Änderungen übertragen werden. Ein Fenster kann auch manuell wieder als "untouched" markiert werden.

int touchwin(WINDOW *win);
int untouchwin(WINDOW *win);
bool is_wintouched(WINDOW *win);

Beispiel

[Bearbeiten]
// ...
mvwaddstr(win, 4, 3, "Beenden -> F1 !"); 
wrefresh(win);
// ... 
Die Zeichenkette wird wie erwartet in das Fenster eingefügt
// ...
mvwaddstr(win, 4, 3, "Beenden -> F1 !"); 
untouchwin(win);
wrefresh(win);
// ... 
untouchwin - der nachfolgende Refresh bewirkt keine Änderung des ursprünglichen Fensterinhaltes
// ...
mvwaddstr(win, 4, 3, "Beenden -> F1 !"); 
untouchwin(win);
wrefresh(win);
touchwin(win);
wrefresh(win);
// ... 
Durch Einfügung der touchwin-Anweisung wird dieses Fenster bei einem Refresh wieder aktualisiert



Ncurses-Fenster sind gut verwendbar, solange sie nebeneinander platziert sind. Überlappen sich Fenster jedoch, dann kann es kompliziert werden. Aus diesem Grund kennt ncurses sogenannte Panels. Diese bieten Mechanismen, Fenster auch in z-Richtung zu verwalten. Panels gehören zwar zu ncurses, sind aber in eine eigene Bibliotheksdatei ausgelagert.


Ein neues Panel erzeugen:

PANEL *new_panel(WINDOW *win);

Reservierten Panel- Speicherplatz freigeben:

int del_panel(PANEL *pan);

Panelanzeige manipulieren:

int bottom_panel(PANEL *pan); // Panel nach hinten verschieben
int top_panel(PANEL *pan);    // Panel in den Vordergrund holen
int show_panel(PANEL *pan);   // Panel anzeigen
int hide_panel(PANEL *pan);   // Panel verstecken

Vorne-, hintenliegendes Panel eruieren (pan=0: top bzw. bottom):

PANEL *panel_above(const PANEL *pan)
PANEL *panel_below(const PANEL *pan)

Panels updaten:

void update_panels();

Beispiel

[Bearbeiten]
#include <panel.h>
#include <stdlib.h>

WINDOW *win1, *win2;
PANEL  *pan1, *pan2;

void quit(void)
{
  del_panel(pan1);
  del_panel(pan2);
  delwin(win1);
  delwin(win2);
  endwin();
}

int main(void)
{
  int flag=0;

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, 1);

  start_color();
  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  init_pair(2, COLOR_BLACK, COLOR_WHITE);
  init_pair(3, COLOR_BLACK, COLOR_YELLOW);

  win1 = newwin(10, 25, 5, 10);
  win2 = newwin(10, 25, 10, 15);
  box(win1, ACS_VLINE, ACS_HLINE);
  box(win2, ACS_VLINE, ACS_HLINE);
  pan1 = new_panel(win1);
  pan2 = new_panel(win2);
  
  bkgd(COLOR_PAIR(1));
  wbkgd(win1, COLOR_PAIR(2));
  wbkgd(win2, COLOR_PAIR(3));

  mvaddstr(2,4, "F9 beendet das Programm");
  mvwaddstr(win1, 2, 3, "Drücke eine Taste");
  mvwaddstr(win2, 7, 3, "Drücke eine Taste");

  update_panels();
  doupdate();

  while(getch() != KEY_F(9))
  {
    if (flag==0)
    {
      top_panel(pan1);
      flag = 1; 
    }
    else
    {
      top_panel(pan2);
      flag = 0;
    }

    update_panels();
    doupdate();
  } 

  return (0);  
}

Compilieren, Linken:

gcc -o bsp bsp.c -lpanel -lncurses




Ein ncurses-Menü ist ein Menü in der ursprünglichen Bedeutung, also eine einfache Auswahlbox. Allerdings bietet auch die menu-Bibliothek genügend Möglichkeiten um diese Auswahlboxen sehr schön und sinnvoll auszugestalten.

Ein Menü erzeugen und wieder löschen

[Bearbeiten]

Die Erzeugung eines Menüs untergliedert sich in folgende Schritte:

  • Speicherplatz für die Items (Menüeinträge) reservieren: calloc-Funktion
  • Items erzeugen: ITEM *new_item(const char *name, const char *description);
  • Menü erzeugen: MENU *new_menu(ITEM **items);
  • Menü "posten" (post = anheften, ankleben -> "PostIt") : int post_menu(MENU *menu);

Das "Abbauen" eines Menüs geschieht in umgekehrter Reihenfolge:

  • Menü "unposten": int unpost_menu(MENU *menu);
  • Menü freigeben: int free_menu(MENU *menu);
  • Items freigeben: int free_item(ITEM *item);
  • Reservierten Items-Speicherplatz freigeben: free-Funktion

Der Menü-Treiber

[Bearbeiten]

Eingabeereignisse für das Menü können mit der Funktion

int menu_driver(MENU *menu, int c);

abgehandelt werden.

Der Parameter c bstimmt, welche Menüaktion durchgeführt werden soll:

REQ_LEFT_ITEM bewegt den Menücursor um einen Eintrag nach links
REQ_RIGHT_ITEM bewegt den Menücursor um einen Eintrag nach rechts
REQ_UP_ITEM bewegt den Menücursor um einen Eintrag nach oben
REQ_DOWN_ITEM bewegt den Menücursor um einen Eintrag nach unten
REQ_SCR_ULINE eine Zeile aufwärts scrollen
REQ_SCR_DLINE eine Zeile abwärts scrollen
REQ_SCR_UPAGE eine Seite aufwärts scrollen
REQ_SCR_DPAGE eine Seite abwärts scrollen
REQ_FIRST_ITEM bewegt den Menücursor zum ersten Eintrag
REQ_LAST_ITEM bewegt den Menücursor zum letzten Eintrag
REQ_NEXT_ITEM bewegt den Menücursor zum nächsten Eintrag
REQ_PREV_ITEM bewegt den Menücursor zum vorherigen Eintrag
REQ_TOGGLE_ITEM An- oder abwählen eines Eintrags
REQ_CLEAR_PATTERN Suchmusterpuffer löschen
REQ_BACK_PATTERN Das vorherige Zeichen aus dem Suchmusterpuffer löschen
REQ_NEXT_MATCH Menücursor zum nächsten Eintrag, der zum Suchmuster passt, bewegen
REQ_PREV_MATCH Menücursor zum vorigen Eintrag, der zum Suchmuster passt, bewegen

Den aktuell angewählten Menüeintrag ermitteln

[Bearbeiten]
ITEM *current_item(const MENU *menu);
int item_index(const ITEM *item);

Beispiel

[Bearbeiten]
#include <menu.h>
#include <stdlib.h>

ITEM **it;
MENU *me;

void quit(void)
{
  int i;

  unpost_menu(me);
  free_menu(me);

  for(i=0; i<=4; i++)
  {
    free_item(it[i]);
  }

  free(it);
  endwin();
}

int main(void)
{
  int ch;

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  nl();
  keypad(stdscr, TRUE);

  it = (ITEM **)calloc(5, sizeof(ITEM *));
  it[0] = new_item("M1", "");
  it[1] = new_item("M2", "");
  it[2] = new_item("M3", "");
  it[3] = new_item("Ende", "");
  it[4] = 0;
  me = new_menu(it);
  post_menu(me);	

  mvaddstr(7, 3, "Programm mittels Menü oder F1-Funktionstaste beenden");
  refresh();

  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_DOWN:
        menu_driver(me, REQ_DOWN_ITEM);
        break;
      case KEY_UP:
        menu_driver(me, REQ_UP_ITEM);
        break;
      case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */
        if(item_index(current_item(me)) == 3)
          exit(0);	
    }
  } 

  return (0);  
}

Compilieren, Linken:

gcc -o bsp bsp.c -lmenu -lncurses

Das Menü formatieren

[Bearbeiten]

Ein Menü ist standardmäßig bis zu 16 Zeilen hoch und 1 Spalte breit. Zur Einstellung einer anderen Größe, z.B. zur Generierung eines mehrspaltigen Menüs, existiert die Funktion

int set_menu_format(MENU *menu, int rows, int cols);

Diese Funktion muss im Bedarfsfall aufgerufen werden, bevor das Menu gepostet wird.

Beispiel

[Bearbeiten]
#include <menu.h>
#include <stdlib.h>

ITEM **it;
MENU *me;

void quit(void)
{
  int i;

  unpost_menu(me);
  free_menu(me);

  for(i=0; i<=4; i++)
  {
    free_item(it[i]);
  }

  free(it);
  endwin();
}

int main(void)
{
  int ch;

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  nl();
  keypad(stdscr, TRUE);

  it = (ITEM **)calloc(5, sizeof(ITEM *));
  it[0] = new_item("M1", "");
  it[1] = new_item("M2", "");
  it[2] = new_item("M3", "");
  it[3] = new_item("Ende", "");
  it[4] = 0;
  me = new_menu(it);
  set_menu_format(me, 2, 2);
  post_menu(me);	

  mvaddstr(7, 3, "Programm mittels Menü oder F1-Funktionstaste beenden");
  refresh();

  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_DOWN:
        menu_driver(me, REQ_DOWN_ITEM);
        break;
      case KEY_UP:
        menu_driver(me, REQ_UP_ITEM);
        break;
      case KEY_RIGHT:
        menu_driver(me, REQ_RIGHT_ITEM);
        break;
      case KEY_LEFT:
        menu_driver(me, REQ_LEFT_ITEM);
        break;				
      case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */
        if(item_index(current_item(me)) == 3)
          exit(0);	
    }
  }   

  return (0);  
}

Das Markierungssymbol

[Bearbeiten]

Zwecks besserer Sichtbarkeit wird vor dem ausgewählten Menüeintrag zusätzlich ein Zeichen oder eine Zeichenkette als Markierungssymbol gesetzt. Normalerweise ist dies ein Bindestrich. Dieses Verhalten kann aber mit der Funktion

int set_menu_mark(MENU *menu, const char *mark);

geändert werden. Diese Funktion wird nur dann das gewünschte Resultat liefern, wenn sie vor dem Posten des Menüs aufgerufen wird.

Beispiel (Programmausschnitt)

[Bearbeiten]
  // ...
  set_menu_mark(me, "-->");
  post_menu(me);
  // ... 

Beispiel (Programmausschnitt)

[Bearbeiten]
  // Ausschalten des Markierungssysmbols
  //...
  set_menu_mark(me, "");
  post_menu(me);
  // ... 

Das Markierungssymbol sollte aber nur dann ausgeschaltet werden, wenn absolut sichergestellt ist, dass das Programm nur auf Terminals mit "Highlighting"- oder Farbunterstützung eingesetzt wird. Das "Erraten" der jeweiligen Menücursorposition kann sich sonst sehr nervenaufreibend gestalten.

Menüfenster

[Bearbeiten]

Jedes Menü kann mit einem Haupt- und Unterfenster verknüpft werden. Das Hauptfenster kann z.B. zur Aufnahme eines Menütitels und zur Umrahmung des Unterfensters dienen. Im Unterfenster werden die Menüeinträge dargestellt.

int set_menu_win(MENU *menu, WINDOW *win);
int set_menu_sub(MENU *menu, WINDOW *sub);

Werden diese Funktionen in einem Programm nicht eingesetzt, so sind die Menüs mit dem Standardscreen verbunden.

Beispiel

[Bearbeiten]
#include <menu.h>
#include <stdlib.h>

ITEM   **it;
MENU   *me;
WINDOW *win;

void quit(void)
{
  int i;

  unpost_menu(me);
  free_menu(me);

  for(i=0; i<=4; i++)
  {
    free_item(it[i]);
  }

  free(it);
  delwin(win);
  endwin();
}

int main(void)
{
  int ch;

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  nl();
  keypad(stdscr, TRUE);

  it = (ITEM **)calloc(5, sizeof(ITEM *));
  it[0] = new_item("M1", "Menueeintrag 1");
  it[1] = new_item("M2", "Menueeintrag 2");
  it[2] = new_item("M3", "Menueeintrag 3");
  it[3] = new_item("Ende", "Programm beenden");
  it[4] = 0;
  me = new_menu(it);

  win = newwin(8, 30, 5, 5);
  set_menu_win (me, win);
  set_menu_sub (me, derwin(win, 4, 28, 3, 2));
  box(win, 0, 0);  
  mvwaddstr(win, 1, 2, "***** Testmenü *****");
  post_menu(me);        

  mvaddstr(14, 3, "Programm mittels Menü oder F1-Funktionstaste beenden");

  refresh();
  wrefresh(win);

  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_DOWN:
        menu_driver(me, REQ_DOWN_ITEM);
        break;
      case KEY_UP:
        menu_driver(me, REQ_UP_ITEM);
        break;
      case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */
        if(item_index(current_item(me)) == 3)
          exit(0);      
    }

    wrefresh(win);
  }   

  return (0);  
}

Die minimal notwendige Größe eines Unterfensters kann über die Funktion

int scale_menu(const MENU *menu, int *rows, int *columns);

ermittelt werden.

Menüs bunt gestalten

[Bearbeiten]

Die Farbgebung und Darstellungsattribute des selektierten Items festlegen:

int set_menu_fore(MENU *menu, chtype attr);

Die Farbgebung und Darstellungsattribute der unselektierten Items festlegen:

int set_menu_back(MENU *menu, chtype attr);

Das Erscheinungsbild des Menühauptfensters kann natürlich über die konventionellen ncurses-Befehle (wbkgd, wattrset, etc.) gestaltet werden.

Beispiel

[Bearbeiten]
#include <menu.h>
#include <stdlib.h>

ITEM   **it;
MENU   *me;
WINDOW *win;

void quit(void)
{
  int i;

  unpost_menu(me);
  free_menu(me);

  for(i=0; i<=4; i++)
  {
    free_item(it[i]);
  }

  free(it);
  delwin(win);
 
  endwin();
}

int main(void)
{
  int ch;

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  nl();
  keypad(stdscr, TRUE);
  start_color();

  init_pair(1, COLOR_WHITE, COLOR_BLUE);
  init_pair(2, COLOR_BLUE, COLOR_YELLOW);

  bkgd(COLOR_PAIR(1));

  it = (ITEM **)calloc(5, sizeof(ITEM *));
  it[0] = new_item("M1", "Menueeintrag 1");
  it[1] = new_item("M2", "Menueeintrag 2");
  it[2] = new_item("M3", "Menueeintrag 3");
  it[3] = new_item("Ende", "Programm beenden");
  it[4] = 0;
  me = new_menu(it);

  win = newwin(8, 30, 5, 5);
  set_menu_win (me, win);
  set_menu_sub (me, derwin(win, 4, 28, 3, 2));
  box(win, 0, 0);  
  mvwaddstr(win, 1, 2, "***** Testmenü *****");
  set_menu_fore(me, COLOR_PAIR(1)|A_REVERSE);
  set_menu_back(me, COLOR_PAIR(1));
  wbkgd(win, COLOR_PAIR(2));

  post_menu(me);        

  mvaddstr(14, 3, "Programm mittels Menü oder F1-Funktionstaste beenden");

  refresh();
  wrefresh(win);
 
  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_DOWN:
        menu_driver(me, REQ_DOWN_ITEM);
        break;
      case KEY_UP:
        menu_driver(me, REQ_UP_ITEM);
        break;
      case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */
        if(item_index(current_item(me)) == 3)
          exit(0);      
    }

    wrefresh(win);
  }   

  return (0);  
}

Optionen für Menüeinträge

[Bearbeiten]
int set_item_opts(ITEM *item, OPTIONS opts);
int item_opts_on(ITEM *item, OPTIONS opts);
int item_opts_off(ITEM *item, OPTIONS opts);

Die Funktionsbezeichnungen sind selbsterklärend. Es gibt hier nur eine Option, nämlich

O_SELECTABLE

Wird die Selektierbarkeit für einen Menüeintrag hiermit ausgeschaltet, so ist dieser Menüeintrag als nicht selektierbar dargestellt. Dies bezieht sich aber nur auf die Menüdarstellung, das Item ist trotzdem immer noch auswählbar. Die "Nichtselektierbarkeit" eines Items muss vom Programmierer im weiteren Code berücksichtigt werden. Möglich ist dies durch die Abfrage der Itemoptionen

OPTIONS item_opts(const ITEM *item);

Die Farbgebung für derartige nicht selektierbare Items kann über die Funktion

int set_menu_grey(MENU *menu, chtype attr);

gesteuert werden.

Beispiel

[Bearbeiten]
#include <menu.h>
#include <stdlib.h>

ITEM   **it;
MENU   *me;
WINDOW *win;

void quit(void)
{
  int i;

  unpost_menu(me);
  free_menu(me);

  for(i=0; i<=4; i++)
  {
    free_item(it[i]);
  }

  free(it);
  delwin(win);
 
  endwin();
} 

int main(void)
{
  int ch; 

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  nl();
  keypad(stdscr, TRUE);
  start_color();

  init_pair(1, COLOR_WHITE, COLOR_BLUE);
  init_pair(2, COLOR_BLUE, COLOR_YELLOW);
  init_pair(3, COLOR_BLACK, COLOR_BLUE); 

  bkgd(COLOR_PAIR(1));

  it = (ITEM **)calloc(5, sizeof(ITEM *));
  it[0] = new_item("M1", "Menueeintrag 1");
  it[1] = new_item("M2", "Menueeintrag 2");
  it[2] = new_item("M3", "Menueeintrag 3");
  it[3] = new_item("Ende", "Programm beenden");
  it[4] = 0;
  item_opts_off(it[3], O_SELECTABLE);
  me = new_menu(it);

  win = newwin(8, 30, 5, 5);
  set_menu_win (me, win);
  set_menu_sub (me, derwin(win, 4, 28, 3, 2));
  box(win, 0, 0);  
  mvwaddstr(win, 1, 2, "***** Testmenü *****");
  set_menu_fore(me, COLOR_PAIR(1)|A_REVERSE);
  set_menu_back(me, COLOR_PAIR(1));
  set_menu_grey(me, COLOR_PAIR(3));
  wbkgd(win, COLOR_PAIR(2));
  post_menu(me);        

  mvaddstr(14, 3, "Programm mittels F1-Funktionstaste beenden");

  refresh();
  wrefresh(win);
 
  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_DOWN:
        menu_driver(me, REQ_DOWN_ITEM);
        break;
      case KEY_UP:
        menu_driver(me, REQ_UP_ITEM);
        break;
      case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */
        if(item_index(current_item(me))==3 &&
           item_opts(current_item(me))==O_SELECTABLE)
        exit(0);      
    }

    wrefresh(win);
  }   

  return (0);  
}

Menüoptionen

[Bearbeiten]

Zum Setzen von Menüoptionen werden diese Funktionen verwendet:

int set_menu_opts(MENU *menu, OPTIONS opts);
int menu_opts_on(MENU *menu, OPTIONS opts);
int menu_opts_off(MENU *menu, OPTIONS opts);

Einige der verfügbaren Optionswerte sind

O_SHOWDESC Beschreibungen zu den Einträgen anzeigen
O_NONCYCLIC Nicht-zyklisch, am Menüende nicht automatisch zum Menübeginn springen und umgekehrt
O_ONEVALUE Nur ein Menüeintrag ist auswählbar

Alle Optionen sind standardmäßig eingeschaltet.

Beispiel: O_SHOWDESC

[Bearbeiten]
// ...
menu_opts_off(me, O_SHOWDESC); 
// ...

Beispiel: Mehrfachauswahl

[Bearbeiten]

Bei einer möglichen Mehrfachauswahl kann nicht mehr einfach mittels current_item der ausgewählte Menüeintrag bestimmt werden. Stattdessen kann die Funktion

bool item_value(const ITEM *item);

Verwendung finden. Diese Funktion liefert für selektierte Menüeinträge TRUE, für unselektierte Menüeinträge FALSE.

// ...
menu_opts_off(me, O_ONEVALUE);
// ...
  
while((ch=getch()) != KEY_F(1))
{
  switch(ch)
  {
    // ...
    case 0x20: /* Leertaste */
      menu_driver(me, REQ_TOGGLE_ITEM);

      int i;

      for(i=0; i<4; i++)
      {
        mvprintw(16+i, 3, "Item %i: %i", i+1, item_value(it[i]));
      }
      break;
    // ...
  }
}
//...

Damit sind die ncurses-Menüs zwar bei weitem noch nicht umfassend abgehandelt. Jedoch wären weitere Themen teilweise schon sehr komplex und nur für spezielle Einsatzfälle wirklich interessant. Mit dieser Bemerkung soll dieses Kapitel hier vorerst abgeschlossen werden.



Ein Formular erzeugen und wieder löschen

[Bearbeiten]

Der Auf- und Abbau von Formularen folgt dem gleichen Prinzip wie bei Menüs.

Die Erzeugung eines Formulars untergliedert sich in folgende Schritte:

  • Speicherplatz für die Formularfelder reservieren: z.B. mittels calloc-Funktion
  • Formularfelder erzeugen:
    • FIELD *new_field(int height, int width, int toprow, int leftcol, int offscreen, int nbuffers);
    • FIELD *dup_field(FIELD *field, int toprow, int leftcol);
    • FIELD *link_field(FIELD *field, int toprow, int leftcol);
  • Formular erzeugen: FORM *new_form(FIELD **fields);
  • Formular "posten": int post_form(FORM *form);

Zu beachten ist, dass das letzte Formularfeld zwingend ein Null-Pointer sein muss.

Das "Abbauen" eines Formulars geschieht in umgekehrter Reihenfolge:

  • Formular "unposten": int unpost_form(FORM *form);
  • Formular freigeben: int free_form(FORM *form);
  • Felder freigeben: int free_field(FIELD *field);
  • Reservierten Feld-Speicherplatz freigeben: free-Funktion

Der Formulartreiber

[Bearbeiten]

Eingabeereignisse für ein Formular werden durch den Formulartreiber abgearbeitet.

int form_driver(FORM *form, int c);

Welche Aktion konkret ausgeführt werden soll, wird durch den Parameter c bestimmt. Eine schiere Unzahl von Optionen ist verfügbar. Nachfolgend werden nur ein paar dieser Request-Optionen aufgelistet:

REQ_NEXT_FIELD Cursor zum nächsten Feld bewegen
REQ_PREV_FIELD Cursor zum vorherigen Feld bewegen
REQ_FIRST_FIELD Cursor zum ersten Feld bewegen
REQ_LAST_FIELD Cursor zum letzten Feld bewegen
REQ_BEG_LINE Cursor zum Zeilenanfang bewegen
REQ_END_LINE Cursor zum Zeilenende bewegen
REQ_LEFT_CHAR Cursor im Feld nach links bewegen
REQ_RIGHT_CHAR Cursor im Feld nach rechts bewegen
REQ_UP_CHAR Cursor im Feld nach oben bewegen
REQ_DOWN_CHAR Cursor im Feld nach unten bewegen
REQ_INS_CHAR An der Cursorposition ein Leerzeichen einfügen
REQ_DEL_CHAR An der Cursorposition ein Zeichen löschen
REQ_DEL_PREV Das Zeichen vor der Cursorposition löschen
REQ_CLR_FIELD Das ganze Formularfeld löschen
REQ_OVL_MODE Überschreibmodus aktivieren
REQ_INS_MODE Einfügemodus aktivieren

Feldfarben und andere Darstellungsattribute

[Bearbeiten]

Hintergrundattribute festlegen:

 int set_field_fore(FIELD *field, chtype attr);

Vordergrundattribute festlegen:

int set_field_back(FIELD *field, chtype attr);

Beispiel

[Bearbeiten]
#include <form.h>
#include <stdlib.h>

FIELD  **fi;
FORM   *fo;

void quit(void)
{
  int i;

  unpost_form(fo);
  free_form(fo);

  for(i=0; i<=3; i++)
  {
    free_field(fi[i]);
  }

  free(fi);
  endwin();
}

int main(void)
{
  int ch, i;

  initscr();
  atexit(quit);
  clear();
  noecho();
  cbreak();
  keypad(stdscr, TRUE);
  start_color();
 
  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  init_pair(2, COLOR_BLACK, COLOR_WHITE);
 
  bkgd(COLOR_PAIR(1));

  fi = (FIELD **)calloc(4, sizeof(FIELD *));
  fi[0] = new_field(1, 10, 2, 3, 0, 0);
  fi[1] = new_field(1, 10, 2, 18, 0, 0);
  fi[2] = new_field(1, 15, 2, 33, 0, 0);
  fi[3] = 0;
 
  for(i=0; i<3; i++)
  {
    set_field_fore(fi[i], COLOR_PAIR(2));
    set_field_back(fi[i], COLOR_PAIR(2));
  }
 
  fo = new_form(fi);
  post_form(fo);        

  mvaddstr(2, 15, "+");
  mvaddstr(2, 30, "=");
  mvaddstr(5, 3, "Programm mittels F1-Funktionstaste beenden"); 

  refresh();
 
  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_RIGHT:
        form_driver(fo, REQ_NEXT_FIELD);
        break;
      case KEY_LEFT:
        form_driver(fo, REQ_PREV_FIELD);
        break;
      default: /* Feldeingabe */ 
        form_driver(fo, ch);
    }
  }   

  return (0);  
}

Compilieren, Linken:

gcc -o bsp bsp.c -lform -lncurses

Zugriff auf den Formularfeldpuffer

[Bearbeiten]

Auf ein bestimmtes Feld zugreifen:

FIELD *current_field(const FORM *);
int field_index(const FIELD *field);

Feldpuffer auslesen:

char *field_buffer(const FIELD *field, int buffer);

Feldpuffer belegen:

int  set_field_buffer(FIELD  *field,  int  buf, const char *value);

Beispiel

[Bearbeiten]
#include <form.h>
#include <stdlib.h>
#include <string.h>

FIELD  **fi;
FORM   *fo;

void quit(void)
{
  int i; 

  unpost_form(fo);
  free_form(fo);

  for(i=0; i<=3; i++)
  {
    free_field(fi[i]);
  }

  free(fi);
  endwin();
}

int main(void)
{
  int ch, i;

  initscr();
  atexit(quit);
  clear();
  noecho();
  cbreak();
  keypad(stdscr, TRUE);
  start_color();

  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  init_pair(2, COLOR_BLACK, COLOR_WHITE);

  bkgd(COLOR_PAIR(1));

  fi = (FIELD **)calloc(4, sizeof(FIELD *));
  fi[0] = new_field(1, 10, 2, 3, 0, 0);
  fi[1] = new_field(1, 10, 2, 18, 0, 0);
  fi[2] = new_field(1, 15, 2, 33, 0, 0);
  fi[3] = 0;

  for(i=0; i<3; i++)
  {
    set_field_fore(fi[i], COLOR_PAIR(2));
    set_field_back(fi[i], COLOR_PAIR(2));
  }

  fo = new_form(fi);
  post_form(fo);        

  mvaddstr(2, 15, "+");
  mvaddstr(2, 30, "=");
  mvaddstr(5, 3, "Programm mittels F1-Funktionstaste beenden");

  refresh();
 
  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_RIGHT:
      { 
        char str[20];
							
        form_driver(fo, REQ_NEXT_FIELD);

        if(field_index(current_field(fo)) == 2)
        {
          snprintf(str, 20, "%s%s", field_buffer(fi[0], 0), field_buffer(fi[1], 0));
          set_field_buffer(fi[2], 0, str);
          refresh();
        }
        break;
      }
      case KEY_LEFT:
        form_driver(fo, REQ_PREV_FIELD);
        break;
      default: /* Feldeingabe */	
        form_driver(fo, ch);
    }
  }   
  return (0);  
}


Textausrichtung

[Bearbeiten]

Die Ausrichtung eines Textes im Formularfeld ist mittels

int set_field_just(FIELD *field, int justification);

einstellbar.

Mögliche Ausrichtungsoptionen sind:

  • JUSTIFY_RIGHT
  • JUSTIFY_LEFT
  • JUSTIFY_CENTER
  • NO_JUSTIFICATION

Beispiel (Programmausschnitt)

[Bearbeiten]
   // ...
   set_field_fore(fi[i], COLOR_PAIR(2));
   set_field_back(fi[i], COLOR_PAIR(2));
   set_field_just(fi[i], JUSTIFY_RIGHT);
   // ...

Feldoptionen

[Bearbeiten]

Selbstverständlich gibt es auch für Formularfelder ein Menge Optionen. Gesetzt und abgefragt werden können sie mit diesen Funktionen:

int set_field_opts(FIELD *field, OPTIONS opts);
int field_opts_on(FIELD *field, OPTIONS opts);
int field_opts_off(FIELD *field, OPTIONS opts);
OPTIONS field_opts(const FIELD *field);

Einige der möglichen Optionen sind:

O_VISIBLE Formularfeldsichtbarkeit
O_ACTIVE Feld ist aktiv
O_PUBLIC Text ist bei der Eingabe sichtbar (z.B. bei Passworteingaben diese Option deaktivieren)
O_EDIT Im Feld kann editiert werden
O_WRAP Zeilenumbbruch
O_AUTOSKIP Wenn Feld vollgeschrieben ist, gehe automatisch zum nächsten Feld
O_STATIC Ein Feld kann nur die Zeichenanzahl entsprechend der Formularfeldgröße aufnehmen. Werden mehr Zeichen eingegeben so wird zum nächsten Formularfeld gesprungen. Ist O_AUTOSKIP deaktiviert, so werden zusätzliche Zeichen ignoriert. Ist O_STATIC ausgeschaltet, so kann das Formularfeld über die Formularfeldgröße Zeichen aufnehmen (die Darstellung wird gescrollt).

Beispiel: Auswirkungen O_AUTOSKIP und O_STATIC

[Bearbeiten]

Beim Eintippen der Zeichenkette "Das ist nicht OK" in ein Formularfeld passiert je nach gesetzten Optionen folgendes

O_AUTOSKIP an, O_STATIC an (Standard):
O_AUTOSKIP aus:
O_STATIC aus:

Formularfeldtypen

[Bearbeiten]

Oft ist es sinnvoll und notwendig die Eingabemöglichkeiten in ein Formularfeld einzuschränken (z.B. nur Zahlen oder alphabetische Zeichen sind erlaubt). Dies ist mit der form-Bibliothek recht weitgehend möglich. Mit der Funktion

int set_field_type(FIELD *field, FIELDTYPE *type, ...);

lassen sich die Feldtypen einstellen. Folgende Alternativen sind möglich

TYPE_ALPHA nur Alphabetzeichen sind erlaubt
TYPE_ALNUM Alphanumerische Zeichen sind erlaubt
TYPE_ENUM Nur Einträge aus einer Stringliste sind erlaubt
TYPE_INTEGER Nur ganze Zahlen sind erlaubt (optional mit vorangestelltem + oder -)
TYPE_NUMERIC Numerische Daten (optional mit vorangestelltem + oder - und mit Dezimalpunkt)
TYPE_REGEXP Feldeintrag muss zu einem regulären Ausdruck passen
TYPE_IPV4 Eine IPv4-Adresse

Es können auch eigene Formularfeldtypen festgelegt werden. Die Abhandlung dieses Themas würde jedoch im Rahmen dieses Tutorials zu weit führen. Nachfolgend ein einfaches Beispiel mit INTEGER- und NUMERIC-Formularfeldern.

Beispiel

[Bearbeiten]
#include <form.h>
#include <stdlib.h>

FIELD  **fi;
FORM   *fo; 

void quit(void)
{
  int i;

  unpost_form(fo);
  free_form(fo);

  for(i=0; i<=3; i++)
  {
    free_field(fi[i]);
  }

  free(fi);
  endwin();
}

int main(void)
{
  int ch, i;

  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, TRUE);
  start_color();

  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  init_pair(2, COLOR_BLACK, COLOR_WHITE);

  bkgd(COLOR_PAIR(1));

  fi = (FIELD **)calloc(4, sizeof(FIELD *));
  fi[0] = new_field(1, 10, 2, 3, 0, 0);
  fi[1] = new_field(1, 10, 2, 18, 0, 0);
  fi[2] = new_field(1, 15, 2, 33, 0, 0);
  fi[3] = 0;

  for(i=0; i<3; i++)
  {
    set_field_fore(fi[i], COLOR_PAIR(2));
    set_field_back(fi[i], COLOR_PAIR(2));
    field_opts_off(fi[i], O_AUTOSKIP);
  }

  set_field_type(fi[0], TYPE_INTEGER, 0, -9999999, 9999999);
  set_field_type(fi[1], TYPE_NUMERIC, 3, -9999999.999, 9999999.999);
  field_opts_off(fi[2], O_EDIT);

  fo = new_form(fi);
  post_form(fo);        

  mvaddstr(2, 15, "+");
  mvaddstr(2, 30, "=");
  mvaddstr(5, 3, "Programm mittels F1-Funktionstaste beenden");

  refresh();
 
  while((ch=getch()) != KEY_F(1))
  {
    switch(ch)
    {
      case KEY_RIGHT:
      { 
        double z1, z2;
        char str[20];
						
        form_driver(fo, REQ_NEXT_FIELD);

        if(field_index(current_field(fo)) == 2)
        {
          z1 = atof(field_buffer(fi[0], 0));
          z2 = atof(field_buffer(fi[1], 0));
          snprintf(str, 20, "%f", z1+z2);
          set_field_buffer(fi[2], 0, str);
          refresh();
        }
        break;
      }
      case KEY_LEFT:
        form_driver(fo, REQ_PREV_FIELD);
        break;
      default: /* Feldeingabe */	
        form_driver(fo, ch);
    }
  }   

  return (0);  
}

Formularfenster

[Bearbeiten]

Das Zuweisen von Haupt- und Unterfenster geschieht äquivalent der Vorgehensweise bei einem Menü.

int set_form_win(FORM *form, WINDOW *win);
int set_form_sub(FORM *form, WINDOW *sub);
int scale_form(const FORM *form, int *rows, int *columns);



Ncurses bietet Mausunterstützung. Standardmäßig ist diese Funktionalität deaktiviert. Zur Aktivierung ist die Funktion

mmask_t mousemask(mmask_t newmask, mmask_t *oldmask);

mit der gewünschten Bitmaske aufzurufen. Vordefinierte Masken sind (auszugsweise):

ALL_MOUSE_EVENTS Alle möglichen Mausereignisse
REPORT_MOUSE_POSITION Mausposition melden
BUTTON1_CLICKED Maustaste 1 geklickt
BUTTON1_DOUBLE_CLICKED Maustaste 1 doppelgeklickt
BUTTON2_CLICKED Maustaste 2 geklickt
BUTTON2_DOUBLE_CLICKED Maustaste 2 doppelgeklickt
BUTTON3_CLICKED Maustaste 3 geklickt
BUTTON3_DOUBLE_CLICKED Maustaste 3 doppelgeklickt
BUTTON_SHIFT Zusätzlich SHIFT-Taste gedrückt
BUTTON_CTRL Zusätzlich STRG-Taste gedrückt
BUTTON_ALT Zusätzlich ALT-Taste gedrückt

Die Abfrage des Auftretens eines durch die Mausmaske festgelegten sichtbaren Mausereignisses kann durch

int getmouse(MEVENT *event);

erfolgen.

Beispiel

[Bearbeiten]
#include <ncurses.h>
#include <stdlib.h>
MEVENT *mev;
void quit(void)
{
  free(mev);
  endwin();
}
int main(void)
{
  int ch;
  mev = (MEVENT *)malloc(sizeof(MEVENT));
  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, TRUE);
  start_color();
  mousemask(BUTTON1_CLICKED, 0);
  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  bkgd(COLOR_PAIR(1));
  mvaddstr(5, 3, „Programm durch anklicken der Maustaste 1 beenden“);
  refresh();
  for(;;)
  {
    ch=getch();
    switch(ch)
    {
      case KEY_MOUSE:
      {
        if(getmouse(mev) == OK)
        {
          exit(0);
        }
      }	
    }	
  }   

  return (0);  
}




Ein Pad (Schreibblock) ist eine Variante des konventionellen ncurses-Fensters. In der Anwendung unterscheidet es sich deutlich von diesem. Pads können größer als der Screen selbst sein und der Programmierer entscheidet erst beim Refresh, welcher Teil des Pads wo sichtbar ist.

Ein Pad erstellen

[Bearbeiten]
WINDOW *newpad(int nlines, int ncols);

Bedeutung der Parameter:

  • nlines, ncols ... Anzahl der Pad-Zeilen und -Spalten

Ein Pad refreshen

[Bearbeiten]
int prefresh(WINDOW *pad, int pminrow, int pmincol,
             int sminrow, int smincol, int smaxrow, int smaxcol);
int pnoutrefresh(WINDOW *pad, int pminrow, int pmincol,
                 int sminrow, int smincol, int smaxrow, int smaxcol);

Die Pad-Refreshfunktionen haben prinzipiell dieselben Aufgaben wie ihre Window-Pendants.

Bedeutung der Parameter:


  • pminrow, pmincol ... Ecke links-oben im Pad (p ... pad).
  • sminrow, smincol, smaxrow, smaxcol ... Ecke links-oben, Ecke rechts-unten. Diese Parameter bestimmen die Größe des auf dem Screen angezeigten Pad-Rechtecks, innerhalb dessen der Pad-Inhalt dargestellt wird (s ... screen).

Beispiel

[Bearbeiten]
#include <curses.h>
#include <stdlib.h>

WINDOW *pad;

void quit(void)
{
  delwin(pad);
  endwin();
} 

int main(void)
{
  initscr();
  atexit(quit);
  clear();
  noecho();
  curs_set(0);
  cbreak();
  keypad(stdscr, 1);

  start_color();
  init_pair(1, COLOR_YELLOW, COLOR_BLUE);
  init_pair(2, COLOR_BLUE, COLOR_WHITE);

  pad = newpad(300, 100);
  
  bkgd(COLOR_PAIR(1));
  wbkgd(pad, COLOR_PAIR(2));
  
  waddstr(pad, "Zeile 1 \n");
  waddstr(pad, "Zeile 2\n");
  waddstr(pad, "Diese Zeichenkette befindet sich in Zeile 3\n"); 
  waddstr(pad, "und diese in Zeile 4");  

  refresh();
  prefresh(pad, 0, 0, 3, 3, 10, 30);
 
  getch();
  refresh();
  prefresh(pad, 2, 2, 12, 2, 20, 45);
   
  while(getch() != KEY_F(1))
  {
  } 

  return(0);
}
Nach dem Programmstart Nach dem ersten Tastendruck



Jedes einzelne Kapitel könnte noch beliebig erweitert werden. Viele ncurses-Funktionen passend zu den einzelnen Kapiteln wurden überhaupt nicht erwähnt. Aber dieses Buch soll auch ein Tutorial darstellen und kein Referenzhandbuch. Vieles von dem, was gezeigt wurde lässt sich sicher auch effizienter und kompakter codieren. Die Beispiele wurden möglichst einfach gehalten und sollen dem grundlegenden Verständnis dienen. Dementsprechend fehlen praxisbezogene Beispiele.

Welche Themen wurden komplett ausgespart?

  • Die intrflush()-Funktion
  • User-Pointer
  • Hook-Funktionen: z. B. für Menüs und Formulare.
  • Diverse Utility-Funktionen
  • Low-level-Funktionen für verschiedene spezielle ncurses-Fähigkeiten
  • slk: soft function-key labels

Des Weiteren bietet ncurses auch Anbindungen an die Programmiersprachen Ada und C++. Interessant und hilfreich sind auch speziell auf curses aufbauende Widgetbibliotheken, wie z. B. CDK (Curses Development Kit).



Weitere Informationen zu ncurses

[Bearbeiten]

Downloadmöglichkeit

[Bearbeiten]

Andere curses-Bibliotheken

[Bearbeiten]