NCurses
Ncurses je v Linuxu poměrně populární knihovna. Využívá ji mnoho programů,
ačkoliv v dnešní době dávají lidé přednost spíše grafickým udělátkům
před textovými. Mezi mé oblíbené ncurses programy patří například mc
,
vim
, vifm
nebo mp3blaster
.
V této kapitole se mimo jiné naučíte vykreslovat barvy, okna, psát na různé pozice na terminálu.
Úvod
Ncurses je knihovna, která vám umožní dělat grafické udělátka v textovém režimu. Ncurses znamená „new curses“, jde tedy o novou verzi starší curses knihovny.
Ncurses nabízí velkou sadu funkcí. Umí kreslit po celé obrazovce, měnit barvy, pracovat s myší, měnit režim klávesnice atp. Na první pohled se může zdát, že je tady hodně se co učit. A taky jsem si tentokrát připravil hodně příkladů. Ale nakonec zjistíte, že ovládnout ncurses nedá zase tak moc práce, protože rozhraní funkcí je jednoduché a blbuvzdorné :-).
Instalace a překlad
Jako obvykle je potřeba nainstalovat vývojové balíčky. A případně i dokumentaci. Tak třeba v Debianu najdete tyto balíčky:
- ncurses-doc
- Dokumentace, manuálové stránky. Bez toho se žádný dobrý programátor neobejde.
- libncurses5-dev
- Vývojový balíček ncurses (bez podpory UTF8 …)
- libncursesw5-dev
- Vývojový balíček ncures s podporou výceznakového kódování (např. UTF8). To je přesně balíček, který potřebujete.
V OpenSUSE místo balíčku libncurses5-dev a libncursesw5-dev najdete balíček ncurses-devel, který obsahuje obě verze.
Při překladu musíte uvést knihovnu ncurses
nebo ncursesw
.
Jestli chcete, aby se správně zobrazovala čeština (UTF-8), tak používejte jen
ncursesw
. Příklad:
Funkce jsou deklarované v hlavičkovém souboru <ncurses.h>. Cestu k němu
(volbu -I
) najde příkaz `pkg-config --cflags ncursesw`.
Více viz pkg-config.
V OpenSUSE se objevil bug,
kvůli kterému příkaz `pkg-config` pro ncurses(w) nefunguje. Zkuste místo něj použít
jednoduše volbu -lncurses
(-lncursesw
).
Ovládání klávesnice
První příklad vám představí některé funkce na ovládání režimu klávesnice. Jedná se o obdobu příkladu z kapitoly Terminál – knihovna termios.h.
Inicializace a ukonční Ncuress
Před použitím ncurses musíte inicializovat terminál zavoláním funkce
initscr()
. Po skončení práce s ncurses zase musíte
terminál „uklidit“ voláním endwin()
.
Tím se terminál vrátí do normálního režimu.
Struktura WINDOW
se používá k ukládání zdrojů pro okna,
která můžete v ncurses vytvářet. Funkce initscr()
vrací
odkaz na okno, které je také dostupné přes globální
proměnnou stdscr
(standardní screen).
Nebo vrací NULL, když selže.
Okno stdscr
reprezentuje celý terminál. Můžete si vytvářet
i menší okna, pokud chcete. O tom viz dále.
Funkce endwin()
vrací v případě úspěchu makro
OK
(což bude nejspíš nula), jinak ERR
.
Funkce initscr()
smaže celou obrazovku a nastaví tzv.
curses mód. V tomto módu nefungují tradiční funkce jako je
printf()
,
scanf()
atp. Ncurses používá své vlastní funkce pro IO
(inupt/output), jako jsou třeba printw()
, scanw()
.
a getch()
.
Funkce printw()
tiskne do okna stdscr
. Prvním
argumenem je formátovací řetězec (stejně jako u printf()
).
Funkce scanw()
je zase obdobou funkce scanf()
.
Funkce getch()
je obdobou funkce getchar()
.
Tyto funkce si nepleťte!
Pokud program skončí, aniž by zavolal endwin()
, bude obrazovka
v nepoužitelném stavu. (V takovém případě se ji můžete pokusit resetovat
příkazem reset
nebo tset
).
Abych se ujistil, že bude
endwin()
zavoláno, dal jsem ho do funkce obnovTerminal()
,
kterou registruji pomocí funkce atexit()
z knihovny
<stdlib.h>. Díky tomu bude funkce zavolána
po skončení programu návratem z funkce main()
, zavoláním
funkce exit()
i při ukončení programu klávesovou zkratkou
CTRL+c.
Pozor! Funkce pro zápis do oken zapisují pouze do bufferu okna (do paměti).
K tomu, aby se změny v bufferu vypsaly na obrazovku, se musí zavolat funkce
refresh()
.
Častou chybou začátečníků je opomenutí této funkce. Pak se diví, že se nic
nevypsalo.
Zápis do bufferu a následné vypsání všeho zapsaného pomocí refresh()
umožňuje ncurses výstup optimalizovat. To se hodilo na pomalých terminálech,
dnes se to stále hodí při práci přes internet.
Výše popsané najdete v každém programu, který jsem pro tuto kapitolu napsal.
Nastavení klávesnice
A teď k funkcím, které ovládají režim klávesnice.
Funkce noecho()
vypne zobrazování uživatelem
zadaných znaků na obrazovce. Funkce echo()
zobrazování
zase zapne. Obě funkce vracejí v případě chyby ERR
.
Funkce raw()
vypne generování signálů klávesovými
zkratkami, jako je CTRL+c. Funkce noraw()
to
zase zapne. Obě funkce vracejí v případě chyby ERR
.
Funkce raw()
taky vypne buffer pro vstup z klávesnice,
takže každá stisknutá klávesa je ihned předána programu.
Funkce cbreak()
a nocbreak()
dělají
to samé jako raw()
a noraw()
, jenom
nevypínají speciální význam zkratek, jako je CTRL+c.
Když jsem omylem použil funkce getchar()
místo getch()
,
a zavolal jsem funkci raw()
bez toho, abych předtím zavolal nocbreak()
, tak se mi nevracel
znak '\n'
. Proč tomu tak je nevím. Každopádně, používat s ncurses getchar()
místo getch()
je chyba.
- /*------------------------------------------------*/
- /* 51ncurses/terminal.c */
- #include <ncurses.h>
- #include <unistd.h>
- #include <stdlib.h>
- {
- printw("Obnovuji nastavení terminálu\n");
- refresh();
- sleep(1);
- endwin();
- }
- {
- char ch;
- initscr();
- noecho();
- //nocbreak();
- raw();
- /* nezapomenout na obnoveni nastaveni terminalu! */
- printw("Zadávání znaků ukončíte klávesou Enter:\n");
- refresh();
- do {
- printw("ch = %c\n", ch);
- refresh();
- return EXIT_SUCCESS;
- }
- /*------------------------------------------------*/
Překlad:
Výstup z programu může vypadat takto:
Zadávání znaků ukon?~Míte klávesou Enter: ch = S ch = a ch = l ch = l ch = y ch = x ch = ^C ch = ^C ch = Obnovuji nastavení terminálu
Po jedné vteřině po stisku enteru celý výstup zmizí (obrazovka se vrátí
do normálního režimu zavoláním funkce obnovTerminal()
).
Možná by vás také mohla zajímat funkce halfdelay()
,
která nastavuje mód jako cbreak()
, ale blokuje jen
zadaný počet desetin sekundy. Když uživatel včas nic nestiskne,
funkce přestane čekat na vstup, vrátí chybu a program jede dál.
Viz manuálové stránky.
Kódování
Jak vidíte v příkladu výše, ne všechny písmenka se zobrazují správně. Od toho je tu
knihovna ncursesw
. Aby tato verze ncurses fungovala správně,
musíte nastavit locale.
Přidejte do předchozího zdrojového kódu hlavičkový soubor
<locale.h>
a do funkce main()
tuto řádku:
Překlad:
A teď už se budou vypisovat UTF-8 znaky správně.
Ale aby to nebylo tak jednoduchý, tak funkce getch()
bude pořád vracet jeden bajt (přetypovaný na int), což znamená,
že výcebajtové znaky UTF-8 bude vracet po částech.
Porvat se s tím dá obdobně, jako je popsáno v kapitole Konverze znakových sad.
Ncursesw nabízí novou sadu funkcí pro práci s širokými znaky.
Jejich seznam najdete v manuálové stránce k ncurses (man ncurses
).
Namátkou:
Funkce getn_wstr()
pracuje, bohužel, s polem typu wint_t
namísto
wchar_t
. To je bug,
se kterým jsou všichni happy, takže ho nikdo neopraví. V Linuxu platí, že
sizeof(wint_t) == sizeof(wchar_t), proto to (zatím) není problém. Stačí, když
argument přetypujete z (wchar_t *)
na (wint_t *)
.
(Ostatní funkce na práci s širokými znaky, jako je třeba wcscmp()
,
používají (správně) wchar_t *
.
Pokud tyto funkce budete používat, musíte použít hlavičkový soubor <ncursesw/curses.h> namísto <ncurses.h>.
Jako příklad vám může posloužit program znamky. Je to jeden z prvních programů, které jsem psal, takže to není úplně učebnicový zdrojový kód (zvlášť práce s hlavičkovými soubory je zmatená, program neraguje na WINCH atp.). Ale je to docela jednoduchý a funkční program, který pracuje s ncurses a UTF-8 znaky. Taky si zopakujete práci s DBM :-).
Barvy
První, co musíte udělat, pokud chcete používat barvy, je, že zavoláte
funkci has_colors()
, která zjišťuje, jestli terminál barvy
podporuje. Dneska už asi těžko narazíte na terminál, který barvy nepodporuje,
ale je to slušnost.
Další funkci, kterou zavoláte, je start_color()
,
kterou si ncruses inicializuje potřebné struktury, které při práci s barvami
potřebuje.
Tyto funkce, spolu s dalšími, které volám v každém příkladu, jsem přesunul
do funkce initNcurses()
, aby byly příklady trochu přehlednější.
Ncurses poskytuje několik užitečných maker:
- COLS
- Počet sloupců v terminálu (kolik znaků se vejde do jedné řádky)
- LINES
- Počet řádek
- COLORS
- Maximální počet barev, který terminál zvládne. Z historických důvodů je jich často k dispozici jen 8.
- COLOR_PAIR
- Maximální počet barevných párů. Měl by jich být alespoň 64, tedy 8x8. Barevný pár je dvojice barev popředí a pozadí.
S ncurses nenastavujete barvu popření a pozadí zvlášť, ale vždycky
si musíte nejdřív vytvořit barevný pár (popředí + pozadí) pomocí funkce init_pair()
.
Barevný pár se aktivuje funkcí attrset()
, ale nepředává se přímo jako
číslo inicializované funkcí init_pair()
, nýbrž se předává pomocí makra
COLOR_PAIR()
.
Proč je tomu tak, to se můžeme jen domnívat :-).
Funkcí attrset()
se nastasvují ještě další vlastnosti terminálu, jako
je třeba A_BOLD
, který nastavuje zvýrazněné písmo. K nastavování
atributů se ještě vrátím podrobněji v dalším příkladu.
Atribut, jako je A_BOLD
, se dá vypnout pomocí funkce attroff()
.
Funkce init_pair()
vytvoří barevný pár s barvou popředí f
a barvou pozadí b. Nově vytvořený barevný pár bude dostupný pod
číslem pair
.
Ncurses definuje 8 základních barev, které můžete pro popředí a pozadí použít. Najdete je v <ncurses.h>.
#define COLOR_BLACK 0
#define COLOR_RED 1
#define COLOR_GREEN 2
#define COLOR_YELLOW 3
#define COLOR_BLUE 4
#define COLOR_MAGENTA 5
#define COLOR_CYAN 6
#define COLOR_WHITE 7
Existují sice funkce, které vám umožní namýchat si téměř libovolný barevný odstín, ale co jsem je zkoušel, tak moc nefungují. Terminály to bohužel nepodporují. Takže si musíte vystačit s těmito 8 barvami.
Zatímco funkce attrset()
nastaví terminál tak, aby byly aktivní
jen zadané volby, funkce attron()
jenom zapne předané
atributy a ostatní nechá neovlivněné a attroff()
jen vypne
předané atributy.
V příkladu také nově používám funkci mvprintw()
. Tiskne
na obrazovku stejně jako printw()
, jenom před tiskem
posune kurzor na zadanou pozici. Stejného efektu docílíte zavoláním
funkce move()
a následně printw()
.
Argument y je řádka a argument x je sloupec, kam se kurzor přesune.
Teď už by mělo být v příkladu téměř všechno jasné. Na začátku jsou funkce pro inicializaci a ukonční ncurses.
- /*------------------------------------------------*/
- /* 51ncurses/colors.c */
- #include <stdio.h>
- #include <ncurses.h>
- #include <locale.h>
- #include <stdlib.h>
- #include <unistd.h>
- #define C_LEN 8
- {
- printw("Obnovuji nastavení terminálu\n");
- endwin();
- }
- initscr();
- noecho();
- raw();
- }
- }
- }
Ve funkci main()
jsem si vytvořil pole všech barev
a pak jej postupně ve dvou cyklech procházím, vytvářím barevný
páry (každou barvu s každou) a tisknu na obrazovku.
Normálně bych si pro barevné páry vytvořil nějaký výčtový typ, kde bych si barevné páry pojmenoval jako modro_zelena, nebo, lépe, barva_horni_menu atp.
Tentokrát ale každý barevný pár použiji jen jednou, takže si vystačím s proměnnou pair.
- {
- COLOR_BLACK,
- COLOR_RED,
- COLOR_GREEN,
- COLOR_YELLOW,
- COLOR_BLUE,
- COLOR_MAGENTA,
- COLOR_CYAN,
- COLOR_WHITE
- };
- initNcurses();
- printw("Max počet barev = %i\n", COLORS); //8
- printw("Max počet barevných párů = %i\n", COLOR_PAIR); //64
- printw("Sloupců x Řádků = %i x %i\n", COLS, LINES);
- pair = 1;
- init_pair(pair, colors[i], colors[j]);
- attroff(A_BOLD);
- attrset(COLOR_PAIR(pair));
- mvprintw(i * 2 + 4, j * 12, "normal %i/%i", colors[i],
- colors[j]);
- attrset(COLOR_PAIR(pair) | A_BOLD);
- mvprintw(i * 2 + 5, j * 12, "bold %i/%i", colors[i],
- colors[j]);
- refresh();
- usleep(1000 * 250);
- pair++;
- }
- }
Nezapomeňte na volání funkce refresh()
.
Zkuste si ze zdrojového souboru odstranit pair++;
.
Co myslíte, že se stane? :-)
Defaultní barvy
Na konci zdrojového souboru ještě nastavím defaultní barvy a vypíšu
text Pokračujte stiskem libovolné klávesy ....
K nastavení defaultních barev je potřeba zavolat
funkci use_default_colors()
, po jejímž zavolání
funkce init_pair()
interpretuje hodnotu barvy -1
jako defaultní barvu (popředí i pozadí).
Defaultní barvy můžou být jakékoliv, jaké si uživatel ve svém terminálu
nastaví. Proto byste neměli volat init_pair()
jinak, než
s -1 pro obě složky. Mohlo by se totiž stát, že náhodou vyberete třeba
barvu popředí stejnou, jako si vybral uživatel (defaultní) barvu pozadí a text se stane
nečitelný.
Výsledek vypadá takto:
Text je na začátku a na konci zelený, protože jsem měl nastavený v terminálu takový barevný profil. Všiměte si, jak některý text není čitelný, protože jde o stejnou barvu popředí i pozadí.
Zkuste si nastavit TERM na vt100. To by mělo způsobit, že terminál nebude podporovat barvy. Uvidíte, jak se bude program chovat.
$ ./attrs
Terminál nepodporuje barvy
$
Existuje ještě funkce assume_default_colors()
, která mění
defaultní barvu textu popředí a pozadí. To se hodí například tehdy, když
chcete překreslit celý terminál pomocí funkce erase()
.
Funkce erase()
překreslý celý termnál mezerami s nastavenou
defaultní barvou. Nejjednoduší způsob jak přebarvit celé okno je nastavit
defaultní barvy (důležitá je vlastně jen barva pozadí) a zavolat erase()
.
Viz následující příklad.
Nastavování atributů
V předchozí části jsem představil základní funkce na nastavování
atributů písma a barvy (attrset()
, attron()
a attrof()
).
Zde uvidíte příklad dalších atributů, které ncurses poskytuje.
Atribut | Význam | Poznámka |
---|---|---|
A_BOLD | Zvýraznění textu | |
A_BLINK | Blikání | Funguje jen v KDE konzoli a xtermu. V některém terminálu se chová podobně, jako A_REVERZE |
A_DIM | Ztmavení textu | Nefunguje, nic neudělá. |
A_REVERSE | Reverzní barvy | |
A_UNDERLINE | Podtržený text | |
COLOR_PAIR(i) | Barevný pár i |
Žádný z terminálů, resp. terminálových emulátorů, které jsem zkoušel, nijak nezměnili písmo s nastaveným atributem A_DIM. A_BLINK funguje jen v málo terminálech. Třeba v KDE konzoli si můžete zaškrtnout v nastasvení, jestli má být blikání povoleno.
Zdrojový kód je, myslím, vcelku přímočarý:
- /*------------------------------------------------*/
- /* 51ncurses/attrs.c */
- #include <stdio.h>
- #include <ncurses.h>
- #include <locale.h>
- #include <stdlib.h>
- #include <unistd.h>
- {
- printw("Obnovuji nastavení terminálu\n");
- endwin();
- }
- initscr();
- noecho();
- raw();
- endwin();
- }
- endwin();
- }
- }
- {
- initNcurses();
- /* nastaveni barvy pozadi a prekresleni celeho okna */
- init_pair(color_pair, COLOR_RED, COLOR_YELLOW);
- attrset(COLOR_PAIR(color_pair));
- assume_default_colors(COLOR_RED, COLOR_YELLOW);
- erase();
- /* vypis zkusebnich textu */
- l = LINES / 2 - 3;
- c = COLS / 2 - 13;
- attron(A_BOLD);
- mvprintw(l++, c, "Zkušební text A_BOLD");
- attroff(A_BOLD);
- attron(A_BLINK);
- mvprintw(l++, c, "Zkušební text A_BLINK");
- attroff(A_BLINK);
- attron(A_DIM);
- mvprintw(l++, c, "Zkušební text A_DIM");
- attroff(A_DIM);
- attron(A_REVERSE);
- mvprintw(l++, c, "Zkušební text A_REVERSE");
- attroff(A_REVERSE);
- attron(A_UNDERLINE);
- mvprintw(l++, c, "Zkušební text A_UNDERLINE");
- attroff(A_UNDERLINE);
- l++;
- mvprintw(l++, c - 5, "Pokračujte stiskem libovolné klávesy ...");
- refresh();
- }
- /*------------------------------------------------*/
Překlad:
Výsledek pak může vypadat takto:
Okna
Doteď jste pracovali s jedním oknem, které je dostupné pod globální proměnnou stdscr. A to je taky vše, s čím byste si mohli vystačit.
Okno stdscr zabírá celou obrazovku.
Ncurses umožňuje vytvářet další okna, která mohou zabírat jen část obrazovky.
Nové okno si alokuje vlastní paměť, takže co do něj zapíšete neovlivní obsah
jiného okna. O tom co uvidíte na obrazovce rozhoduje které okno naposledy
necháte vykreslit zavoláním funkce wrefresh()
.
Jinak řečeno, okno (se kterým se pracuje přes odkaz na strukturu WINDOW
) je
jen datová struktura, která si udržuje informace o nastavených atributech,
umístění kurzoru a
textu, který jste do okna zapsali. Pomocí funkce wrefresh()
se potom
obsah okna vypisuje na obrazovku.
Nové okno vytváří funkce newwin()
. Smazat ho můžete funkcí
delwin()
.
Parametry begin_y a begin_x určují souřadnice
levého horního rohu okna. Dejte si pozor na to, aby se celé okno
vešlo do obrazovky. Jinak se nevykreslí! To znamená, že begin_y
a begin_x musí být větší nebo rovno nule a musí platit, že
nlines + begin_y >= LINES
a
ncols + begin_x >= COLS
.
Pokud vytvoříte okno mimo obrazovku, tak se nic nestane, jen se nevykreslí. Ikdyž přesahuje mimo obrazovku jen kousek, stejně se nevykreslí vůbec nic.
Jak už jsem psal, pro vykreslení obsahu okna na obrazovku je nezbytné
zavolat funkci wrefresh()
. Ta má jako argument odkaz na
strukturu WINDOW
. Funkce refresh()
je vlastně
synonymum pro wrefresh(stdscr);
. Vlastně všechny funkce,
které pracují s okny mají svojí verzi s i bez „w“. Namátkou:
int refresh(void);
int wrefresh(WINDOW *win);
// zápis
int printw(const char *fmt, ...);
int wprintw(WINDOW *win, const char *fmt, ...);
int mvprintw(int y, int x, const char *fmt, ...);
int mvwprintw(WINDOW *win, int y, int x, const char *fmt, ...);
// čtení
int scanw(char *fmt, ...);
int wscanw(WINDOW *win, char *fmt, ...);
int mvscanw(int y, int x, char *fmt, ...);
int mvwscanw(WINDOW *win, int y, int x, char *fmt, ...);
// přesun kurozru
int move(int y, int x);
int wmove(WINDOW *win, int y, int x);
Vysvětlovat parametry asi nemusím. Třeba funkce wscanw()
bude načítat standardní vstup a pokud není vypnuté echo, tak bude
vypisovat vstup na pozici kurzoru zadaného okna. Funkce mvwscanw()
dělá to samé, jen nejdříve přesune kurzor na zadané souřadnice v zadaném
oknu. Tak jsem to vysvtětlil. No nic.
V příkladu používám novou funkci, curs_set()
, která nastavuje,
jakým způsobem se bude zobrazovat kurzor.
Parametr visibility může být 0 = neviditelný, 1 = normální a 2 = zvýrazněný. Rozdíl mezi normálním a zvýrazněným jsem nezaznamenal. Asi zase jedna z vymožeností, která funguje jen na některých exotických terminálech.
Další nová funkce použitá v následujícím příkladu je funkce box()
.
Ta vykreslí kolem okna rámeček ze zadaných znaků pro svislou a vodorovnou
část rámu. Pokud byste chtěli nad rámečkem větší kontrolu, nastujdujte
si funkci wborder()
.
Velmi důležitou novinkou je funkce touchwin()
.
Ncurses si udržuje informaci o tom, které části okna už byli vykreslené
a které se změnili od posledního volání wrefresh()
.
Při zavolání wrefresh()
vykreslí jen ty části okna, které
se změnili (například voláním funkce wprintw()
, werase()
,
box()
atp.).
Tyto infomrace se ukládají ve struktuře okna. Pokud máte na obrazovce překrývající
se okna a chcete vykreslit okno, které bylo překresleno jiným oknem, tak
o tom ve struktuře WINDOW nemáte žádné info. Ncurses si tak myslí, že se v okně nic
nezměnilo a nemusí nic kreslit. Funkce touchwin()
se okna
„dotkne“, takže si ncurses bude myslet, že se celé změnilo a celé
ho znovu vykreslí. V příkladu to uvidíte v praxi.
Poslední novinkou je volání funkce keypad(stdscr, TRUE);
, které
zapne „keypad“ režim. V tomto režimu funkce wgetch()
při stisku funkční klávesy (jako je F1, nebo šipky) vrací jedno číslo, pro které
ncurses definuje makra, jako je KEY_LEFT, KEY_F(i), KEY_BACKSPACE atd.
Celý seznam najdete v souboru /usr/include/ncurses.h
.
Pokud je „keypad“ režim vypnutý (což defaultně je), tak
vrací wgetch()
escape sekvenci reprezentující stisknutou funkční
klávesu. A s tím se pracuje blbě.
V příkladu používám funkci getch()
, tedy alias pro wgetch(stdscr)
,
proto volám keypad()
s arugmentem stdscr. (Vzhledem k tomu,
že mám vypnutý echo režim, tak je vlastně jedno, z jakého okna čtu.)
A teď už se můžete podívat na příklad. Vykreslím okno uprostřed obrazovky,
které můžete pomocí kláves posouvat. Napsal jsem si pomocnou funkci
wpozadi()
, která vyplní okno tečkami (čímž taky změní
barvu jeho pozadí). Můžete příklad upravit tak, aby místo teček používal
mezery, nebo zkusit trik s assume_default_colors()
a werase()
.
- /*------------------------------------------------*/
- /* 51ncurses/window.c */
- #include <stdio.h>
- #include <ncurses.h>
- #include <locale.h>
- #include <stdlib.h>
- #include <unistd.h>
- #define O_RADKU 5
- #define O_SLOUPCU 50
- {
- printw("Obnovuji nastavení terminálu\n");
- endwin();
- }
- initscr();
- noecho();
- raw();
- endwin();
- }
- endwin();
- }
- }
- {
- mvwprintw(okno, i, j, "%c", '.');
- wmove(okno, 0, 0);
- }
- {
- int ch;
- WINDOW *okno;
- initNcurses();
- curs_set(0);
- /* nastaveni barvy pozadi a prekresleni cele obrazovky */
- assume_default_colors(COLOR_RED, COLOR_YELLOW);
- erase();
- refresh();
- line = LINES / 2 - 3;
- col = COLS / 2 - 25;
- okno = newwin(O_RADKU, O_SLOUPCU, line, col);
- /* nastaveni okna */
- init_pair(pair_okno, COLOR_RED, COLOR_WHITE);
- wattrset(okno, COLOR_PAIR(pair_okno));
- wpozadi(okno, O_RADKU, O_SLOUPCU);
- //box(okno,'|','-');
- box(okno, ACS_VLINE, ACS_HLINE);
- wprintw(okno, "Nové okno");
- mvwprintw(okno, 2, 2, "Ahoj světe!");
- wattron(okno, A_BOLD);
- mvwprintw(okno, 4, 1, " Enter = konec, c = clear, sipky = posun okna ");
- keypad(stdscr, TRUE);
- ch = '\0';
- do {
- wrefresh(okno);
- case KEY_LEFT:
- col--;
- mvwin(okno, line, col);
- }
- case KEY_RIGHT:
- col++;
- mvwin(okno, line, col);
- case KEY_UP:
- line--;
- mvwin(okno, line, col);
- }
- case KEY_DOWN:
- line++;
- mvwin(okno, line, col);
- touchwin(stdscr);
- wrefresh(stdscr);
- }
- }
- /*------------------------------------------------*/
Výsledek může vypadat takto:
Pokud zmáčknete šipku, okno se posune a vykreslí na nové pozici.
Nevolám nic jiného, než wrefresh(okno);
, proto se nevykreslí na obrazovce nic jiného,
než toto okno (na nové pozici) a na obrazovce zůstávají „duchové“.
Funkce mvwin()
označí celé okno k překreslení,
takže není nutné volat touchwin()
.
Když stisknete klávesu c, tak se dotknu pozadí (struktura
strdscr
se nijak neměnila, nic jsem do ní nezapisoval, takže se musím
dotknout) a překreslím ho. Okna jsem se ale nedotknul a nijak ho nezměnil,
proto volání wrefresh(okno);
nic neudělá. Výsledkem je
prázdná žlutá obrazovka.
Do třetice je zajímavé, co se stane, když posunete okno mimo obrazovku. Zdánlivě nic. Okno se nehne, protože ncurses ho odmítne mimo obrazovku překreslit. Ale změní se hodnota proměnné line nebo col. Takže kolikrát jste posunuli okno mimo obrazovku, tolikrát ho zese musíte posunout zpět, aby se začalo hýbat.
Aby se okno hýbalo bez duchů, museli byste nejdříve překreslit pozadí. Tedy zavolat funkce nějak takto:
wrefresh(stdscr);
// ted je prazdna (zluta) obrazovka
mvwin(okno, line, col);
wrefresh(okno);
// okno se vykreslilo na nove pozici
To ale není úplně efektivní řešení. Část pod oknem se totiž vykreslí dvakrát. Nejdříve žluté pozadí, které je následně překresleno obsahem okna. Na terminálu vám to proběhne tak rychle, že si ničeho nevšimnete, ale při práci po síti by vám to mohlo trochu blikat.
Můžete použít optimálnější postup:
wnoutrefresh(stdscr);
// ted je v bufferu prazdna (zluta) obrazovka
// terminal se nijak nezmenil
mvwin(okno, line, col);
wnoutrefresh(okno);
// ted je v bufferu vykreslene okno na nove pozici
// terminal se porad nijak nezmenil
doupdate();
// ted se vysledek vykreslil na terminal
Funkce wnoutrefresh()
překreslí okno do bufferu (ne na terminál,
takže třeba při práci po síti se ještě nikam nic neposílá). Funkce
doupdate()
pak výsledek vypíše na terminál.
Funkce wrefresh()
je vlastně alias pro volání funkcí
wnoutrefresh()
a doupdate()
.
V další kapitole se dostane na některé složitější funkce. Proto si nejdříve pořádně prostudujte tuto kapitolu, ať v tom pak nemáte zmatek.