home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga MA Magazine 1997 #3
/
amigamamagazinepolishissue03-1
/
ma_1995
/
11
/
ami041.txt
< prev
next >
Wrap
Text File
|
1997-04-07
|
10KB
|
217 lines
Kurs jëzyka C (cz. 6.)
----------------------
MALOWANIE PO EKRANIE
<a>Kamil Iskra, Dariusz Ûbik
<txt>Odcinek ten zawiera dokoïczenie omówienia listingu 9., który
zamieôciliômy w poprzednim numerze Magazynu AMIGA.
W naszym programie wystëpuje oczywiste powiâzanie pomiëdzy trybem
pracy programu, klawiszem powodujâcym jego wywoîanie i opisujâcym
go tekstem. Stworzyliômy wiëc wîasnâ strukturë o nazwie
"operacja", która îâczy te trzy elementy: pole "name" zawiera
opis trybu, a "key" literë wywoîujâcâ dany tryb. Trzecie pole --
"fun" -- moûe byê czymô nowym dla niezbyt doôwiadczonych
programistów. Jest to wskaúnik na funkcjë pobierajâcâ argumenty
"wskaúnik na strukturë IntuiMessage" oraz "wskaúnik na strukturë
Window", a nie zwracajâcâ niczego (void). Peîniejsze wiadomoôci
znajdziesz w ksiâûce "Jëzyk ANSI C". W programie definiowana jest
tablica takich struktur: "op_tab", w pola "fun" jest wpisywany
adres funkcji obsîugujâcych dany tryb programu, a to, w jakim
trybie program znajduje sië w danym momencie, jest odnotowane w
zmiennej "op_pos", której wartoôê jest po prostu indeksem tablicy
"op_tab". Aby wywoîaê funkcjë, której adres znajduje sië w polu
"fun", naleûy postâpiê tak samo, jakby sië wywoîywaîo normalnâ
funkcjë. My w programie robimy po prostu:
<l>op_tab[op_pos].fun(&msg, window);
<txt>Deklaracjë zmiennych mamy juû za sobâ, nastëpnie otwieramy
biblioteki i okna, potem przychodzi czas na pëtlë "for",
wykonujâcâ sië do koïca programu, oraz "rutynkë", czyli
oczekiwanie na informacje z portu okna, a póúniej jeszcze jakieô
"cuda": "__STDC__" i inne.
Zastosowaliômy wyraûenie, które nie jest znane wszystkim
kompilatorom, a mianowicie skopiowanie jednej struktury do
drugiej za pomocâ pojedynczego przypisania. Taki zapis jest
zgodny z normâ ANSI jëzyka C, ale niektóre starsze kompilatory
mogâ mieê z nim problemy. Z tego powodu uûyliômy dyrektyw
preprocesora, powodujâcych warunkowâ kompilacjë: jeûeli jest
zdefiniowana staîa "__STDC__" i jest ona róûna od 0 (#if
__STDC__), co oznacza, ûe dany kompilator jest zgodny z normâ
ANSI, to uûywamy wyûej opisanej instrukcji, w przeciwnym wypadku
(#else) jawnie kopiujemy jednâ strukturë do drugiej. Warto moûe w
tym momencie wspomnieê o kilku innych standardowo zdefiniowanych
staîych, np. __SASC i AMIGA dla SAS/C, AZTEC_C i MCH_AMIGA dla
Aztec C, __GNUC__ i AMIGA dla GNU CC. Dziëki nim moûna tworzyê
programy, dziaîajâce na kilku platformach sprzëtowych oraz
moûliwe do skompilowania na róûnych kompilatorach.
Wprawdzie nie wszyscy bëdâ zmuszeni do wywoîania funkcji
CopyMem() z biblioteki Exec, jednak wszyscy powinni jâ znaê:
<l>void CopyMem( APTR source, APTR dest, unsigned long size );
<txt>Zadaniem funkcji jest skopiowanie obszaru pamiëci,
wskazywanego przez wskaúnik "source", do obszaru wskazywanego
przez "dest", rozmiar kopiowanego "kawaîka" podany w bajtach jest
trzecim argumentem. UWAGA! Kolejnoôê parametrów "source" i "dest"
jest ODWROTNA niû w standardowych funkcjach kopiujâcych jëzyka
C, takich jak "strcpy", "memcpy" itd.
Po wykonaniu kopii przybyîej wiadomoôci i zwrocie oryginaîu
"rozpakowujemy prezent" od Intuition.
Klasa wiadomoôci IDCMP_VANILLAKEY:
Flaga ta powoduje dostarczanie do programu informacji o uûyciu
klawiatury. Informacja tego typu zawiera w polu "Code" kod znaku,
jaki znajdowaî sië pod naciôniëtym przyciskiem, obsîugiwane jest
aktualne obîoûenie klawiatury, ustawione przez uûytkownika za
pomocâ systemowego programu "Input". VANILLAKEY dostarcza tylko
pojedyncze znaki, nie jest moûliwe odczytanie ciâgów
zdefiniowanych pod przyciskami, nie moûna dowiedzieê sië o
przycisku HELP, kursorach... Moûliwe jest jednak odczytanie
wciôniëcia klawisza [Esc], poniewaû jest on pojedynczym znakiem o
kodzie 27.
W programie, po otrzymaniu informacji o naciôniëciu przycisku,
sprawdzamy, czy jest to [Esc]. Jeôli tak, to opuszczamy program,
w przeciwnym wypadku przeglâdamy tablicë operacji, sprawdzajâc,
czy któraô z nich nie ma skrótu z klawiatury identycznego z
naciôniëtym przyciskiem. Ten przydîugawy warunek w pëtli "for"
pozwala przejrzeê wszystkie elementy tablicy "op_tab". Poniewaû
operator "sizeof" zwraca rozmiar obiektu w bajtach, naleûy
wielkoôê të podzieliê przez rozmiar pojedynczego elementu.
Operator "sizeof" podaje rozmiar obiektu podczas kompilacji
programu, w zwiâzku z tym wyraûenie w warunku pëtli "for" otrzyma
staîâ wartoôê.
Po otrzymaniu informacji IDCMP_CLOSEWINDOW wywoîujemy funkcjë:
<l>BOOL DoubleClick( ULONG sSeconds, ULONG sMicros, ULONG cSeconds, ULONG cMicros);
<txt>Jej zadaniem jest sprawdzenie, czy podane dwie wartoôci
czasu mieszczâ sië w tzw. double clicku. Jeôli róûnica miëdzy
tymi wartoôciami jest mniejsza od "double clicku", to zostanie
zwrócona wartoôê TRUE, w przeciwnym wypadku FALSE. Czas do
funkcji podaje sië w postaci sekund i mikrosekund pierwszego i
drugiego wydarzenia. Moûna je znaleúê w strukturze
"IntuiMessage".
Dziëki funkcji DoubleClick() program opuszcza sië dopiero po
dwukrotnym klikniëciu na gadûecie zamykania. W wypadku
pojedynczego naciôniëcia zostanie zmieniony tryb pracy programu
(tak, wiemy, to nie jest intuicyjne).
W wypadku, gdy system dostarczy informacji o przyciskach myszy
(IDCMP_MOUSEBUTTONS), zostanie wywoîana bieûâca funkcja z tablicy
"op_tab".
Wewnâtrz instrukcji "switch" pojawiîa sië kolejna, nie omówiona
dotychczas, klasa informacji IDCMP, a mianowicie IDCMP_MOUSEMOVE.
Klasa ta powoduje przekazywanie do portu okna aktywnego
informacji o kaûdym ruchu myszy (trzeba je obsîugiwaê szybko, bo
przy nagîych ruchach myszâ przybywa naprawdë sporo informacji!).
Do jej funkcjonowania konieczne jest umieszczenie w polu "Flags"
okna flagi WFLG_REPORTMOUSE. Program w prezentowanej wersji nie
robi nic w wypadku stwierdzenia takiej klasy wiadomoôci.
Umieôciliômy jâ jednak, aby umoûliwiê rozbudowë programu, która
bez znajomoôci tej klasy informacji mogîaby byê kîopotliwa.
Poniewaû w funkcji "main" nie znajdziemy juû nic ciekawego, omówië
funkcje zawarte w tablicy "op_tab":
linia() -- zawiera funkcjë z biblioteki "graphics.library":
<l>void Draw( struct RastPort *rp, long x, long y );
<txt>Jej zadaniem jest narysowanie linii pomiëdzy obecnym
poîoûeniem kursora a podanymi wspóîrzëdnymi, zmieniane jest
poîoûenie kursora graficznego na wartoôci x i y.
punkt() -- zawiera dwie funkcje biblioteczne:
<l>LONG WritePixel( struct RastPort *rp, long x, long y );
<txt>Zmienia kolor punktu o wspóîrzëdnych (x, y) na kolor
ustawiony jako APen. Rezultatem dziaîania funkcji jest zero, gdy
wszystko jest OK, lub -1, gdy podany punkt jest poza rastportem.
Funkcja, jako jedna z niewielu, nie zmienia poîoûenia kursora
graficznego.
<l>void Move( struct RastPort *rp, long x, long y );
<txt>Przesuwa kursor graficzny w podane miejsce, bez
jakiegokolwiek efektu wizualnego.
Funkcja napis() -- odwoîuje sië do funkcji systemowej:
<l>LONG Text( struct RastPort *rp, STRPTR string, unsigned long count );
<txt>Umieszcza ona tekst, wskazywany przez "string", w
rastporcie. Napis jest umieszczany w bieûâcym miejscu (cp_x,
cp_y) i rysowany bieûâcâ czcionkâ. Pisany jest zarówno poniûej,
jak i powyûej poîoûenia kursora (dlaczego tak jest, powiemy w
kolejnej czëôci). Po zakoïczeniu dziaîania funkcji kursor
graficzny znajduje sië na koïcu tekstu. Funkcja ta wymaga podania
liczby znaków do wypisania -- sîuûy do tego ostatni argument.
drmd() -- funkcja ta zmienia tryb rysowania po naciôniëciu lewego
przycisku myszy. Systemowe narzëdzia, sîuûâce do tego celu,
opisaliômy wczeôniej. Wystëpujâca tu przydîuga i skomplikowana
konstrukcja, bëdâca drugim argumentem funkcji SetDrMd(),
powoduje, ûe tryb rysowania zostanie ustalony na kolejny lub
odliczanie rozpocznie sië od poczâtku. Postëpujâc zgodnie z
kolejnoôciâ operatorów, najpierw zostanie wykonana konstrukcja
warunkowa, zwracajâca numer obecnego trybu (jej zadanie byîo
omówione wczeôniej) -- po jego zwiëkszeniu o jeden otrzymamy
numer kolejnego trybu. Poniewaû argument funkcji SetDrMd(),
bëdâcy równoczeônie indeksem tablicy names[], powinien byê
istniejâcym trybem, naleûy zadbaê o to, aby wartoôê ta nie
przekraczaîa 7, poniewaû po zsumowaniu masek bitowych wszystkich
trybów rysowania otrzymamy takâ wîaônie wartoôê (patrz
"graphics/rastport.h"). Do zapisania liczby z przedziaîu 0..7
wystarczâ trzy najmîodsze bity -- wîaônie w tym celu wykonujemy
bitowâ operacjë I (AND) z flagâ 7 (bitowo 0111). Dziëki temu
przetrwajâ jedynie najmîodsze bity i zapisanie liczby wiëkszej od
7 nie bëdzie moûliwe, uzyskamy wiëc cyklicznie zmieniajâcy sië
tryb graficzny. Niektórzy z Was zapewne zastanawiajâ sië,
dlaczego nie zastosowaliômy tu prostszej i bardziej ogólnej
metody z wyraûeniem warunkowym "if" -- po prostu tak, jak jest,
jest szybciej i krócej.
color() -- zmienia kolor tîa po przyciôniëciu prawego przycisku
oraz atramentu po przyciôniëciu lewego.
kwadrat() -- rysuje wypeîniony prostokât od poîoûenia kursora
graficznego do poîoûenia myszy w momencie klikniëcia. Zwie sië
niezbyt zgodnie z prawdâ, poniewaû kolega Darek nie odróûnia
prostokâtów od kwadratów. Funkcja ta korzysta z funkcji
biblioteki graficznej:
<l>void RectFill( struct RastPort *rp, long xMin, long yMin, long xMax, long yMax );
<txt>Jej zadaniem jest wypeînienie prostokâtnego obszaru za
pomocâ aktualnego koloru APen. Wspóîrzëdne lewego górnego rogu
prostokâta (xMin, yMin) MUSZÂ byê mniejsze od wspóîrzëdnych
prawego dolnego rogu (xMax, yMax) lub im równe.
elipsa() -- funkcja rysuje elipsë, dîugoôci póîosi sâ równe
wspóîrzëdnym wektora îâczâcego punkt (cp_x, cp_y) z punktem, nad
którym "przyciôniëto" mysz. Biblioteczna funkcja do rysowania
elipsy wyglâda nastëpujâco:
<l>void DrawEllipse( struct RastPort *rp, long xCenter, long yCenter, long a, long b );
<txt>xCenter i yCenter to wspóîrzëdne ôrodka elipsy, a i b to
póîosie elipsy (muszâ byê wiëksze od zera). Wspóîrzëdne cp_x i
cp_y nie sâ zmieniane.
Za miesiâc pokaûemy, jak uûywaê róûnych czcionek oraz jak tworzyê
górne menu.