"f(%d)\n", arg);
x = arg;
x++;
}
int main(int ac, char *av[]){
h();
f(1);
g();
printf("x=%d y=%d\n", x, y);
return 0;
}
--------
fajl b.c
--------
#include "header.h"
void g(){
y = f(5);
}
--------
fajl c.c
--------
#include "header.h"
void h(){
f();
}
Popytka kompilyacii:
abs@wizard$ cc a.c b.c c.c -o aaa
a.c:
b.c:
"b.c", line 4: operand cannot have void type: op "="
"b.c", line 4: assignment type mismatch:
int "=" void
cc: acomp failed for b.c
c.c:
"c.c", line 4: prototype mismatch: 0 args passed, 1 expected
cc: acomp failed for c.c
A. Bogatyrev, 1992-95 - 348 - Si v UNIX
8. |krannye biblioteki i rabota s videopamyat'yu.
Terminal v UNIX s tochki zreniya programm - eto fajl. On predstavlyaet soboj dva
ustrojstva: pri zapisi write() v etot fajl osushchestvlyaetsya vyvod na ekran; pri chtenii
read()-om iz etogo fajla - chitaetsya informaciya s klaviatury.
Sovremennye terminaly v opredelennom smysle yavlyayutsya ustrojstvami pryamogo dos-
tupa:
- informaciya mozhet byt' vyvedena v lyuboe mesto ekrana, a ne tol'ko posledovatel'no
stroka za strokoj.
- nekotorye terminaly pozvolyayut prochest' soderzhimoe proizvol'noj oblasti ekrana v
vashu programmu.
Tradicionnye terminaly yavlyayutsya samostoyatel'nymi ustrojstvami, obshchayushchimisya s komp'yu-
terom cherez liniyu svyazi. Protokol|- obshcheniya obrazuet sistemu komand terminala i mozhet
byt' razlichen dlya terminalov raznyh modelej. Poetomu biblioteka raboty s tradicionnym
terminalom dolzhna reshat' sleduyushchie problemy:
- nastrojka na sistemu komand dannogo ustrojstva, chtoby odna i ta zhe programma
rabotala na raznyh tipah terminalov.
- emulyaciya nedostayushchih v sisteme komand; maksimal'noe ispol'zovanie predostavlen-
nyh terminalom vozmozhnostej.
- mimnimizaciya peredachi dannyh cherez liniyu svyazi (dlya uskoreniya raboty).
- bylo by polezno, chtoby biblioteka predostavlyala pol'zovatelyu nekotorye logiches-
kie abstrakcii, vrode OKON - pryamougol'nyh oblastej na ekrane, vedushchih sebya
podobno malen'kim terminalam.
V UNIX eti zadachi reshaet standartnaya biblioteka curses (a tol'ko pervuyu zadachu -
bolee prostaya biblioteka termcap). Dlya nastrojki na sistemu komand konkretnogo disp-
leya eti biblioteki schityvayut opisanie sistemy komand, hranyashcheesya v fajle
/etc/termcap. Krome nih byvayut i drugie ekrannye biblioteki, a takzhe sushchestvuyut inye
sposoby raboty s ekranom (cherez videopamyat', sm. nizhe).
V zadachah dannogo razdela vam pridetsya pol'zovat'sya bibliotekoj curses. Pri kom-
pilyacii programm eta biblioteka podklyuchaetsya pri pomoshchi ukazaniya klyucha -lcurses, kak
v sleduyushchem primere:
cc progr.c -Ox -o progr -lcurses -lm
Zdes' podklyuchayutsya dve biblioteki: /usr/lib/libcurses.a (rabota s ekranom) i
/usr/lib/libm.a (matematicheskie funkcii, vrode sin, fabs). Klyuchi dlya podklyucheniya
bibliotek dolzhny byt' zapisany v komande SAMYMI POSLEDNIMI. Zametim, chto standartnaya
biblioteka yazyka Si (soderzhashchaya sistemnye vyzovy, biblioteku stdio (funkcii printf,
scanf, fread, fseek, ...), raznye chasto upotreblyaemye funkcii (strlen, strcat, sleep,
malloc, rand, ...)) /lib/libc.a podklyuchaetsya avtomaticheski i ne trebuet ukazaniya
klyucha -lc.
V nachale svoej programmy vy dolzhny napisat' direktivu
#include <curses.h>
podklyuchayushchuyu fajl /usr/include/curses.h, v kotorom opisany formaty dannyh, ispol'zue-
myh bibliotekoj curses, nekotorye predopredelennye konstanty i.t.p. (eto nado, chtoby
vasha programma pol'zovalas' imenno etimi standartnymi soglasheniyami). Posmotrite v
etot fajl!
Kogda vy pol'zuetes' curses-om, vy NE dolzhny pol'zovat'sya funkciyami standartnoj
biblioteki stdio dlya neposredstvennogo vyvoda na ekran; tak vy ne dolzhny pol'zovat'sya
____________________
|- Pod protokolom v programmirovanii podrazumevayut ryad soglashenij dvuh storon (ser-
vera i klientov; dvuh mashin v seti (kstati, termin dlya oboznacheniya mashiny v seti -
"host" ili "site")) o formate (pravilah oformleniya) i smysle dannyh v peredavaemyh
drug drugu soobshcheniyah. Analogiya iz zhizni - chelovecheskie rech' i yazyk. Rech' vseh lyu-
dej sostoit iz odnih i teh zhe zvukov i mozhet byt' zapisana odnimi i temi zhe bukvami
(a dannye - bajtami). No esli dva cheloveka govoryat na raznyh yazykah - t.e. po-
raznomu konstruiruyut frazy i interpretiruyut zvuki - oni ne pojmut drug druga!
A. Bogatyrev, 1992-95 - 349 - Si v UNIX
funkciyami printf, putchar. |to proishodit potomu, chto curses hranit v pamyati pro-
cessa kopiyu soderzhimogo ekrana, i esli vy vyvodite chto-libo na ekran terminala obhodya
funkcii biblioteki curses, to real'noe soderzhimoe ekrana i poziciya kursora na nem
perestayut sootvetstvovat' hranimym v pamyati, i biblioteka curses nachnet vyvodit' nep-
ravil'noe izobrazhenie.
PROGRAMMA
| |
| CURSES---kopiya ekrana
| printw,addch,move
| |
V V
biblioteka STDIO --printf,putchar----> ekran
Takim obrazom, curses yavlyaetsya dopolnitel'nym "sloem" mezhdu vashej programmoj i stan-
dartnym vyvodom i ignorirovat' etot sloj ne sleduet.
Napomnim, chto izobrazhenie, sozdavaemoe pri pomoshchi biblioteki curses, snachala
formiruetsya v pamyati programmy bez vypolneniya kakih-libo operacij s ekranom displeya
(t.e. vse funkcii wmove, waddch, waddstr, wprintw izmenyayut tol'ko OBRAZY okon v
pamyati, a na ekrane nichego ne proishodit!). I lish' tol'ko POSLE togo, kak vy vyzo-
vete funkciyu refresh() ("obnovit'"), vse izmeneniya proisshedshie v oknah budut otobra-
zheny na ekrane displeya (takoe odnovremennoe obnovlenie vseh izmenivshihsya chastej
ekrana pozvolyaet provesti ryad optimizacij). Esli vy zabudete sdelat' refresh - ekran
ostanetsya neizmennym. Obychno etu funkciyu vyzyvayut pered tem, kak zaprosit' u pol'zo-
vatelya kakoj-libo vvod s klaviatury, chtoby pol'zovatel' uvidel tekushchuyu "svezhuyu" kar-
tinku. Hranenie soderzhimogo okon v pamyati programmy pozvolyaet ej schityvat' soderzhi-
moe okon, togda kak bol'shinstvo obychnyh terminalov ne sposobny vydat' v komp'yuter
soderzhimoe kakoj-libo oblasti ekrana.
Obshchenie s terminalom cherez liniyu svyazi (ili voobshche cherez posledovatel'nyj proto-
kol) yavlyaetsya dovol'no medlennym. Na personal'nyh komp'yuterah sushchestvuet drugoj spo-
sob raboty s ekranom: cherez pryamoj dostup v tak nazyvaemuyu "videopamyat'" - special'-
nuyu oblast' pamyati komp'yutera, soderzhimoe kotoroj apparatno otobrazhaetsya na ekrane
konsoli. Rabota s ekranom prevrashchaetsya dlya programmista v rabotu s etim massivom
bajt (zapis'/chtenie). Programmy, pol'zuyushchiesya etim sposobom, prosty i rabotayut ochen'
bystro (ibo dostup k pamyati cherezvychajno bystr, i sdelannye v nej izmeneniya "proyavlya-
yutsya" na ekrane pochti mgnovenno). Nedostatok takih programm - privyazannost' k konk-
retnomu tipu mashiny. |ti programmy nemobil'ny i ne mogut rabotat' ni na obychnyh ter-
minalah (podklyuchaemyh k linii svyazi), ni na mashinah s drugoj strukturoj videopamyati.
Vybor mezhdu "tradicionnoj" rabotoj s ekranom i pryamym dostupom (fakticheski - mezhdu
mobil'nost'yu i skorost'yu) - vopros principial'nyj, tem ne menee prinyatie resheniya
zavisit tol'ko ot vas. Videopamyat' IBM PC v tekstovom rezhime 80x25 16 cvetov imeet
sleduyushchuyu strukturu:
struct symbol{ /* IBM PC family */
char chr; /* kod simvola */
char attr; /* atributy simvola (cvet) */
} mem[ 25 ] [ 80 ]; /* 25 strok po 80 simvolov */
Struktura bajta atributov:
-------------------------------------------
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | # bita
------------------|------------------------
|blink| R | G | B | intensity | r | g | b | cvet
------------------|------------------------
background (fon) | foreground (cvet bukv)
R - red (krasnyj) G - green (zelenyj) B - blue (sinij)
blink - mercanie bukv (ne fona!)
intensity - povyshennaya yarkost'
A. Bogatyrev, 1992-95 - 350 - Si v UNIX
Koordinatnaya sistema na ekrane: verhnij levyj ugol ekrana imeet koordinaty (0,0), os'
X gorizontal'na, os' Y vertikal'na i napravlena sverhu vniz.
Cvet simvola poluchaetsya smeshivaniem 3h cvetov: krasnogo, zelenogo i sinego
(elektronno-luchevaya trubka displeya imeet 3 elektronnye pushki, otvechayushchie etim cve-
tam). Krome togo, dopustimy bolee yarkie cveta. 4 bita zadayut kombinaciyu 3h osnovnyh
cvetov i povyshennoj yarkosti. Obrazuetsya 2**4=16 cvetov:
I R G B nomer cveta
BLACK 0 0 0 0 0 chernyj
BLUE 0 0 0 1 1 sinij
GREEN 0 0 1 0 2 zelenyj
CYAN 0 0 1 1 3 cianovyj (sero-goluboj)
RED 0 1 0 0 4 krasnyj
MAGENTA 0 1 0 1 5 malinovyj
BROWN 0 1 1 0 6 korichnevyj
LIGHTGRAY 0 1 1 1 7 svetlo-seryj (temno-belyj)
DARKGRAY 1 0 0 0 8 temno-seryj
LIGHTBLUE 1 0 0 1 9 svetlo-sinij
LIGHTGREEN 1 0 1 0 10 svetlo-zelenyj
LIGHTCYAN 1 0 1 1 11 svetlo-cianovyj
LIGHTRED 1 1 0 0 12 yarko-krasnyj
LIGHTMAGENTA 1 1 0 1 13 yarko-malinovyj
YELLOW 1 1 1 0 14 zheltyj
WHITE 1 1 1 1 15 (yarko)-belyj
Fizicheskij adres videopamyati IBM PC v cvetnom alfavitno-cifrovom rezhime (80x25,
16 cvetov) raven 0xB800:0x0000. V MS DOS ukazatel' na etu pamyat' mozhno poluchit' pri
pomoshchi makrosa make far pointer: MK_FP (eto dolzhen byt' far ili huge ukazatel'!). V
XENIX|- ukazatel' poluchaetsya pri pomoshchi sistemnogo vyzova ioctl, prichem sistema pre-
dostavit vam virtual'nyj adres, ibo privelegiya raboty s fizicheskimi adresami v UNIX
prinadlezhit tol'ko sisteme. Rabotu s ekranom v XENIX vy mozhete uvidet' v primere
"osypayushchiesya bukvy".
8.1.
/*#! /bin/cc fall.c -o fall -lx
* "Osypayushchiesya bukvy".
* Ispol'zovanie videopamyati IBM PC v OS XENIX.
* Dannaya programma illyustriruet dostup k ekranu
* personal'nogo komp'yutera kak k massivu bajt;
* vse izmeneniya v massive nemedlenno otobrazhayutsya na ekrane.
* Funkciya nap() nahoditsya v biblioteke -lx
* Pokazana takzhe rabota s portami IBM PC pri pomoshchi ioctl().
*/
#include <stdio.h>
#include <fcntl.h> /* O_RDWR */
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/at_ansi.h>
#include <sys/kd.h> /* for System V/4 and Interactive UNIX only */
/*#include <sys/machdep.h> for XENIX and SCO UNIX only */
#include <sys/sysmacros.h>
____________________
|- XENIX - (proiznositsya "ziniks") versiya UNIX dlya IBM PC, pervonachal'no razrabo-
tannaya firmoj Microsoft i postavlyaemaya firmoj Santa Cruz Operation (SCO).
A. Bogatyrev, 1992-95 - 351 - Si v UNIX
#ifdef M_I386
# define far /* na 32-bitnoj mashine far ne trebuetsya */
#endif
char far *screen; /* videopamyat' kak massiv bajt */
/* far - "dlinnyj" (32-bitnyj) adres(segment,offset) */
int segm; /* segment s videopamyat'yu */
#define COLS 80 /* chislo kolonok na ekrane */
#define LINES 25 /* chislo strok */
#define DELAY 20 /* zaderzhka (millisekund) */
int ega; /* deskriptor dlya dostupa k drajveru EGA */
/* struktura dlya obmena s portami */
static struct port_io_struct PORT[ 4 /* ne bolee 4 za raz */] = {
/* operaciya nomer porta dannye */
/* .dir .port .data */
/* Pereustanovit' flip/flop:
* zastavit' port 0x3C0 ozhidat' pary adres/znachenie
* pri posledovatel'noj zapisi bajtov v etot port.
*/
{ IN_ON_PORT, 0x3DA, -1 },
/* IN-chtenie */
/* Teper' 3c0 ozhidaet pary adres/znachenie */
{ OUT_ON_PORT, 0x3C0, -1 /* adres */ },
{ OUT_ON_PORT, 0x3C0, -1 /* znachenie*/ },
/* OUT-zapis' */
/* pereinicializirovat' displej, ustanoviv bit #5 porta 3c0 */
{ OUT_ON_PORT, 0x3C0, 0x20 }
};
void closescr(nsig){ /* konec raboty */
setbgcolor(0); /* ustanovit' chernyj fon ekrana */
exit(0);
}
/* poluchenie dostupa k videopamyati adaptera VGA/EGA/CGA */
void openscr () {
static struct videodev {
char *dev; int mapmode;
} vd[] = {
{ "/dev/vga", MAPVGA },
{ "/dev/ega", MAPEGA },
{ "/dev/cga", MAPCGA },
{ NULL, -1 }
}, *v; /* ustrojstvo dlya dostupa k videoadapteru */
for(v=vd; v->dev;v++ )
if((ega = open (v->dev, O_RDWR)) >= 0 ) goto ok;
fprintf( stderr, "Can't open video adapter\n" );
exit(1);
A. Bogatyrev, 1992-95 - 352 - Si v UNIX
ok:
/* fprintf(stderr, "Adapter:%s\n", v->dev); */
/* poluchit' adres videopamyati i dostup k nej */
#ifdef M_I386
screen = (char *) ioctl (ega, v->mapmode, 0);
#else
segm = ioctl (ega, v->mapmode, 0);
screen = sotofar (segm, 0); /* (segment,offset) to far pointer */
#endif
signal( SIGINT, closescr );
}
/* makrosy dlya dostupa k bajtam "simvol" i "atributy"
* v koordinatah (x,y) ekrana.
*/
#define GET(x,y) screen[ ((x) + (y) * COLS ) * 2 ]
#define PUT(x,y, c) screen[ ((x) + (y) * COLS ) * 2 ] = (c)
#define GETATTR(x,y) screen[ ((x) + (y) * COLS ) * 2 + 1 ]
#define PUTATTR(x,y, a) screen[ ((x) + (y) * COLS ) * 2 + 1 ] = (a)
/* simvol izobrazhaetsya kak chernyj probel ? */
#define white(c,a) ((isspace(c) || c==0) && (attr & 0160)==0)
/* ustanovit' cvet fona ekrana */
void setbgcolor( color ){
PORT[1].data = 0; /* registr nomer 0 palitry soderzhit cvet fona */
/* vsego v palitre 16 registrov (0x00...0xFF) */
PORT[2].data = color ;
/* novoe znachenie cveta, sostavlennoe kak bitovaya maska
* RGBrgb (r- krasnyj, g- zelenyj, b- sinij, RGB- dopolnitel'nye
* tusklye cveta)
*/
/* vypolnit' obmeny s portami */
if( ioctl( ega, EGAIO, PORT ) < 0 ){
fprintf( stderr, "Can't out port\n" );
perror( "out" );
}
}
A. Bogatyrev, 1992-95 - 353 - Si v UNIX
void main(ac, av) char **av;{
void fall();
openscr();
if( ac == 1 ){
setbgcolor(020); /* temno-zelenyj fon ekrana */
fall(); /* osypanie bukv */
} else {
if(*av[1] == 'g')
/* Ustanovit' rezhim adaptera graphics 640x350 16-colors */
ioctl( ega, SW_CG640x350, NULL);
/* Esli vy hotite poluchit' adres videopamyati v graficheskom rezhime,
* vy dolzhny SNACHALA vklyuchit' etot rezhim,
* ZATEM sdelat' screen=ioctl(ega, v->mapmode, NULL);
* i ESHCHE RAZ sdelat' vklyuchenie graficheskogo rezhima.
*/
/* Ustanovit' rezhim adaptera text 80x25 16-colors */
else ioctl( ega, SW_ENHC80x25, NULL);
}
closescr(0);
}
/* osypat' bukvy vniz */
void fall(){
register i, j;
int rest;
int nextcol;
int n;
int changed = 1; /* ne 0, esli eshche ne vse bukvy opali */
char mask [ COLS ];
while( changed ){
changed = 0;
for( i = 0 ; i < COLS ; i++ )
mask[ i ] = 0;
for( i = 0 ; i < COLS ; i++ ){
rest = COLS - i; /* ostalos' osypat' kolonok */
nextcol = rand() % rest;
j = 0; /* indeks v mask */
n = 0; /* schetchik */
for(;;){
if( mask[j] == 0 ){
if( n == nextcol ) break;
n++;
} j++;
}
changed += fallColumn( j );
mask[j] = 1;
}
}
}
A. Bogatyrev, 1992-95 - 354 - Si v UNIX
/* osypat' bukvy v odnom stolbce */
int fallColumn( x ){
register int y;
char ch, attr;
int firstspace = (-1);
int firstnospace = (-1);
Again:
/* find the falled array */
for( y=LINES-1; y >= 0 ; y-- ){
ch = GET( x, y );
attr = GETATTR( x,y );
if( white(ch, attr)){
firstspace = y;
goto FindNoSpace;
}
}
AllNoSpaces:
return 0; /* nichego ne izmenilos' */
FindNoSpace: /* najti ne probel */
for( ; y >= 0 ; y-- ){
ch = GET( x, y );
attr = GETATTR( x, y );
if( !white(ch, attr)){
firstnospace = y;
goto Fall;
}
}
AllSpaces: /* v dannom stolbce vse upalo */
return 0;
Fall:
/* "uronit'" bukvu */
for( y = firstnospace ; y < firstspace ; y++ ){
/* peremestit' simvol na ekrane na odnu poziciyu vniz */
ch = GET( x, y );
attr = GETATTR( x, y );
PUT( x, y, 0 );
PUTATTR( x, y, 0 );
PUT( x, y+1 , ch );
PUTATTR( x, y+1, attr );
nap( DELAY ); /* podozhdat' DELAY millisekund */
}
return 1; /* chto-to izmenilos' */
}
8.2. Dlya raboty mozhet okazat'sya bolee udobnym imet' ukazatel' na videopamyat' kak na
massiv struktur. Privedem primer dlya sistemy MS DOS:
A. Bogatyrev, 1992-95 - 355 - Si v UNIX
#include <dos.h> /* tam opredeleno MK_FP */
char far *screen =
MK_FP(0xB800 /*segment*/, 0x0000 /*smeshchenie*/);
struct symb{
char chr; char attr;
} far *scr, far *ptr;
#define COLS 80 /* chislo kolonok */
#define LINES 25 /* chislo strok */
#define SCR(x,y) scr[(x) + COLS * (y)]
/* x iz 0..79, y iz 0..24 */
void main(){
int x, y;
char c;
scr = (struct symb far *) screen;
/* ili srazu
* scr = (struct symb far *) MK_FP(0xB800,0x0000);
*/
/* perepisat' stroki ekrana sprava nalevo */
for(x=0; x < COLS/2; x++ )
for( y=0; y < LINES; y++ ){
c = SCR(x,y).chr;
SCR(x,y).chr = SCR(COLS-1-x, y).chr;
SCR(COLS-1-x, y).chr = c;
}
/* sdelat' cvet ekrana: zheltym po sinemu */
for(x=0; x < COLS; x++)
for(y=0; y < LINES; y++)
SCR(x,y).attr = (0xE | (0x1 << 4));
/* zheltyj + sinij fon */
/* prochest' lyubuyu knopku s klaviatury (pauza) */
(void) getch();
}
I, nakonec, eshche udobnee rabota s videopamyat'yu kak s dvumernym massivom struktur:
#include <dos.h> /* MS DOS */
#define COLS 80
#define LINES 25
struct symb {
char chr; char attr;
} (far *scr)[ COLS ] = MK_FP(0xB800, 0);
void main(void){
register x, y;
for(y=0; y < LINES; y++)
for(x=0; x < COLS; ++x){
scr[y][x].chr = '?';
scr[y][x].attr = (y << 4) | (x & 0xF);
}
getch();
}
Uchtite, chto pri rabote s ekranom cherez videopamyat', kursor ne peremeshchaetsya! Esli v
A. Bogatyrev, 1992-95 - 356 - Si v UNIX
obychnoj rabote s ekranom tekst vyvoditsya v pozicii kursora i kursor avtomaticheski
prodvigaetsya, to zdes' kursor budet ostavat'sya na svoem prezhnem meste. Dlya peremeshche-
niya kursora v nuzhnoe vam mesto, vy dolzhny ego postavit' yavnym obrazom po okonchanii
zapisi v videopamyat' (naprimer, obrashchayas' k portam videokontrollera).
Obratite vnimanie, chto specifikator modeli pamyati far dolzhen ukazyvat'sya pered
KAZHDYM ukazatelem (imenno dlya illyustracii etogo v pervom primere opisan neispol'zue-
myj ukazatel' ptr).
8.3. Sostav'te programmu sohraneniya soderzhimogo ekrana IBM PC (videopamyati) v teks-
tovom rezhime v fajl i obratno (v sisteme XENIX).
8.4. Pol'zuyas' pryamym dostupom v videopamyat', napishite funkcii dlya spaseniya pryamou-
gol'noj oblasti ekrana v massiv i obratno. Vot funkciya dlya spaseniya v massiv:
typedef struct {
short xlen, ylen;
char *save;
} Pict;
extern void *malloc(unsigned);
Pict *gettext (int x, int y, int xlen, int ylen){
Pict *n = (Pict *) malloc(sizeof *n);
register char *s; register i, j;
n->xlen = xlen; n->ylen = ylen;
s = n->save = (char *) malloc( 2 * xlen * ylen );
for(i=y; i < y+ylen; i++)
for(j=x; j < x+xlen; j++){
*s++ = SCR(j,i).chr ;
*s++ = SCR(j,i).attr;
}
return n;
}
Dobav'te proverki na korrektnost' xlen, ylen (v predelah ekrana). Napishite funkciyu
puttext dlya vyvoda spasennoj oblasti obratno; funkciyu free(buf) luchshe v nee ne vstav-
lyat'.
void puttext (Pict *n, int x, int y){
register char *s = n->save;
register i, j;
for(i=y; i < y + n->ylen; i++)
for(j=x; j < x + n->xlen; j++){
SCR(j,i).chr = *s++;
SCR(j,i).attr = *s++;
}
}
/* ochistka pamyati tekstovogo bufera */
void deltext(Pict *n){ free(n->save); free(n); }
Privedem eshche odnu poleznuyu funkciyu, kotoraya mozhet vam prigodit'sya - eto analog printf
pri pryamoj rabote s videopamyat'yu.
#include <stdarg.h>
/* tekushchij cvet: belyj po sinemu */
static char currentColor = 0x1F;
int videoprintf (int x, int y, char *fmt, ...){
char buf[512], *s;
va_list var;
A. Bogatyrev, 1992-95 - 357 - Si v UNIX
/* clipping (otsechenie po granicam ekrana) */
if( y < 0 || y >= LINES ) return x;
va_start(var, fmt);
vsprintf(buf, fmt, var);
va_end(var);
for(s=buf; *s; s++, x++){
/* otsechenie */
if(x < 0 ) continue;
if(x >= COLS) break;
SCR(x,y).chr = *s;
SCR(x,y).attr = currentColor;
}
return x;
}
void setcolor (int col){ currentColor = col; }
8.5. Pol'zuyas' napisannymi funkciyami, realizujte funkcii dlya "vyskakivayushchih" okon
(pop-up window):
Pict *save;
save = gettext (x,y,xlen,ylen);
// ... risuem cvetnymi probelami pryamougol'nik s
// uglami (x,y) vverhu-sleva i (x+xlen-1,y+ylen-1)
// vnizu-sprava...
// ...risuem nekie tablicy, menyu, tekst v etoj zone...
// stiraem narisovannoe okno, vosstanoviv to izobrazhenie,
// poverh kotorogo ono "vsplylo".
puttext (save,x,y);
deltext (save);
Dlya nachala napishite "vyskakivayushchee" okno s soobshcheniem; okno dolzhno ischezat' po nazha-
tiyu lyuboj klavishi.
c = message(x, y, text);
Razmer okna vychislyajte po dline stroki text. Kod klavishi vozvrashchajte v kachestve zna-
cheniya funkcii.
Teper' sdelajte text massivom strok: char *text[]; (poslednyaya stroka - NULL).
8.6. Sdelajte tak, chtoby "vyskakivayushchie" okna imeli ten'. Dlya etogo nado sohranit' v
nekotoryj bufer atributy simvolov (sami simvoly ne nado!), nahodyashchihsya na mestah $:
##########
##########$
##########$
$$$$$$$$$$
a zatem propisat' etim simvolam na ekrane atribut 0x07 (belyj po chernomu). Pri stira-
nii okna (puttext-om) sleduet vosstanovit' spasennye atributy etih simvolov (steret'
ten'). Esli okno imeet razmer xlen*ylen, to razmer bufera raven xlen+ylen-1 bajt.
8.7. Napishite funkciyu, risuyushchuyu na ekrane pryamougol'nuyu ramku. Ispol'zujte ee dlya
risovaniya ramki okna.
A. Bogatyrev, 1992-95 - 358 - Si v UNIX
8.8. Napishite "vyskakivayushchee" okno, kotoroe proyavlyaetsya na ekrane kak by rasshiryayas'
iz tochki:
##############
###### ##############
### ###### ##############
###### ##############
##############
Vam sleduet napisat' funkciyu box(x,y,width,height), risuyushchuyu cvetnoj pryamougol'nik s
verhnim levym uglom (x,y) i razmerom (width,height). Pust' konechnoe okno zadaetsya
uglom (x0,y0) i razmerom (W,H). Togda "vyrastanie" okna opisyvaetsya takim algoritmom:
void zoom(int x0, int y0, int W, int H){
int x, y, w, h, hprev; /* promezhutochnoe okno */
for(hprev=0, w=1; w < W; w++){
h = H * w; h /= W; /* W/H == w/h */
if(h == hprev) continue;
hprev = h;
x = x0 + (W - w)/2; /* chtoby centry okon */
y = y0 + (H - h)/2; /* sovpadali */
box(x, y, w, h);
delay(10); /* zaderzhka 10 millisek. */
}
box(x0, y0, W, H);
}
8.9. Sostav'te biblioteku funkcij, analogichnyh biblioteke curses, dlya |VM IBM PC v
OS XENIX. Ispol'zujte pryamoj dostup v videopamyat'.
8.10. Napishite rekursivnoe reshenie zadachi "hanojskie bashni" (perekladyvanie diskov:
est' tri sterzhnya, na odin iz nih nadety diski ubyvayushchego k vershine diametra. Trebu-
etsya perelozhit' ih na tretij sterzhen', nikogda ne kladya disk bol'shego diametra poverh
diska men'shego diametra). Uslozhnenie - ispol'zujte paket curses dlya izobrazheniya
perekladyvaniya diskov na ekrane terminala. Ukazanie: ideya rekursivnogo algoritma:
carry(n, from, to, by) = if( n > 0 ){
carry( n-1, from, by, to );
perenesiOdinDisk( from, to );
carry( n-1, by, to, from );
}
Vyzov: carry( n, 0, 1, 2 );
n - skol'ko diskov perenesti (n > 0).
from - otkuda (nomer sterzhnya).
to - kuda.
by - pri pomoshchi (promezhutochnyj sterzhen').
n diskov potrebuyut (2**n)-1 perenosov.
8.11. Napishite programmu, ishchushchuyu vyhod iz labirinta ("chervyak v labirinte"). Labi-
rint zagruzhaetsya iz fajla .maze (ne zabud'te pro rasshirenie tabulyacij!). Algoritm
imeet rekursivnuyu prirodu i vyglyadit primerno tak:
#include <setjmp.h>
jmp_buf jmp; int found = 0;
maze(){ /* |to golovnaya funkciya */
if( setjmp(jmp) == 0 ){ /* nachalo */
if( neStenka(x_vhoda, y_vhoda))
GO( x_vhoda, y_vhoda);
A. Bogatyrev, 1992-95 - 359 - Si v UNIX
}
}
GO(x, y){ /* pojti v tochku (x, y) */
if( etoVyhod(x, y)){ found = 1; /* nashel vyhod */
pometit'(x, y); longjmp(jmp, 1);}
pometit'(x, y);
if( neStenka(x-1,y)) GO(x-1, y); /* vlevo */
if( neStenka(x,y-1)) GO(x, y-1); /* vverh */
if( neStenka(x+1,y)) GO(x+1, y); /* vpravo */
if( neStenka(x,y+1)) GO(x, y+1); /* vniz */
snyat'Pometku(x, y);
}
#define pometit'(x, y) labirint[y][x] = '*'
#define snyat'Pometku(x, y) labirint[y][x] = ' '
#define etoVyhod(x, y) (x == x_vyhoda && y == y_vyhoda)
/* mozhno iskat' "zoloto": (labirint[y][x] == '$') */
neStenka(x, y){ /* stenku izobrazhajte simvolom @ ili # */
if( koordinatyVnePolya(x, y)) return 0; /*kraj labirinta*/
return (labirint[y][x] == ' ');
}
Otobrazite massiv labirint na videopamyat' (ili vospol'zujtes' curses-om). Vy uvidite
chervyaka, polzayushchego po labirintu v svoih iskaniyah.
8.12. Ispol'zuya biblioteku termcap napishite funkcii dlya:
- ochistki ekrana.
- pozicionirovaniya kursora.
- vklyucheniya/vyklyucheniya rezhima vydeleniya teksta inversiej.
8.13. Ispol'zuya napisannye funkcii, realizujte programmu vybora v menyu. Vybrannuyu
stroku vydelyajte inversiej fona.
/*#!/bin/cc termio.c -O -o termio -ltermcap
* Smotri man termio, termcap i screen.
* Rabota s terminalom v stile System-V.
* Rabota s sistemoj komand terminala cherez /etc/termcap
* Rabota so vremenem.
* Rabota s budil'nikom.
*/
#include <stdio.h> /* standard input/output */
#include <sys/types.h> /* system typedefs */
#include <termio.h> /* terminal input/output */
#include <signal.h> /* signals */
#include <fcntl.h> /* file control */
#include <time.h> /* time structure */
void setsigs(), drawItem(), drawTitle(), prSelects(), printTime();
A. Bogatyrev, 1992-95 - 360 - Si v UNIX
/* Rabota s opisaniem terminala TERMCAP ---------------------------------*/
extern char *getenv (); /* poluchit' peremennuyu okruzheniya */
extern char *tgetstr (); /* poluchit' strochnyj opisatel' /termcap/ */
extern char *tgoto (); /* podstavit' %-parametry /termcap/ */
static char Tbuf[2048], /* bufer dlya opisaniya terminala, obychno 1024 */
/* Tbuf[] mozhno sdelat' lokal'noj avtomaticheskoj peremennoj
* v funkcii tinit(), chtoby ne zanimat' mesto */
Strings[256], /* bufer dlya rasshifrovannyh opisatelej */
*p; /* vspomogatel'naya perem. */
char *tname; /* nazvanie tipa terminala */
int COLS, /* chislo kolonok ekrana */
LINES; /* chislo strok ekrana */
char *CM; /* opisatel': cursor motion */
char *CL; /* opisatel': clear screen */
char *CE; /* opisatel': clear end of line */
char *SO,
*SE; /* opisateli: standout Start i End */
char *BOLD,
*NORM; /* opisateli: boldface and NoStandout */
int BSflag; /* mozhno ispol'zovat' back space '\b' */
void tinit () { /* Funkciya nastrojki na sistemu komand displeya */
p = Strings;
/* Prochest' opisanie terminala v Tbuf */
switch (tgetent (Tbuf, tname = getenv ("TERM"))) {
case -1:
printf ("Net fajla TERMCAP (/etc/termcap).\n");
exit (1);
case 0:
printf ("Terminal %s ne opisan.\n", tname);
exit (2);
case 1:
break; /* OK */
}
COLS = tgetnum ("co"); /* Prochest' chislovye opisateli. */
LINES = tgetnum ("li");
CM = tgetstr ("cm", &p); /* Prochest' strochnye opisateli. */
CL = tgetstr ("cl", &p); /* Opisatel' deshifruetsya i zanositsya */
CE = tgetstr ("ce", &p); /* v massiv po adresu p. Zatem */
SO = tgetstr ("so", &p); /* ukazatel' p prodvigaetsya na */
SE = tgetstr ("se", &p); /* svobodnoe mesto, a adres rasshif- */
BOLD = tgetstr ("md", &p); /* rovannoj stroki vydaetsya iz f-cii */
NORM = tgetstr ("me", &p);
BSflag = tgetflag( "bs" ); /* Uznat' znachenie flazhka:
1 - est', 0 - net */
}
A. Bogatyrev, 1992-95 - 361 - Si v UNIX
/* Makros, vnesennyj v funkciyu.
Delo v tom, chto tputs v kachestve tret'ego argumenta
trebuet imya funkcii, kotoruyu ona vyzyvaet v cikle: (*f)(c);
Esli podat' na vhod makros, vrode putchar,
a ne adres vhoda v funkciyu, my
i ne dostignem zhelannogo effekta,
i poluchim rugan' ot kompilyatora.
*/
void put (c) char c;
{ putchar (c); }
/* ochistit' ekran */
void clearScreen () {
if (CL == NULL) /* Funkciya tputs() dorasshifrovyvaet opisatel' */
return; /* (obrabatyvaya zaderzhki) i vydaet ego */
tputs (CL, 1, put); /* posimvol'no f-ciej put(c) 1 raz */
/* Mozhno vydat' komandu ne 1 raz, a neskol'ko: naprimer esli eto */
/* komanda sdviga kursora na 1 poziciyu vlevo '\b' */
}
/* ochistit' konec stroki, kursor ostaetsya na meste */
void clearEOL () { /* clear to the end of line */
if (CE == NULL)
return;
tputs (CE, 1, put);
}
/* pozicionirovat' kursor */
void gotoXY (x, y) { /* y - po vertikali SVERHU-VNIZ. */
if (x < 0 || y < 0 || x >= COLS || y >= LINES) {
printf ("Tochka (%d,%d) vne ekrana\n", x, y);
return;
}
/* CM - opisatel', soderzhashchij 2 parametra. Podstanovku parametrov
* delaet funkciya tgoto() */
tputs (tgoto (CM, x, y), 1, put);
}
/* vklyuchit' vydelenie */
void standout () {
if (SO) tputs (SO, 1, put);
}
/* vyklyuchit' vydelenie */
void standend () {
if (SE) tputs (SE, 1, put);
/* else normal(); */
}
/* vklyuchit' zhirnyj shrift */
void bold () {
if (BOLD) tputs (BOLD, 1, put);
}
A. Bogatyrev, 1992-95 - 362 - Si v UNIX
/* vyklyuchit' lyuboj neobychnyj shrift */
void normal () {
if (NORM) tputs (NORM, 1, put);
else standend();
}
/* Upravlenie drajverom terminala --------------------------------- */
#define ESC '\033'
#define ctrl(c) ((c) & 037 )
int curMode = 0;
int inited = 0;
struct termio old,
new;
int fdtty;
void ttinit () {
/* otkryt' terminal v rezhime "chtenie bez ozhidaniya" */
fdtty = open ("/dev/tty", O_RDWR | O_NDELAY);
/* uznat' tekushchie rezhimy drajvera */
ioctl (fdtty, TCGETA, &old);
new = old;
/* input flags */
/* otmenit' preobrazovanie koda '\r' v '\n' na vvode */
new.c_iflag &= ~ICRNL;
if ((old.c_cflag & CSIZE) == CS8) /* 8-bitnyj kod */
new.c_iflag &= ~ISTRIP; /* otmenit' & 0177 na vvode */
/* output flags */
/* otmenit' TAB3 - zamenu tabulyacij '\t' na probely */
/* otmenit' ONLCR - zamenu '\n' na paru '\r\n' na vyvode */
new.c_oflag &= ~(TAB3 | ONLCR);
/* local flags */
/* vyklyuchit' rezhim ICANON, vklyuchit' CBREAK */
/* vyklyuchit' ehootobrazhenie nabiraemyh simvolov */
new.c_lflag &= ~(ICANON | ECHO);
/* control chars */ /* pri vvode s klavish zhdat' ne bolee ... */
new.c_cc[VMIN] = 1; /* 1 simvola i */
new.c_cc[VTIME] = 0; /* 0 sekund */
/* |to sootvetstvuet rezhimu CBREAK */
/* Simvoly, nazhatie kotoryh zastavlyaet drajver terminala poslat' signal
* libo otredaktirovat' nabrannuyu stroku. Znachenie 0 oznachaet,
* chto sootvetstvuyushchego simvola ne budet */
new.c_cc[VINTR] = ctrl ('C'); /* simvol, generyashchij SIGINT */
new.c_cc[VQUIT] = '\0'; /* simvol, generyashchij SIGQUIT */
new.c_cc[VERASE] = '\0'; /* zaboj (otmena poslednego simvola)*/
new.c_cc[VKILL] = '\0'; /* simvol otmeny stroki */
/* Po umolchaniyu eti knopki ravny: DEL, CTRL/\, BACKSPACE, CTRL/U */
setsigs ();
inited = 1; /* uzhe inicializirovano */
}
A. Bogatyrev, 1992-95 - 363 - Si v UNIX
void openVisual () { /* open visual mode (vklyuchit' "ekrannyj" rezhim) */
if (!inited)
ttinit ();
if (curMode == 1)
return;
/* ustanovit' mody drajvera iz struktury new */
ioctl (fdtty, TCSETAW, &new);
curMode = 1; /* ekrannyj rezhim */
}
void closeVisual () { /* canon mode (vklyuchit' kanonicheskij rezhim) */
if (!inited)
ttinit ();
if (curMode == 0)
return;
ioctl (fdtty, TCSETAW, &old);
curMode = 0; /* kanonicheskij rezhim */
}
/* zavershit' process */
void die (nsig) {
normal();
closeVisual (); /* Pri zavershenii programmy (v tom chisle po
* signalu) my dolzhny vosstanovit' prezhnie rezhimy drajvera,
* chtoby terminal okazalsya v korrektnom sostoyanii. */
gotoXY (0, LINES - 1);
putchar ('\n');
if (nsig)
printf ("Prishel signal #%d\n", nsig);
exit (nsig);
}
void setsigs () {
register ns;
/* Perehvatyvat' vse signaly; zavershat'sya po nim. */
/* UNIX imeet 15 standartnyh signalov. */
for (ns = 1; ns <= 15; ns++)
signal (ns, die);
}
A. Bogatyrev, 1992-95 - 364 - Si v UNIX
/* Rabota s menyu -------------------------------------------- */
struct menu {
char *m_text; /* vydavaemaya stroka */
int m_label; /* pomechena li ona ? */
} menuText[] = {
/* nazvaniya pesen Beatles */
{ "Across the Universe", 0 } ,
{ "All I've got to do", 0 } ,
{ "All my loving", 0 } ,
{ "All together now", 0 } ,
{ "All You need is love",0 } ,
{ "And I love her", 0 } ,
{ "And your bird can sing", 0 } ,
{ "Another girl", 0 } ,
{ "Any time at all", 0 } ,
{ "Ask me why", 0 } ,
{ NULL, 0 }
};
#define Y_TOP 6
int nitems; /* kolichestvo strok v menyu */
int nselected = 0; /* kolichestvo vybrannyh strok */
char title[] =
"PROBEL - vniz, ZABOJ - vverh, ESC - vyhod, \
ENTER - vybrat', TAB - otmenit'";
# define TIMELINE 1
void main (ac, av) char **av; {
char **line;
register i;
int c;
int n; /* tekushchaya stroka */
extern char readkey (); /* forward */
extern char *ttyname (); /* imya terminala */