"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 */