Ncurses

Aus Ethersex_Wiki
Wechseln zu: Navigation, Suche

ncurses

ncurses ist eine Bibliothek um quasi-grafische Oberflächen auf der Konsole zu zaubern. Soll heißen damit können zum Beispiel relativ einfach Texte an bestimmte Stellen positioniert werden, scrollbare Teilfenster erschaffen werden, uvm. Das ist für unserer Tutorial insbesondere dahingehend interessant, dass das Thema bei Weitem nicht so komplex ist wie die Programmierung von $richtigen grafischen Oberflächen (a la Gtk, Qt, Fltk, etc.)

Hallo Welt

#include <signal.h>

/* das ist die Header-Datei die für ncurses benötigt wird */
#include <curses.h>

/* wir müssen die Tastenkombination Strg+C abfangen 
 * und ggf. ncurses das Terminal wieder aufräumen lassen
 */
void finisher(int sig);

int
main(void)
{
  /* wir verknüpfen das INT-Signal von Linux mit der Funktion finisher */
  signal(SIGINT, finisher);

  /* curses initialisieren, siehe man ncurses */
  initscr();
  cbreak();
  noecho();

  nonl();
  intrflush(stdscr, FALSE);
  keypad(stdscr, TRUE);

  /* curser in Zeile 2, Spale 5 (von links, oben 0/0) */
  move(2, 5);

  /* text ausgeben */
  addstr("Hallo Welt!");

  move(4, 5);
  addstr(" --> ");

  for(;;) {
    /* zeichen lesen und in (den ascii code) 'ch' abspeichern */
    int ch = getch();

    /* alles außer a-z ignorieren */
    if(ch < 'a' |} ch > 'z') continue;

    /* rot25: wenn ch=='a' dann 'z' sonst minus 1 */
    ch = ch == 'a' ? 'z' : ch - 1;

    /* zeichen wieder ausgeben */
    addch(ch);
  }

  /* finisher aufrufen */
  finisher(0);
  return 0;
}

void
finisher(int sig)
{
  /* curses stoppen */
  endwin();
  exit(0);
}

Scroll The Screen

Ein großer Vorteil bei der Verwendung von ncurses ist, dass man schön den Bildschrim rauf und runter scrollen lassen kann - auf Wunsch auch automatisch ....

Programmende nach wie vor mit Strg+C !!

#include <signal.h>
#include <curses.h>

void finisher(int sig);

int
main(void)
{
  signal(SIGINT, finisher);

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

  nonl();
  intrflush(stdscr, FALSE);
  keypad(stdscr, TRUE);

  /* wir wollen, dass curses scrollen darf ... */
  scrollok(stdscr, TRUE);

  move(10, 5);
  addstr("Hallo Welt!");

  for(;;) {
    int ch = getch();

    /* jetzt sind nur noch zahlen erlaubt */
    if(ch < '1' |} ch > '9') continue;

    /* scrolle um die gewünschte anzahl 5 = 0, 6 = 1 rauf, 4 = 1 runter */
    wscrl(stdscr, ch - '5');
  }

  finisher(0);
  return 0;
}

void
finisher(int sig)
{
  endwin();
  exit(0);
}

Unterfenster

Gleich vorweg, Unterfenster ist hier auch nicht im Sinne eines wirklich grafischen Fensters gedacht, also Fensterdekoration etc. wie von X11 Fenstermanagern üblich gibt's hier nicht. Da müssen wir uns selber drum kümmern, zum Beispiel mittels der Funktion box

#include <signal.h>
#include <curses.h>

void finisher(int sig);

int
main(void)
{
  signal(SIGINT, finisher);

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

  nonl();
  intrflush(stdscr, FALSE);
  keypad(stdscr, TRUE);

  /* wir wollen ein Unterfenster, 40x5 Zeichen,
   * an Position 5/5 von oben/links aus gesehen
   */
  WINDOW *win = subwin(stdscr, 5, 40, 5, 5);

  /* cursor an position 1/1 im fenster!! */
  wmove(win, 1, 1);

  /* ausgabe von text im fenster */
  waddstr(win, "Hallo Welt!");

  /* um das fenster eine box zeichnen,
   * achtung, die linien erscheinen an stelle der
   * äußersten Zeichen des sub-window !!! */
  box(win, ACS_VLINE, ACS_HLINE);

  /* wait for the user to hit ^C */
  for(;;) getch();

  finisher(0);
  return 0;
}

void
finisher(int sig)
{
  endwin();
  exit(0);
}


Die obenstehenden Beispiele compiliert man am besten so

gcc -Wall -W -ggdb beispiel.c -o beispiel -lncurses

Spaß und Unterhaltung mit UTF-8

Das folgende Programm liest Zeichen für Zeichen vom Terminal und gibt dieses daraufhin jeweils fünf mal wieder aus (unter Verwendung der UTF-8-fähigen ncurses-Variante, der libncursesw)

Kompilieren könnt ihr das Ganze mit gcc -Wall -W -ggdb -o testling test.c -lncursesw -D_XOPEN_SOURCE_EXTENDED -std=c99 ...

#include <ncursesw/curses.h>
#include <signal.h>
#include <stdlib.h>
#include <locale.h>
#include <langinfo.h>
#include <string.h>

void
finisher(int sig)
{
  /* compiler-warnung, dass sig nicht verwendet wird, ignorieren.
   * auf grund dieser anweisung wird kein code im binary erstellt!!
   */
  (void) sig;

  /* curses stoppen */
  endwin();
  exit(0);
}

int
main(void)
{
  /* locale initialisieren */
  setlocale(LC_ALL, "");
  if(strcmp(nl_langinfo(CODESET), "UTF-8")) {
    fprintf(stderr, "Du willst UTF-8 aktivieren!");
    return 1;
  }

  /* curses initialisieren, siehe man ncurses */
  initscr();
  cbreak();
  noecho();

  nonl();
  intrflush(stdscr, FALSE);
  keypad(stdscr, TRUE);

  signal(SIGINT, finisher);

  for(;;) {
    wint_t wch;

    /* versuchen, zeichen zu lesen */
    int result = get_wch(&wch);

    /* wenn result nicht OK ist, also kein Zeichen gelesen
     * wurde, nochmal versuchen ... */
    if(result != OK) continue;

    /* zeichen rendern (für ausgabe vorbereiten) */
    cchar_t my_wchar;
    setcchar(&my_wchar, (wchar_t *) &wch, 0, 0, NULL);

    /* zeichen fünf mal ausgeben */
    for(int i = 0; i < 5; i ++)
      add_wch(&my_wchar);
  }

  finisher(0);
  return 0;
}