DirectX (10.) V dneÜnφ jubilejnφ lekci budeme pokraΦovat v implementaci novΘ t°φdy CInput, kterou jsme rozd∞lali v minulΘ lekci. V²sledkem dneÜnφ lekce, by m∞la b²t fungujφcφ myÜ i klßvesnice, ale bohu₧el zatφm nemßme co ovlßdat. V n∞kolika p°φÜtφch lekcφch vÜak hodlßm sestavit v∞tÜφ projekt, kter² bude vyu₧φvat znalostφ, kterΘ mßte. Bude se jednat o trochu slo₧it∞jÜφ systΘm grafickΘho menu, ale vφce se o tom dovφte a₧ p°φÜt∞. 10.1. Funkce ProcessInput() a UpdateCursor()Ob∞ tyto funkce jsou spoleΦnΘ tφm, ₧e je budeme volat z funkce UpdateFrame() v naÜem hlavnφm programu. ProblΘm je ale v tom, ₧e funkce ProcessInput() musφte volat d°φve, ne₧ obnovφte grafiku. Kdybyste ale obnovili grafiku d°φve ne₧ kurzor, byl by kurzor skryt za pozadφm a to nechceme. Funkce ProcessInput() stahuje data z klßvesnice a myÜφ, p°φpadn∞ posune sou°adnice kurzoru. Na tyto novΘ sou°adnice se poslΘze nakreslφ kurzor z funkce UpdateCursor(). Abychom mohli tyto dv∞ hlavnφ funkce implementovat, musφme p°idat pßr nov²ch prom∞nn²ch a metod, kterΘ s t∞mito funkcemi spolupracujφ. Za prvΘ to bude inline funkce MoveCursor(), kterß je vskutku primitivnφ: void MoveCursor(int dx, int dy) {m_ptCursor += CPoint(dx, dy);} Vidφte, ₧e musφme p°idat atribut m_ptCursor. To je objekt typu CPoint (pokud nemßte rßdi MFC, m∙₧ete po₧φt strukturu POINT) a obsahuje aktußlnφ absolutnφ sou°adnice kurzoru na monitoru. Hodnoty x-ovΘ sou°adnice se pohybujφ od 0 do hodnoty rozliÜenφ v x-ovΘm sm∞ru. Ve vertikßlnφm sm∞ru je to obdobnΘ. Nynφ u₧ vφte, proΦ jsme si uklßdali aktußlnφ rozliÜenφ. Funkce ProcessInput() vypadß nßsledovn∞:
HRESULT dwRet = 1; P°ibyly jeÜt∞ dalÜφ prom∞nnΘ. Prom∞nnß m_bShowCursor nßm °φkß, zda-li je kurzor vid∞t Φi nikoliv. Pokud ne, je zbyteΦnΘ stahovat data z myÜi a v∙bec pracovat s kurzorem. Prvnφ co v tΘto funkci ud∞lßme je, ₧e stßhneme data z klßvesnice. Funkce GetDeviceState() naplnφ pole o 256 byte prvcφch. Pole m_arKeyboard je tedy dalÜφ atribut. V tomto poli je ulo₧ena informace o stavu vÜech klßves. To samΘ provedeme s myÜφ (pokud je kurzor zobrazen). Za prvΘ stßhneme data z myÜi tentokrßt do struktury DIMOUSESTATE, kterß obsahuje vÜe co pot°ebuje: p°φr∙stky pozice kurzoru a stavy tlaΦφtek. V dalÜφm kroku p°iΦteme relativnφ p°φr∙stky kurzoru k absolutnφ pozici - to provßdφ v²Üe definovanß funkce MoveCursor(). VÜimn∞te si, ₧e pou₧φvßme atributy lX a lY struktury DIMOUSESTATE. Struktura DIMOUSESTATE obsahuje nßsledujφcφ atributy:
LONG
lX;
lX a lY jsou p°φr∙stky ve sm∞ru osy x a y. lZ je p°φr∙stek otoΦenφ koleΦka myÜi (pokud myÜ nemß koleΦko, je tato hodnota rovna 0). Poslednφ pole rgbButtons[] obsahuje stavy Φty° tlaΦφtek. V poli je na prvnφm mφst∞ levΘ tlaΦφtko, pak pravΘ a na dalÜφch dvou indexech jsou dalÜφ tlaΦφtka, pokud myÜ n∞jakΘ mß. Pokud je po₧adovanΘ tlaΦφtko stisknuto, je v poli nenulovß hodnota jinak 0. V poslednφm kroku funkce, provedeme tzv. clipping kurzoru. Musφme zabrßnit tomu, aby kurzor mohl vyjet mimo obrazovku. KoneΦn∞ vyu₧ijeme rozliÜenφ, kterΘ jsme si ulo₧ili. A to je vÜe. Dßle si rozeberme funkci UpdateCursor(). I k tΘto funkci budeme pot°ebovat jednu funkce navφc. Bude to SetCursor(), kterß nastavφ handle kursoru, kter² se bude vykreslovat na pozici urΦenΘ atributem m_ptCursor. Op∞t se jednß o jednoduchou inline funkci: HRESULT SetCursor(HICON hCursor) {m_hCursor = hCursor; return 0;} Pouze p°i°adφme nov² handle.
Samotnß funkce UpdateCursor() bude takΘ celkem
primitivnφ: Nejd°φve otestujeme sprßvnost handlu m_hCursor a zobrazφme kurzor jen kdy₧ mß b²t skuteΦn∞ zobrazen (m_bShowCursor). Jako druh² a poslednφ krok zavolßme funkci DrawIcon(), kterß vykreslφ po₧adovan² kurzor na sprßvn²ch sou°adnicφch. Mo₧nß si te∩ prßv∞ myslφte, proΦ nevyu₧it k vykreslenφ kurzoru DirectDraw. Bylo by sprßvnΘ to tak ud∞lat, ale v souΦasnΘ dob∞ mßme k≤d DirectDraw v modulu .exe souboru a ten vyu₧φvß knihovny Input.dll. Tudφ₧ Input.dll nem∙₧e b²t zßvislß na .exe souboru, kde je pot°ebnΘ DirectDraw. Pozd∞ji projekt upravφme tak, ₧e i DirectDraw bude v samostatnΘ knihovn∞ a potΘ budeme kurzor vykreslovat pomocφ DirectDraw. 10.2. Exportovßnφ funkcφNynφ jsme dosp∞li do stavu, kdy nßÜ systΘm DirectInput mß n∞co d∞lat a pot°ebovali bychom to vyzkouÜet. To znamenß, ₧e pot°ebuje volat funkce jako je ProcessInput() a UpdateCursor(). Toto je dß provΘst n∞kolika zp∙soby (o t∞chto zp∙sobech jsem psal v minul²ch lekcφch). Vytvo°me tedy globßlnφ objekt CInput: CInput g_theInput; Tento °ßdek napiÜte na zaΦßtek souboru input1.cpp. Nynφ do hlaviΦkovΘho souboru input1.h doplnφme seznam exportovan²ch globßlnφch funkcφ:
INPUT_API HRESULT
inpCreateDirectInputSystem(HINSTANCE hInst, HWND hWnd, CSize csResolution); Konstanta INPUT_API je definovßna na dvou mφstech r∙zn∞. V implementaΦnφ souboru input1.cpp definujte konstantu takto: #define INPUT_API __declspec(dllexport) Tuto definici musφte provΘst p°ed vlo₧enφm hlaviΦkovΘho souboru input1.h. Za druhΘ p°ipiÜte nßsledujφ °ßdky na zaΦßtek souboru input1.h:
#ifndef INPUT_API Tyto °ßdky za°φdφ to, ₧e pokud se pokusφme vlo₧it tento hlaviΦkov² soubor mimo knihovnu input.dll, nadefinuje se makro COMMON_API pro import v²Üe uveden²ch funkcφ. Naopak v modulu knihovny je konstanta definovßna pro export funkcφ. Od te∩, pokud chcete pou₧φt funkce s prefixem inp, musφte pouze vlo₧it hlaviΦkov² soubor input1.h. Zb²vß jen nadefinovat exportovanΘ funkce. Definice bude velmi jednoduchß. StaΦφ vyu₧φt globßlnφho objektu k volanφ jednotliv²ch metod:
// Dßle upravφme druh² projekt DirectDraw tak, abychom koneΦn∞ mohli zavolat novΘ funkce. Do souboru control.cpp vlo₧te hlaviΦkov² soubor input1.h tφmto zp∙sobem:
#include "..\Input\Input1.h" Do funkce InitDD() p°idejte nßsledujφcφ °ßdky:
dwResult = inpCreateDirectInputSystem(AfxGetInstanceHandle(),
hWnd, CSize(RES_X, RES_Y));
ZaprvΘ je nutno zavolat funkci, kterß zinicializuje DirectInput. Funkce
AfxGetInstanceHalndle() vracφ handle instance, co₧
je p°esn∞ to, co pot°ebujeme. Dßle posφlßme handle okna a nakonec strukturu
CSize s rozliÜenφm obrazovky. V druhΘm kroku nastavφme kurzor tzn., ₧e nahrajeme
po₧adovan² kurzor do pam∞ti a nastavφme handle funkcφ
inpSetCursor(). Kurzor p°idejte do zdroj∙ projektu DirectDraw do slo₧ky
Icons.
void CControl::UpdateFrame()
//... Na zaΦßtku zavolßme funkci inpProcessInput() a na konci p°ed prohozenφm buffer∙ inpUpdateCursor(). Po zkompilovßnφ a spuÜt∞nφ vskutku uvidφte kurzor (poznßmka: myÜ musφ mφt nastaven² cooperative level DISCL_EXCLUSIVE, jinak bude d∞lat neoΦekßvanΘ v∞ci). Pou₧φvßm funkce pro zφskßnφ a uvoln∞nφ kontextu za°φzenφ naÜeho zadnφho bufferu (co₧ je v tuto chvφli nejlepÜφ °eÜenφ, i kdy₧ jsou funkce pomalΘ). Pokud bychom pou₧ili kontext za°φzenφ okna, kurzor by problikßval. Nezapome≥te kontext za°φzenφ uvolnit. JeÜt∞ jsme zapomn∞li na jednu funkci. Je to funkce ShowCursor(), kterß podle parametru bu∩ zobrazφ Φi skryje kurzor. Jist∞ tuÜφte, ₧e funkce bude velmi krßtkß. Zkrßtka nastavφ sv²m jedin²m parametrem atribut t°φdy m_bShowCursor. I tuto funkci exportujte z knihovny: input1.h: void ShowCursor(BOOL bShow = TRUE) {m_bShowCursor = bShow;}
INPUT_API void inpShowCursor(BOOL bShow =
TRUE); input1.cpp:
void inpShowCursor(BOOL bShow) 10.3. Detekce stisku klßvesy a tlaΦφtka myÜiBudeme pokraΦovat v implementaci zbyl²ch funkcφ, kterΘ zajiÜ¥ujφ detekci stisku a¥ klßvesy na klßvesnici nebo tlaΦφtka myÜi. Jsou to funkce:
IsKeyDown() Abychom mohli obslou₧it jak klßvesnici tak myÜ, musφme mφt dalÜφ pole, kde bude nastaveno zda-li byla klßvesa obslou₧ena Φi nikoliv (TRUE nebo FALSE). Funkce IsRButtonDown():
// Definuji je rovnou pro ob∞ tlaΦφtka, proto₧e je z°ejmΘ, ₧e ob∞ funkce budou toto₧nΘ prßv∞ a₧ na tyto konstanty. Nynφ se koneΦn∞ dostaneme k principu funkce. Prvnφ podmφnka (1) zjiÜ¥uje zda-li je pravΘ resp. levΘ tlaΦφtku stisknutΘ. Testujeme hodnotu v poli rgbButtons, o kterΘm ji₧ byla °eΦ. Pokud nenφ ₧ßdnΘ tlaΦφtko stisknutΘ (8), musφme vynulovat obsluhu, abychom zaznamenali nov² stisk. V p°φpad∞, ₧e tlaΦφtko stisknutΘ je, musφme zjistit, zda-li chce u₧ivatel pou₧φt obsluhu (2). Pokud ne (7), jednoduÜe vrßtφme TRUE. ProblΘm nastßvß, kdy₧ u₧ivatel po₧aduje obsluhu. Zase tak velk² problΘm to nenφ. Zkrßtka se podφvßme do obslu₧nΘho pole (3) a zjistφme, jestli u₧ tlaΦφtko bylo obslou₧eno Φi nikoliv. Pokud ano, vracφme FALSE (6) a pokud ne vracφme TRUE (5), ale navφc je pot°eba nastavit (4), ₧e tlaΦφtko bylo prßv∞ obslou₧eno. Funkce IsRButtonUp(): return (m_MouseState.rgbButtons[MOUSEBUTTON_LEFT]) ? FALSE : TRUE; Tato funkce je proti svΘmu opaku velmi jednoduchß. Prost∞ jen vracφme TRUE, pokud je tlaΦφtko naho°e a FALSE pokud je stisknutΘ. Op∞t jsou funkce pro pravΘ a levΘ tlaΦφtko identickΘ. Zb²vß metoda IsKeyDown():
// Funkce je v principu naprosto stejnß jako u myÜi. Musφme ovÜem definovat makro KEYDOWN: #define KEYDOWN(name, key) (name[key] & 0x80) Makro zjistφ hodnotu v obslu₧nΘm poli klßvesnice, kterΘ mß tentokrßt 256 prvk∙ (pro ka₧dou klßvesu jeden). Na zaΦßtku je test vstupnφho parametru, kter² pouze hlφdß, aby u₧ivatel nep°esßhl meze pole. Zbytek funkce je naprosto analogick². VÜech 5 uveden²ch funkcφ budeme exportovat: input1.h:
INPUT_API BOOL inpIsRButtonDown(BOOL _Handling
= TRUE); input1.cpp:
BOOL inpIsRButtonDown(BOOL _Handling) 10.4. Funkce RestoreDevices()A je tu ·pln∞ poslednφ funkce! Je to funkce, kterß obnovφ p°φstup k za°φzenφm, kdy₧ aplikace ztratφ fokus (vlastn∞, kdy₧ ho op∞tovn∞ dostane). Funkce bude velmi jednoduchß:
HRESULT CInput::RestoreDevices() Prost∞ zavolßme funkci Acquire() pro ka₧dΘ za°φzenφ, kterΘ mßme. I tuto funkci exportujte. To je vÜe k naÜφ t°φd∞. Nynφ jeÜt∞ malinko upravφme projekt DirectDraw, abychom vyzkouÜeli detekΦnφ funkce. Vlo₧te hlaviΦkov² soubor input1.h rovn∞₧ do souboru directdraw.cpp a pak funkci MainWndProc() upravte takto:
LRESULT CALLBACK MainWndProc( HWND hWnd,
UINT msg, WPARAM wParam, LPARAM lParam ) Tato ·prava zajistφ sprßvnou funkΦnost za°φzenφ i pro ztracenφ a op∞tnΘm navrßcenφ fokusu okna. Na ·pln² zßv∞r jeÜt∞ drobn∞ upravte funkci UpdateFrame() takto:
void CControl::UpdateFrame()
//... VÜimn∞te si, ₧e naÜe funkce inpIsKeyDown() p°ijφmß parametr DIK_SPACE. To je konstanta, kterß je unikßtnφ pro ka₧dou klßvesu. ┌pln² seznam t∞chto konstant najdete na tΘto strßnce. VyzkouÜejte si, co by program d∞lal, kdybyste nepou₧ili obsluhu (funkce bude naprosto nepou₧itelnß). Po tΘto ·prav∞ zm∞nφ raketka sm∞r po ka₧dΘm stisku mezernφku. DneÜnφ k≤d si samoz°ejm∞ m∙₧ete stßhnout jako obvykle v sekci Downloads. 10.5. Zßv∞rTφmto dφlem jsme ·pln∞ dokonΦili kurz DirectInput, kter² samoz°ejm∞ nebyl zcela ·pln² a podrobn², ale zßklady jsme zvlßdli. Jak jsem naznaΦil minule i na zaΦßtku dneÜnφ lekce, hodlßm p°φÜt∞ zaΦφt v∞tÜφ p°φklad, kde kompletn∞ vyu₧ijeme t°φdy CInput avÜak mohutn∞ upravφme DirectDraw. T∞Üφm se p°φÜt∞ nashledanou. © 2001 Vogel Publishing, design by ET NETERA
|