Ncurses - Wstęp Następujące terminy będą używane w tym rozdziale: okno - jest wewnętrzną reprezentacją zawierającą obraz części ekranu. WINDOW jest zdefiniowane w ncurses.h. ekran - jest oknem o rozmiarze całego ekranu ( od górnego lewego rogu po prawy dolny ). Stdscr i curscr są ekranami. terminal - jest specjalnym oknem zawierającym informacje o tym jak aktualnie wygląda ekran. zmienne - następujące zmienne i stałe zdefiniowano w ncurses.h WINDOW *curscr - aktualny ekran WINDOW *stdscr - standardowy ekran int LINES - linie terminala int COLS - kolumny terminala bool TRUE - flaga prawdy, 1 bool FALSE - flaga fałszu, 0 int ERR - flaga błędu, -1 int OK - flaga ok, 0 funkcje - w opisach funkcji argumenty są następujących typów: win - WINDOW* bf - bool ch - chtype str - char* chstr - chtype* fmt - char* inne int Zazwyczaj program używający bibliotekę ncurses wygląda następująco: Dołączenie ncurses.h zdefiniuje zmienne i typy dla ncurses, np. WINDOW i prototypy funkcji. Plik automatycznie dołącza stdio.h, stdarg.h, termios.h i unctrl.h. initscr() używana jest do zainicjowania danych oraz odczytania odpowiedniego pliku terminfo. Pamięć dla stdscr i curscr zostanie zaallokowana. Jeżeli wystąpią jakieś błędy initscr zwróci ERR, w innym wypadku zwrócony zostanie wskaźnik do stdscr. Dodatkowo ekran zostanie wymazany, zmienne LINES i COLS zostaną zainicjowane. Musisz wywołać tę funkcję przed jakąkolwiek inną funkcją ncurses. endwin() usunie wszystkie utworzone przez ncurses zasoby, przywróci tty tryb, w którym był przed initscr(). Musisz wywołać tę funkcję po zakończeniu korzystania z ncurses.
Jeżeli chcesz korzystać z kilku terminali możesz użyć newterm(...) zamiast initscr(). Skompiluj program następująco: Flagi mogą zawierać to co chcesz (zobacz gcc(1)). Ponieważ ścieżka do ncurses.h zmieniła się musisz podać również: W innym przypadku ncurses.h, nterm.h, termcap.h i unctrl.h nie zostaną znalezione. Inne możliwe opcje dla Linuxa to: O2 mówi gcc aby dokonał pewnej optymalizacji, -ansi używane jest dla kodu zgodnego z kodem ansi-c, -Wall wypisze wszyskie ostrzeżenia, -m486 stworzy kod zoptymalizowany dla 486 ( będzie on również wykonywalny na 386 ). Biblioteka ncurses znajduje się w /usr/lib/. Istnieją trzy wersje tej biblioteki: libncurses.a normalna biblioteka ncurses. libdcurses.a biblioteka do odpluskwiania. libpcurses.a biblioteka do profilowania ( począwszy od 1.8.6 libpcurses.a nie istnieje? ). libcurses.a Brak czwartej wersji, oprócz oryginalnej wersji BSD curses ( mój slackware 2.1.0 ma pakiet bsd ). Stuktury danych dla ekranu nazywane są oknami, są one zdefiniowane w ncurses.h. Okno jest jakby tablicą znaków w pamięci, którą programista może zmieniać bez wypisywania na ekran. Domyślnym oknem jest stdscr, ma ono wymiary teminala. Możesz tworzyć nowe okna za pomocą newwin(...). Aby optymalnie zaktualizować fizyczny terminal ncurses posiadają dodatkowe okno - curscr. Jest to aktualny obraz terminala, podczas gdy stdscr zawiera informacje o tym jak terminal powinien wyglądać. Zapisanie informacji na terminalu zostanie dokonane gdy wywołasz funkcję refresh(). Ncurses zaktualizują curscr informacją zawartą w stdscr. Funkcje biblioteki użyją wewnętrznej optymalizacji procesu odświeżania, więc możesz zmienić wiele okien, po czym zaktualizować ekran w najbardziej optymalny sposób. Za pomocą funkcji ncurses możesz manipulować różnymi oknami. Funkcje rozpoczynające się od w pozwalają wybrać okno, podczas gdy inne zazwyczaj działają na stdscr. Funcje zaczynające się na mv przesuną najpierw kursor do pozycji y,x. Każdy znak jest typu chtype, który zdefiniowany jest jako long unsigned int w celu przechowywania dodatkowych informacji ( atrybuty, itp. ). Ncurses używają bazy danych terminfo. Zazwyczaj znajduje się ona w usr/lib/terminfo/, tam ncurses zajrzą w poszukiwaniu jej. Jeżeli chcesz przetestować jakiś inny opis terminala bez zmiany terminfo ustaw zmienną systemową TERMINFO. Ncurses skorzysta z definicji zachowanych tam gdzie podałeś, zamiast /usr/lib/terminfo/. Aktualną wersją ncurses jest 1.8.6(. ( ja mam 5.0, ale ta też już jest pewnie stara ). Na końcu tego rozdziału znajdziesz tabelę zawierającą przegląd BSD-Curses, ncurses i curses z Sun-OS 5.4. Zajrzyj do niej w poszukiwaniu odpowiedniej funkcji i informacji o systemie, w którym ją zaimplementowano.
Inicjalizacja WINDOW *initscr() Jest to zazwyczaj pierwsza funkcja wywoływana w programie używającym ncurses. Czasami dobrze jest wywołać slk_init(int), filter(), ripoffline(...) lub use_env(bf) przed initscr(). Kiedy używasz kilku terminali ( lub testujesz zdolności ) możesz użyć newterm(...) zamiast initscr(). initscr() odczyta odpowiedni wpis z terminfo i zainicjuje dane ncurses, zallokuje pamięć dla curscr i stdscr, oraz LINES i COLS. Funkcja zwróci ERR jeżeli nastąpił błąd lub wskaźnik do stdscr. Nie musisz inicjalizować wskaźnika: initscr() zrobi to za ciebie. Jeżeli funkcja zwróciła ERR twój program powinien zakończyć się, gdyż żadna funkcja ncurses nie zadziała. SCREEN *newterm(char *type, FILE *outfd, FILE *infd) Jeżeli chcesz korzystać z kilku terminali użyj newterm(...) dla każdego z nich, zamiast initscr(). type jest nazwą terminala zawartą w zmiennej $TERM ( dla przykładu: ansi, xterm, vt100 ), outfd jest wskaźnikiem używanym jako wyjście, natomiast infd jest wskaźnikiem wejścia. Dla każdego terminala utworzonego za pomocą newterm(...) powinieneś wywołać endwin(). SCREEN *set_term(screen *new) Za pomocą funkcji set_term(screen) możesz zmienić aktualny terminal. Wszystkie funkcje działają na aktualnym terminalu, który ustawiany jest przez set_term(screen). int endwin() endwin() zrobi porządki, przywróci wszelkim trybom stan, w którym były przed wywołaniem initscr(), oraz przesunie kursor do lewego, górnego rogu. Nie zapomnij zamknąć wszystkich otwartych okien przed wywołaniem tej funkcji. Dodatkowe wywołanie refresh() po endwin() przywróci terminal do stanu z przed wywołania initscr() ( tryb wizualny ), w innym wypadku zostanie wyczyszczony ( tryb niewizualny ). int isendwin() Zwraca TRUE jeżeli endwin() wywołana została z refresh(), w innym przypadku FALSE. void delscreen(screen* sp) Jeżeli nie potrzebujesz już SCREEN to po endwin() wywołaj delscreen(screen) aby zwolnić wszelkie zasoby.
Okna Okna mogą być tworzone, usuwane, przesuwane, kopiowane, duplikowane, itd. WINDOW *newwin(nlines, ncols, begy, begx) begy i begx są współrzędnymi górnego, lewego rogu. nlines jest liczbą linii, a ncols to ilość kolumn. Figure 8.1: Ncurses - schemat dla newwin Lewy, górny róg naszego okna będzie w 10 linii i 10 kolumnie, okno będzie miało 10 linii i 60 kolumn. Jeżeli nlines jest zerem okno będzie miało LINES-begy wierszy. Tak samo okno będzie miało kolumn COLS-begx jeżeli ncols jest zerem. Jeżeli wywołasz newwin(...) z wszystkimi argumentami równymi zero: otwarte okno będzie miało rozmiar ekranu. Za pomocą LINES i COLS możemy otworzyć okno na środku ekranu, bez względu na jego rozmiar:
W ten sposób stworzymy okno z 22 liniami i 70 kolumnami na środku ekranu. Przed otwarciem okna sprawdź rozmiar ekranu. Nie zakładaj, że wszyscy mają 25x80 ( ja lubię 34x80 ), do tego xterminale mają zmienny rozmiar. Alternatywnie możesz użyć LINES i COLS aby zaadaptować dwa okienka do rozmiaru ekranu: int delwin(win) Usuwa okno win. Jeżeli istnieją podokna usuwa je. Usuwa zasoby zajmowane przez win. Usuń wszystkie okna przed wywołaniem endwin(). int mvwin(win, by, bx) Przeniesie okno do współrzędnych by,bx. Jeżeli oznaczałoby to przesunięcie okna poza ekran nic nie jest robione, funkcja zwraca ERR. WINDOW *subwin(origwin, nlines, ncols, begy, begx) Zwraca podokno na środku origwin. Kiedy zmienisz jedno z okien ( origwin lub nowo utworzone ) zmienią się oba. Wywołaj touchwin(origwin) przed następnym wywołaniem refresh(). begx i begy są liczone względem ekrany, nie origwin. WINDOW *derwin(origwin, nlines, ncols, begy, begx) To samo co subwin(...), za wyjątkiem tego że begx i begy są liczone względem origwin.
int mvderwin(win, y, x) Przesunie okno win wewnątrz nadrzędnego okna. WINDOW *dupwin(win) Duplikuje okno win. int syncok(win, bf) void wsyncup(win) void wcursyncup(win) void wsyncdown(win) int overlay(win1, win2) int overwrite(win1, win2) overlay(...) skopiuje cały tekst z win1 do win2 bez kopiowania znaków pustych. overwrite(...) robi to samo, ale kopiuje także pustkę. int copywin(win1, win2, sminrow, smincol, dminrow, dmincol, dmaxrow, dmaxcol, overlay) Podobne do overlay(...) i overwrite(...), przy czym pozwala określić obszar do skopiowania.
Wyjście int addch(ch) int waddch(win, ch) int mvaddch(y, x, ch) int mvwaddch(win, y, x, ch) Funkcje zapisujące znak w okienku. Zmieniają okno, po czym musisz wywołać refresh() aby aktualizować ekran. addch(...) i waddch(...) zapisują znak ch w oknie stdscr lub win. mvaddch(...) i mvwaddch(...) robią to samo, ale najpierw przenoszą kursor do pozycji x, y. int addstr(str) int addnstr(str, n) int waddstr(win, str) int waddnstr(win, str, n) int mvaddstr(y, x, str) int mvaddnstr(y, x, str, n) int mvwaddstr(win, y, x, str) int mvwaddnstr(win, y, x, str, n) Funkcje zapisujące łańcuch w oknie; są one odpowiednikami addch(...). str musi być zakończony nullem ( "blafoo\0" ). Funkcje z w zapisują łańcuch w okienku, natomiast inne zapisują stdscr. Funkcje z n zapisują n znaków z str. Jeżeli n równa się -1 zapisywany jest cały str. int addchstr(chstr) int addchnstr(chstr, n) int waddchstr(win, chstr) int waddchnstr(win, chstr, n) int mvaddchstr(y, x, chstr) int mvaddchnstr(y, x, chstr, n) int mvwaddchstr(win, y, x, chstr) int mvwaddchnstr(win, y, x, chstr, n) Funckje kopiujące chstr do obrazu okna (stdscr lub win). Pozycją początkową jest aktualne położenie kursora. Funkcje z n zapisują n znaków. Jeżeli n = -1 zapisywany jest cały chstr. Kursor nie jest przemieszczany, nie zachodzi kontrola znaków. Te fukncje są szybsze od addstr(...). chstr jest wskaźnikiem do tablicy chtype. int echochar(ch) int wechochar(win, ch) To samo co addch(...) ( waddch(...) ) zakończone przez refresh() ( wrefresh(win) ).
Sformatowane wyjście int printw(fmt,...) int wprintw(win, fmt,...) int mvprintw(y, x, fmt,...) int mvwprintw(win, y, x, fmt,...) int vwprintw(win, fmt, va_list) Funckje odpowiadają printf(...) z libc. W pakiecie libc funkcja printf(...) używana jest do sformatowanego wyjścia. Możesz zdefiniować łańcuch wyjściowy, oraz włączyć w niego zmienne różnych typów. Aby korzystać z vwprintw(...) musisz dołączyć varargs.h. Wstawianie znaków/linii int insch(c) int winsch(win, c) int mvinsch(y,x,c) int mvwinsch(win,y,x,c) Znak ch wstawiany jest po lewej stronie kursora, wszystkie znaki są przesuwane jedną pozycję w prawo. Znak na prawy końcu może być utracony. int insertln() int winsertln(win) Wstaw pustą linię powyżej aktualnej. Ostatnia linia zostanie stracona. int insdelln(n) int winsdelln(win, n) Dla dodatniego n zostanie wstawionych n linii powyżej kursora w odpowiednim okienku, n dolnych linii zostanie straconych. Dla ujemnego n zostanie usuniętych n linii, reszta zostanie przesunięta do góry. int insstr(str) int insnstr(str, n) int winsstr(win, str) int winsnstr(win, str, n) int mvinsstr(y, x, str) int mvinsnstr(y, x, str, n) int mvwinsstr(win, y, x, str) int mvwinsnstr(win, y, x, str, n) Funkcje wstawiają str po lewej stronie kursora ( znaków może być aż do wypełnienia linii ). Znaki po prawej stronie są przesuwane, jeżeli przesuniesz je za ekran zostaną stracone. Pozycja kursora pozostaje niezmieniona. y i x są współrzędnymi, pod które kursor zostanie przesunięty. n jest ilością znaków ( n = 0 - cały łańcuch ).
Usuwanie znaków/linii int delch() int wdelch(win) int mvdelch(y, x) int mvwdelch(win, y, x) Usuwa znak pod kursorem i przesuwa pozostałe znaki po prawej kursora w lewo. y i x są współrzędnymi, pod które kursor zostanie przesunięty przed kasowaniem. int deleteln() int wdeleteln(win) Kasuj linię pod kursorem i przesuń poniższe linie do góry. Dodatkowo, dolna linia okna zostanie usunięta.
Ramki i linie int border(ls, rs, ts, bs, tl, tr, bl, br) int wborder(win, ls, rs, ts, bs, tl, tr, bl, br) int box(win, vert, hor) Rysują ramkę wokół krawędzi okna ( stdscr lub win ). W ramce poniżej znajdują się znaki i ich domyślne wartości dla funkcji box(...). Pozycja określa położenie względem znaków w ramce. Table: Ncurses - oznaczenia znaków Znak Pozycja Domyślne tl góra, lewo ACS_ULCORNER ts góra ACS_HLINE tr góra, prawo ACS_URCORNER ls lewa strona ACS_VLINE rs prawa strona ACS_VLINE bl dół, lewo ACS_LLCORNER bs dół ACS_HLINE br dół, prawo ACS_LRCORNER rt rozgałęzienie w prawo ACS_RTEE lt rozgałęzienie w lewo ACS_LTEE tt rozgałęzienie w górę ACS_TTEE bt rozgałęzienie w dół ACS_BTEE Figure 8.2: Ncurses - znaki ramek int vline(ch, n) int wvline(win, ch, n) int hline(ch, n) int whline(win, ch, n) Funkcje rysujące pionową ( v* ) lub poziomą ( h* ) linię rozpoczynającą się w aktualnej
Tło pozycji kursora. ch jest znakiem, który będzie tworzył linię, n jest ilością powtórzeń. Pozycja kursora nie jest zmieniana. void bkgdset(ch) void wbkgdset(win, ch) Ustawiają znak tła, oraz jego atrybut dla ekranu, okna. Atrybut zostanie zorowany z każdy nie pustym znakiem znajdującym się w oknie. Tło jest częścią okna - nie jest przesuwane, zmieniane w czasie wejścia/wyjścia. int bkgd(ch) int wbkgd(win, ch) Zmieniają znak i atrybut tła na podany w ch. Wejście int getch() int wgetch(win) int mvgetch(y, x) int mvwgetch(win, y, x) getch() odczytuje wejście z terminala w sposób zależny od trybu opóźnienia. Jeżeli opóźnienie jest włączone getch() poczeka, aż zostanie naciśnięty klawisz, jeżeli tryb jest wyłączony funkcja powróci natychmiast zwracając klawisz lub ERR jeżeli bufor jest pusty. mvgetch(...) i mvwgetch(...) przesuwają kursor przed czytaniem. Funckje z w w nazwie odczytują wejście z terminala związanego z win. Po wywołaniu keypad(...) getch() zwraca kod zdefiniowany w ncurses.h jako makro KEY_* jeżeli znakiem był klawisz funkcyjny. Kiedy naciśnięto ESCAPE ( który może być początkiem kodu klawisza funkcyjnego ) ncurses włączy jednosekundowy timer. Jeżeli pozostały kod nie nadejdzie w ciągu sekundy klawisz jest zwracany, w innym przypadku zwracana jest wartość klawisza funkcyjnego. ( możesz wywołać notimeout() jeżeli nie chcesz sekundowego timera ). int ungetch(ch) Odłoży znak ch do bufora wejściowego. int getstr(str) int wgetstr(win, str) int mvgetstr(y, x, str) int mvwgetstr(win, y, x, str) int wgetnstr(win, str, n) Funkcje wywołują getch() aż do znaku nowej linii. Znaki odkładane są do str, więc nie zapomnij zallokować pamięci przed wywołaniem funkcji. Jeżeli włączone jest echo łańcuch jest wyświetlany ( użyj noecho() aby je wyłączyć ), znaki kill i delete są interpretowane. chtype inch() chtype winch(win) chtype mvinch(y, x) chtype mvwinch(win, y, x) Funkcje zwracają znak z ekranu lub okna. Wartością zwracaną jest chtype, więc informacje o atrybutach są przekazywane. Możesz je wyciągnąć ze znaku używając stałych
A_* ( zobacz tabeli 8.4 na stronie ). int instr(str) int innstr(str, n) int winstr(win, str) int winnstr(win, str, n) int mvinstr(y, x, str) int mvinnstr(y, x, str, n) int mvwinstr(win, y, x, str) int mvwinnstr(win, y, x, str, n) Zwracają łańcuch znaków z ekranu lub okna. int inchstr(chstr) int inchnstr(chstr, n) int winchstr(win, chstr) int winchnstr(win, chstr, n) int mvinchstr(y, x, chstr) int mvinchnstr(y, x, chstr, n) int mvwinchstr(win, y, x, chstr) int mvwinchnstr(win, y, x, chstr, n) Zwracają łańcuch chtype z ekranu lub okna. Łańcuch zawiera informacje o atrybucie każdego znaku. Opcje wyjścia int idlok(win, bf) void idcok(win, bf) Włącz/wyłącz wstawianie/usuwanie przez terminal dla danego okna ( idlok(...) dla linii, idcok(...) dla znaków ) void immedok(win, bf) Jeżeli ustawiona na TRUE, każda zmiana win spowoduje odświeżenie fizycznego ekranu. Może to spowodować spadek wydajności, więc domyślnie FALSE. int clearok(win, bf) Jeżeli bf = TRUE następne wywołanie wrefresh(win) wyczyści ekran i odrysuje go ( np.: ctrl+l w vi ). int leaveok(win, bf) Domyślnie ncurses zostawia kursor tam gdzie się znajdował w czasie ostatniego odświeżania. Programy nie używające kursora mogą wywołać leaveok(...) z TRUE aby zaoszczędzić czas, który zużyty zostałby na przesunięcie kursora. Dodatkowo ncurses spróbują uczynić kursor niewidocznym. int nl() int nonl() Jeżeli wywołasz nl() ncurses będą wysyłały na wyjście znak nowej linii jako powrót karetki i wysunięcie linii. nonl() wyłączy takie zachowanie co spowoduje, że ncurses będą szybciej przesuwały kursor.
Sformatowane wejście int scanw(fmt,...) int wscanw(win, fmt,...) int mvscanw(y, x, fmt,...) int mvwscanw(win, y, x, fmt,...) int vwscanw(win, fmt, va_list) Funkcje są podobne do scanf(...) z libc ( zobacz sekcja 8.3.2 na stronie ). wgetstr(...) jest wywoływana, to co zwróci używane jest jako wejście dla scan. Opcje wejścia int keypad(win, bf) Jeżeli TRUE włączona zostaje klawiatura numeryczna terminala użytkownika podczas oczekiwania na wejście. Ncurses zwrócą kod klawisza zdefiniowany w ncurses.h jako KEY_* dla klawiszy funkcyjnych i strzałek na klawiaturze numerycznej. Jest to bardzo użyteczne przy klawiaturze PC, gdyż możesz uaktywnić blok numeryczny i strzałki. int meta(win, bf) Jeżeli TRUE kody zwracane przez getch() są 8-bitowe ( najwyższy bit nie zostanie zmieniony ). int cbreak() int nocbreak() int crmode() int nocrmode() cbreak() i nocbreak() włączą/wyłączą tryb CBREAK. Kiedy CBREAK jest włączone wejście jest natychmiast dostępne dla programu, gdy wyłączone wejście będzie buforowane aż do pojawienia się nowej linii. ( Uwaga: crmode() i nocrmode() istnieją dla kompatybilności ze starszymi programami - nie używaj ich. ) int raw() int noraw() Włączenie/wyłączenie trybu RAW. RAW jest tym samym co CBREAK, z wyjątkiem tego, iż w tym trybie nie dochodzi do przetwarzania znaków specjalnych. int echo() int noecho() Ustaw echo() aby wypisywać wejście wprowadzane przez użytkownika, noecho() milczy na
ten temat ;) int halfdelay(t) To samo co cbreak() z opóźnieniem t sekund. int nodelay(win, bf) Terminal zostanie ustawiony w tryb nie-blokowania. getch() zwróci ERR jeżeli nie ma żadnego gotowego wejścia. Ustawione na FALSE powoduje, iż getch() poczeka na naciśnięcie klawisza. int timeout(t) int wtimeout(win, t) Zalecane jest używanie tych funkcji zamiast halfdelay(t) i nodelay(win,bf). Rezultat getch() zależy od wartości t. Jeżeli jest ona dodatnia wejście blokowane jest na t milisekund, jeżeli t = 0 nie zachodzi blokowanie, a dla wartości ujemnej program czeka na wejście. int notimeout(win, bf) Jeżeli bf = TRUE, getch() użyje specjalnego timera ( trwającego jedną sekundę ) aby zinterpretować i odczytać sekwencję zaczynającą się od ESCAPE, itp. int typeahead(fd) Jeżeli fd = -1 nie zachodzi sprawdzanie z wyprzedzeniem ( typeahead ), w innym przypadku ncurses skorzystają z fd ( zamiast z stdin ) dla tych sprawdzeń. int intrflush(win, bf) Jeżeli bf = TRUE klawisz przerwania ( quit, break,... ) naciśnięty w terminalu opróżni kolejkę wejściową sterownika tty. void noqiflush() void qiflush() Atrybuty terminala int baudrate() Zwraca prędkość terminala w bps. char erasechar() Zwraca aktualny znak erase ( usuwania ). char killchar() Zwraca aktualny znak kill ( zabijania ). int has_ic() int has_il() has_ic() zwraca TRUE jeżeli terminal posiada znak wstaw/usuń, has_il() zwraca TRUE jeżeli terminal może wstawiać/kasować linie. W innym przypadku funkcje zwracają ERR. char *longname() Zwrócony wskaźnik daje ci dostęp do opisu terminala. chtype termattrs() char *termname() Zwraca zawartość zmiennej środowiskowej TERM.
Używanie opcji Widzieliśmy opcje okien, tryby terminala czas aby opisać ich sposób użycia. Po pierwsze, w Linuksie powinieneś włączyć klawiaturę numeryczną. Dzięki temu użytkownik będzie mógł skorzystać ze strzałek i części numerycznej klawiatury. Teraz, mamy dwa główne typu wejścia. 1. 2. Program chce aby użytkownik nacisnął klawisz, po czym wywoła funkcję zależną od klawisza ( dla przykład coś takiego: "Naciśnij 'q' aby zakończyć", przy czym czekamy na klawisz ). Program chce sformatowany łańcuch znaków ( np.: katalog, lub adres w bazie danych ). Dla pierwszego używamy następujących opcji i pętli while. Program będzie czekał na naciśnięcie jakiegoś klawisza, jeżeli naciśnięto q program wywoła funkcja_konczaca, w innym wypadku program zaczeka na inne wejście. Wyrażenie switch może zostać rozszerzone dla dowolnego wejścia. Użyj makr KEY_* aby sprawdzić czy naciśnięto specjalne klawisze, np.: dla klawiszy kursora. Dla przeglądarki plików pętla mogłaby wyglądać następująco:
Dla drugiego typu musimy tylko ustawić echo() aby znaki wpisywane przez użytkownika były wypisywane na ekranie. Aby wypisywać znaki na wybranej przez ciebie pozycji skorzystaj z move(...) lub wmove(...). Lub, możemy otworzyć okno z maską ( inne kolory od tych okna zrobią to ) i poprosić użytkownika o łańcuch:
Zobacz input.c w katalogu z przykładami. Usuwanie okien i linii int erase() int werase(win) werase(...) i erase() skopiują pustkę w każdą pozycję okna win lub stdscr. Jeżeli ustawiłeś oknu kolory wywołanie werase() pozostawi je bez zmian. Miałem kiedyś problemy z COLOR_PAIRS nie zdefiniowanymi jako czarny i biały, więc napisałem własną funkcję ( jest to niskopoziomowy dostęp do struktury WINDOW ): Problemem jest to, iż ncurses czasami nie używają atrybutów okna podczas zapełniania go pustką. W lib_clrtoeol.c, BLANK zdefiniowana jest następująco: co powoduje, iż inne atrybuty okna zostają stracone. int clear()
int wclear(win) To samo co erase(), z wyjątkiem tego, że ustawi także clearok() ( ekran zostanie wyczyszczony przy następnym odświeżaniu ). int clrtobot() int wclrtobot(win) Czyszczą aktualną linię kursora ( start jest jeden znak na prawo od kursora ) i linię poniżej kursora. int clrtoeol() int wclrtoeol(win) Czyszczą aktualną linię począwszy od prawej strony kursora aż do jej końca. Aktualizacja terminala Jak napisano we wstępie ncurses traktuje okna jako abraz w pamięci. Oznacza to, iż zmiany nie są pokazywane na ekranie aż do czasu odświeżania. Dzięki temu programy stają się szybsze - przeprowadzasz wiele zmian a dopiero po tym wyświetlasz je w jednym przebiegu. int refresh() int wrefresh(win) refresh() kopiuje stdscr na terminal, a wrefresh(win) kopiuje obraz okna na stdscr po czym aktualizuje curscr aby wyglądał jak stdscr. int wnoutrefresh(win) int doupdate() wnoutrefresh(win) tylko kopiuje okno win na stdscr. Zapisywany jest stdscr, natomiast terminal pozostaje niezmieniony. doupdate() aktualizuje terminal. Program może zmienić kilka okien, wywołać wnoutrefresh(win) dla każdego z nich, a potem wywołać doupdate() aby odświeżyć fizyczny ekran. Np. mamy dwa okienka, zmieniamy je daną funkcją, po czym odświeżamy ekran: Taka funkcja spowoduje, iż terminal zostanie zaktualizowany dwa razy. Gdybyśmy użyli wnoutrefresh(win) i doupdate() uzyskalibyśmy szybsze działanie ( tylko raz odświeżamy terminal ):
int redrawwin(win) int wredrawln(win, bline, nlines) Użyj tych funkcji jeżeli chcesz przerysować cały ekran lub daną linię ( np. gdy są one zaśmiecone, itp. ) int touchwin(win) int touchline(win, start, count) int wtouchln(win, y, n, changed) int untouchwin(win) Mówią ncurses, że całe okienko win lub linie od start do start+count powinny być zmieniane. Np. gdy masz dwa zachodzące na siebie okienka ( tak jak w przykładzie type.c ) zmiana jednego z nich nie zmieni drugiego. wtouchln(...) dotknie n linii zaczynając od y. Jeżeil change = TRUE linie są dotykane, w innym przypadku nie ( zmieniane lub nie ). untouchwin(win) zaznaczy okno win jako niezmienione od ostatniego wywołania refresh(). int is_linetouched(win, line) int is_wintouched(win) Za pomocą tych funkcji możesz sprawdzić czy line lub okno win zostało zmienione od czasu ostatniego wywołania refresh(). Atrybuty video i kolory Atrybuty są specjalnymi zdolnościami terminala używanymi podczas wypisywania znaków na ekran. Znaki mogą być wypisywane jako: pogrubione, podkreślone, migające, itp. W ncurses masz możliwość włączania/wyłączania atrybutów aby twój program wyglądał ładniej. Atrybuty które możesz wykorzystać: Table 8.4: Ncurses - atrybuty Definicja Atrybut A_ATTRIBUTES maska dla atrybutów (chtype) A_NORMAL normalny, resetuje wszystkie inne A_STANDOUT najlepsze podświetlenie A_UNDERLINE podkreślenie A_REVERSE odwrócenie video A_BLINK miganie A_DIM dim lub połowa jasności A_BOLD pogrubienie lub extra jasno A_ALTCHARSET użyj alternatywnego zestawu znaków A_INVIS niewidzialny A_PROTECT??? A_CHARTEXT maska dla aktualnych znaków (chtype) A_COLOR maska dla koloru COLOR_PAIR(n) ustaw parę kolorów na tą przechowywaną w n PAIR_NUMBER(a) pobierz parę kolorów przechowywaną w atrybucie a
Ncurses definiują osiem kolorów, które możesz użyć na terminalu obsługującym kolory. Najpierw musisz zainicjować struktury danych kolorów za pomocą start_color(), póżniej sprawdziać zdolność terminala has_colors(). start_color() zainicjuje COLORS maxymalną liczbą wspieraną przez terminal, oraz COLOR_PAIR - maxymalna ilość par, które możesz zdefiniować. Table 8.5: Ncurses - kolory Definition Color COLOR_BLACK COLOR_RED COLOR_GREEN COLOR_YELLOW COLOR_BLUE czarny czerwony zielony żółty niebieski COLOR_MAGENTA karmazynowy COLOR_CYAN COLOR_WHITE cyan biały Atrybuty mogą być łączony za pomocą operatora OR ' ', tak więc możesz stworzyć pogrubione, migające wyjście: Kiedy ustawisz oknu atrybut attr wszystkie znaki zawarte w nim zostaną wypisane z tym atrybutem. Atrybut nie zniknie podczas przewijania, przesuwania, itp. Pisząc programy dla ncurses i BSD curses musisz pamiętać, że te drugie nie obsługują kolorów ( także starsze wersje curses z SYSV nie mają obsługi kolorów ). Jeżeli chcesz aby twój program pracował pod obiema bibliotekami musisz posłużyć się #ifdef. int attroff(attr) int wattroff(win, attr) int attron(attr) int wattron(win, attr) Wyłączają/włączają określony atrybut attr, bez zmiany innych, w oknie ( stdscr lub win ). int attrset(attr) int wattrset(win, attr) Ustawiają atrybut stdscr lub win na attr. int standout() int standend() int wstandout(win) int wstandend(win) Włączają podświetlanie w oknie ( stdscr lub win ). chtype getattrs(win) Zwraca aktualne atrubytu okna win. bool has_colors() Zwraca TRUE jeżeli terminal jest kolorowy. Przed użyciem kolorów powinieneś sprawdzić
terminal za pomocą tej funkcji, rób to zawsze przed inicjalizacją ( start_color() ). bool can_change_color() TRUE jeżeli terminal może zmieniać kolory. int start_color() Inicjalizacja koloru. Musisz ją wywołać przed użyciem kolorów!!! int init_pair(pair, fg, bg) Gdy używasz kolorów jako atrybut okna musisz zdefiniować parę kolorów za pomocą init_pair(...). fg jest kolorem textu, natomiast bg jest tłem. Wartości mogą zawierać się od 1 do ( 0 jest zarezerwowane dla czarnego i białego ). Po zdefiniowaniu możesz taką parę używać jako zwykły atrybut. Gdy chcesz czerwone znaki na niebieskim tle: Użyj wattron(...) aby ustawić win nową parę kolorów: Lub połącz kolory z innymi atrybutami: Pierwsze wywołanie ustawi kolor i pogrubienie. Drugie wyróżnienie i kolory, będziesz miał podświetlony czerwony na niebieskim tle. int pair_content(pair, f, b) Zwraca kolor textu i tła zawarty w podanej pair. int init_color(color, r, g, b) Zmień składowe koloru r, g i b dla color. r, g i b mogą przyjmować wartości od 1 do COLORS-1. int color_content(color, r, g, b) Pobierz składowe r, g i b danego color. Jak mieszać atrybuty i kolory? Niektóre terminale, tak jak Linuksowa konsola, posiadają kolory, a niektóre ich nie mają ( xterm, vs100, itp. ). Następujący kod powinien rozwiązać problem:
Najpierw CheckColor inicjalizuje kolory za pomocą start_color(), póżniej sprawdza ( has_colors() ) czy terminal ma kolory. Jeżeli TRUE to inicjalizujemy parę kolorów, oraz ustawiamy ją dla okien ( wattrset(...) ). Jeżeli nie mamy kolorów ustawiamy jakieś zastępcze atrybuty ( odwróceni, pogrubienie ). Aby uzyskać kolory w xtermie najlepszym sposobem jaki znalazłem jest skorzystanie z ansi_xterm wraz z połatanym wpisem w terminfo, zawartym w pakiecie Midnight Commander. Znajdź źródła ansi_xterm i Midnight Commandera ( mc-x.x.tar.gz ). Skompiluj ansi_xterm, użyj tic z xterm.ti i vt100.ti z archiwum MC. Włącz ansi_xterm i przetestuj go.
Współrzędne kursora i okna int move(y, x) int wmove(win, y, x) move() przesuwa kursor w stdscr ( wmove(win) ) Dla funkcji wejscia/wyjścia zdefiniowane są odpowiednie makra przesuwające kursor na podaną pozycję. int curs_set(bf) Jeżeli terminal na to pozwala włączy lub wyłączy ( niewidzialny ) kursor. void getyx(win, y, x) getyx(...) zwróci pozycję kursora. ( Uwaga: jest to makro. ) void getparyx(win, y, x) Gdy win jest podoknem getparyx(...) wypisze współrzędne okna względem jego rodzica. W innym przypadku y i x równają się -1. void getbegyx(win, y, x) void getmaxyx(win, y, x) int getmaxx(win) int getmaxy(win) Pobierają współrzędne początku ( getbegyx(...) ) i rozmiar ( getmaxyx(...) ) win. int getsyx(int y, int x) int setsyx(int y, int x) Zachowuje kursor wirtualnego ekranu w y i x. Ustawia kursor. Jeżeli wywołasz getsyx(...) z y i x = -1 leaveok zostanie włączone. Przewijanie int scrollok(win, bf) Jeżeli TRUE, text w okienku zostanie przewinięty jedną linię do góry gdy kursor będzie w dolnym, prawym rogu i zostanie wprowadzony znak lub nowa linia. Jeżeli FALSE, kursor zostanie na tej samej pozycji. Gdy włączone zawartość okna może być przesuwana za pomocą następujących funkcji. ( Uwaga: Będzie również przewijane gdy wpiszesz znak nowej linii w ostatniej linii okna. Bądź ostrożny z scrollok(...), inaczej uzyskasz niechciane efekty. ) int scroll(win) Przewinie do góry okno ( i linie w strukturze danych ) o jedną linię. int scrl(n) int wscrl(win, n) Funckje przewijają okno stdscr lub win do góry lub w dół zależnie od wartości n. Jeżeli n jest dodatnie okna będzie przesunięte do góry, w innym przypadku w dół. int setscrreg(t, b) int wsetscrreg(win, t, b) Ustawia obszar przewijania programowego. Następujący kod powinien wytłumaczyć sposób przewijania textu. Zobacz również przykład type.c. Mamy okienko z 18 liniami i 66 kolumnami i chcemi przewijać text zawarty w nim. S[] jest tablicą znaków zawierającą text. Max_s jest numerem ostatniej linii w s[]. Clear_line zapisuje pustkę od pozycji kursora do końca linii używając aktualnych atrybutów okna ( nie A_NORMAL jak robi to