Rozhraní Direct Input II. díl
V minulém čísle jsme nakousli problematiku rozhraní DirectInput, které je
součástí standardu firmy Microsoft DirectX. Zde jsme tuto relativně složitou
problematiku načali částí věnovanou inicializaci a úvodu do práce s klávesnicí.
V dnešním díle celou tuto část dokončíme popisem vlastní práce s klávesnicí,
podíváme se také na přepínání aplikací, ovládání myši a v neposlední řadě na
joystick, a to včetně force feedbacku (zpětné vazby).
Práce s klávesnicí
Na začátku je třeba zavolat di_keyboard->Acquire() a na konci práce je nutné
klávesnici uvolnit voláním funkce di_keyboard->Unacquire(). Nejlépe nám tyto
požadavky splní makro s názvem DEV_RELEASE. Důležité je také ošetřit možné
přepínání mezi aplikacemi (alt+Tab), při kterém obvykle o klávesnici přijdete.
Jak ukazuje následující příklad, projevuje se tato změna chybovým kódem
DIERR_INPUTLOST a musíte ji ošetřit znovuzavoláním metody Acquire().
Podívejme se nyní na vlastní čtení z klávesnice, které můžeme naprogramovat
dvěma různými způsoby. Jednodušší variantou, ale nepříliš vhodnou, je číst stav
celé klávesnice. Druhou variantou je sledování změn na klávesnici což je sice
malinko složitější, ale rychlejší a vhodnější.
Čteme všechno
Podívejme se nejprve na první, jednodušší řešení, které si asi nejlépe
přiblížíme pomocí následujícího jednoduchého příkladu:
char keys[256];
while(1) { HRESULT hr; //stav klávesnice načteme do pole keys
if(FAILED(hr=di_keyboard->GetDeviceState(256,keys))) { if(hr==DIERR_INPUTLOST)
{ di_keyboard->Acquire(); continue; } else ; //chyba } break;
}
Z příkladu je vidět, že většinu práce za nás odvede funkce GetDeviceState(),
která nám naplní pole keys. V těchto 256 bajtech jsou poté obsaženy informace o
celé klávesnici. V jednotlivých bajtech pak dochází k nastavení nejvyššího bitu
v případě stisku určité klávesy, dané pořadím v poli. Vlastní kódy kláves jsou
pak dány konstantami s prefixem DIK_, jejichž seznam najdete v programové
dokumentaci a jejich význam je většinou jasný. Například jestliže potřebujete
zjistit, zda došlo ke stlačení klávesy Enter, můžete použít tento kód:
if(keys[DIK_ENTER]&128) /*je stisknuta klávesa Enter*/ ;
Na závěr se nesmí zapomenout na jeden drobný problémek:
Metoda Acquire() selže vždy, když máte nastavený režim DISCL_BACKGROUND a okno
vaší aplikace není aktivní. Na druhou stranu se nesmí zapomenout na skutečnost,
že při nastavení výhradního režimu DISCL_ EXCLUSIVE nemůžete klávesnicí ovládat
menu apod.
Změny na klávesnici
Je jasné, že předchozí varianta je sice funkční, ale asi není tou nejlepší.
Daleko výhodnější je použít variantu dotazování se na změny klávesnice. Pod
pojmem změny jsou myšleny události jako "stisk klávesy" a "uvolnění klávesy".
To je výhodné v případě, že chcete pomocí DInput ignorovat opakování klávesy
vzniklé déle trvajícím stiskem, a donutit tak uživatele k jejímu mačkání, což
je výhodné zejména u akčních her.
Podobně jako v předchozím případě se podíváme rovnou na příklad, ze kterého by
mělo být relativně vše jasné. Zajímavá je již zmiňovaná inicializace metodou
Acquire v případě přepnutí aplikace; vlastní zpracování pak probíhá v cyklu
for, kdy jsou zkontrolovány všechny změny. Náznak případného zpracování vidíte
v komentářích nejprve tedy zjistíte, jestli došlo ke stlačení nebo uvolnění
klávesy (to je uloženo v části dwData), a pak určíte, o jakou klávesu vlastně
šlo (část dwOfs obsahuje kód klávesy, který musíte porovnat s konstantami s
prefixem DIK_).
HRESULT r;
DIDEVICEOBJECTDATA kdata[KBD_BUFSIZE];
DWORD keys; //počet kláves na vstupu
//vezme data z bufferu DInput
keys=KBD_BUFSIZE;
r=di_keyboard->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),kdata,&keys,0);
//když je chyba INPUTLOST nebo BUFFEROVERFLOW
//musíme volat Acquire() a předpokládáme všechny klávesy
//za nestisknuté
if(r==DI_BUFFEROVERFLOW||r==DIERR_INPUTLOST) { di_keyboard->Acquire(); } else {
//znaky z bufferu se zpracují DIDEVICEOBJECTDATA *kd=kdata; for(int a=0;
a<keys; a++,kd++) { //kd->dwData & 128 = příznak stisku //kd->dwOfs = číslo
klávesy DIK_něco } }
Přepínání aplikací
Obecně platí, že po přepnutí aplikace můžete z různých důvodů ztratit přístup
ke kterémukoliv vstupnímu zařízení. Proto je nutné u každé funkce testovat
návratovou hodnotu (kód chyby). Pokud je hodnota DIERR_ INPUTLOST, musíte znovu
zavolat Acquire() a opakovat volání poslední funkce. Pokud tímto způsobem
"ztratíte vstup", zmizí také seznam změn, ke kterým došlo na vstupním zařízení.
U klávesnice se jedná o jednoduché řešení stačí to ošetřit tak, že
předpokládáte, že žádná klávesa není stisknuta. Podobně postupujete u myši, kdy
je třeba načíst aktuální pozici polohovacího zařízení, anebo se tohoto problému
můžete vyvarovat tak, že nebudete číst změny myši, ale pouze její stav (což
ovšem můžete udělat i u klávesnice).
Nejdůležitější pro správnou funkci DInput je vždy při přepínání aplikací
nezapomenout na nutnost zavolat metodu Acquire() při znovuaktivování programu.
Je tedy třeba doplnit příslušný kód do obsluhy zprávy WM_ ACTIVATE.
Ovládání myši
Ovládání myši se příliš neliší od postupu, který jsme použili u klávesnice
včetně přístupu, ke sledování jejího pohybu. Nejprve tedy vytvoříme objekt myši
stejným způsobem jako u klávesnice. To prakticky znamená zavolat konstrukci
podobnou následujícímu fragmentu kódu:
if(create_device(di_mouse,GUID_SysMouse,&c_dfDIMouse, hwnd, DISCL_FOREGROUND|
DISCL_EXCLUSIVE)) { ; //chyba
}
Poté je nutné zahájit práci s myší podobně, jako tomu bylo u klávesnice voláním
metody Acquire():
di_mouse->Acquire();
V tomto případě bude myš v režimu DISCL_EXCLUSIVE, kdy se nekreslí kurzor myši
a aplikace se de facto tváří, že myš ani neexistuje.
Zjišťování stavu myši
Na rozdíl od klávesnice myš nemá statický stav, stavem myši chápeme polohu
tlačítek a změnu souřadnic. Na rozdíl od myší, se kterými se normálně setkáte,
podporuje rozhraní DInput standardně tři souřadnice myši (x,y,z) a čtyři
tlačítka. V nejnovější verzi DirectX 7.0 je tato podpora u myši rozšířena až na
8 tlačítek.
Vlastní zjištění stavu myši je velmi jednoduché. Na rozdíl od klávesnice však
obdržíme v případě souřadnic změnu od posledního volání, ne statickou hodnotu.
Konkrétní volání si opět představíme na fragmentu kódu:
DIMOUSESTATE state;
while(1) { HRESULT hr; hr=di_mouse->GetDeviceState (sizeof(state),&state);
if(FAILED(hr)) { if(hr==DIERR_INPUTLOST) { di_mouse->Acquire(); continue; }
else return; //chyba } break;
}
Vlastní zpracování si předvedeme na jednuchém příkladu, který by tisknul
aktuální koordináty spolu s příznaky stavu jednotlivých tlačítek:
//vytvoříme textový řetězec informující o stavu myši
char text[200];
sprintf(text,"x=%i, y=%i, z=%i, but=[%i,%i,%i,%i]", state.lX,state.lY,state.lZ,
state.rgbButtons[0],state.rgbButtons[1], state.rgbButtons[2],state.rgbButtons[3]
);
Problémy pro leváky
U myši byste neměli zapomenout na možnost ovládání levou rukou, na což možná
samotní návrháři DirectX zapomněli. DInput totiž nepodporuje nastavení myši pro
levou ruku, tzn. první tlačítko je vždy levé, druhé je pravé a třetí je
prostřední. V případě, že chcete podporovat i levoruké myši, musíte nejdříve
pomocí volání Win32 API zjistit, zda je myš v levorukém režimu, a potom na tuto
skutečnost brát ohled ve svém vlastním programu.
Další možnou komplikací je práce s režimem DISCL_ EXCLUSIVE, kdy je nejvyšší
opatrnost opravdu na místě. Abyste totiž mohli používat menu, musíte při jeho
vyvolání (k tomu slouží zpráva WM_ ENTERMENULOOP) zavolat metodu Unacquire() a
při jeho opuštění (zpráva WM_ EXITMENULOOP) naopak opět volat metodu Acquire().
Ovládání joysticku
Poslední obvyklou možností, jak mohou vaši uživatelé interagovat s vaší
aplikací, je joystick. Ten se ovládá podobně jako myš. Nejprve tedy vytvoříte
vlastní objekt joysticku. Vlastní práce je pak o trochu složitější než u
klávesnice nebo myši, protože si musíme vyžádat interface IDirectInputDevice2,
které má důležitou metodu Poll(). Joysticky jsou podporovány až od verze
DirectX 5.
LPDIRECTINPUTDEVICE di_joy; //pomocná proměnná
if(create_device(di_joy,GUID_Joystick,&c_dfDIJoystick, hwnd, DISCL_BACKGROUND|
DISCL_NONEXCLUSIVE)) return 1; //chyba
di_joy->QueryInterface( IID_IDirectInputDevice2,(void**)&di_joystick);
di_joy->Release();
Dále je nutno klasickým způsobem zahájit práci s joystickem jak jinak než
voláním metody Acquire().
di_mouse->Acquire();
Zjišťování stavu joysticku
Na rozdíl od klávesnice máme v případě joysticku výrazně ulehčen výběr s
joystickem se pracuje výhradně způsobem "zjišťování stavu". Většina joysticků
neumí poskytovat informace o změnách stavu, jak to známe z klávesnice. A právě
proto se nám bude hodit ona již zmiňovaná funkce Poll(), kterou musíme volat
vždy před vlastním zjišťováním.
if(di_joystick->Poll()==DIERR_NOTACQUIRED) { if(FAILED(di_joystick->Acquire()))
return; //chyba di_joystick->Poll();
}
DIJOYSTATE state;
//načteme stav joysticku
di_joystick->GetDeviceState(sizeof(state),&state);
//sestavíme informační textový řetězec
char text[200];
sprintf(text,"x=%i, y=%i, but=[%i,%i,%i,%i]", state.lX,state.lY,
state.rgbButtons[0],state.rgbButtons[1], state.rgbButtons[2],state.rgbButtons[3]
);
Všimněte si, že první metoda může opět selhat a je třeba jako obvykle znovu
volat Acquire(). Ovládání joysticku přes DInput podporuje tři osy, rotace os,
dva slidery, čtyři směrové kloboučky a až 32 tlačítek.
Force feedback
Force feedback je speciální funkce, se kterou se setkáte zejména u dražších
joysticků a volantů (volanty se ovládají stejně jako joysticky). Tato funkce,
jak už sám název napovídá, funguje jako zpětná vazba. Aplikace tedy pomocí
force feedbacku nastavuje chování joysticku, obvykle za účelem simulace
fyzického odporu zařízení. Podrobný popis ovládání force feedbacku je již nad
rámec tohoto textu.
Závěr
Jak je vidět z předchozích odstavců, tak zpracování vstupů pomocí DirectInput
není nic složitého a jednotlivá zařízení se od sebe ani moc neliší. Zpracování
událostí klávesnice, myši a joysticku tak může obohatit vaši aplikaci o nový
rozměr samozřejmě je asi nepoužijete při tvorbě informačního systému, ale u her
se jedná o opravdovou nezbytnost.
0 1835 / alsn