Náš trojdílný seriál zakončíme dvěma díly pohromadě, protože klávesnice a myš k sobě dnes již neodmyslitelně patří, ať už se jedná o standardní práci s počítačem (kde se spíše využívá myš), psaní textů (kde je naopak sahání po myši nežádoucí) či hraní her (kde jsou vytíženy obě na maximum). Začneme asi tím nejjednodušším, a to je myš. Možná se Vám bude zdát, že to není pravda, ale uvidíte, že kompletně ovládat myš je na assembler mnohem snažší než hákovat přerušení od klávesnice a hlavně jej číst. A nemusím doufám podotýkat, že příklady s přerušením vyžadují jednotku DOS a u Free Pascalu ještě GO32. Pokud Vám u některých příkladů nebude fungovat kombinace "interrupt; assembler; far;", tak to buď napište jako "interrupt assembler far;" a nebo napište jen "interrupt; assembler;" a před řádek FUNCTION dejte {$F+} a za řádek END; dejte {$F-}. Efekt to bude mít stejný.
Myš se dnes připojuje k portu PS/2, ty novější i k USB a ty starší ještě ke COMu. Vždy ale platí, že pokud je určena pro DOSové aplikace, tak tu musí být ovladač (alespoň pro COM a PS/2), který sídlí na přerušení INT 33h a tam poskytuje své služby. Není tedy nic jednoduššího, než prostě toto přerušení volat a dané informace si získat. Začneme v TP7, protože je to jednodušší na vysvětlení. Předně, než začnete myš používat, měli byste jí resetovat. Jsou dva typy resetů. Ten "teplý" (softwarový) je rychlejší, ale může se stát, že ho některé myši neakceptují. Pak je tu "studený" (hardwarový), který je pomalý, ale za to spolehlivý. Většinou stačí myš resetovat jen jednou, takže je možné provést ten studený. Nebo, zkusit teplý a pokud se myš nechytne, pak studený. A později pak už přímo ten, na který se myš ozvala. Tato funkce je také důležitá v tom, že nám vrací i počet tlačítek, které může myš v současné době používat (to ovšem neznamená, že jich nemá víc).
function ResetMysi : word; assembler; asm xor ax,ax {číslo služby: 0-HW, jinak 21h-SW reset} int 33h {volání ovladače myši na INT 33h} or ax,ax {je AX = 0? (Rychlejší nez CMP AX,0?)} jne @ok {není, v pořádku, myš je přítomná} mov bx,128 {myš není, nastavíme počet tlačítek na 128} @ok: mov ax,bx {vrátíme zjištěný počet tlačítek} end;
Pokud je počet tlačítek 1, 2 nebo $ffff (je vrácen v BX), jedná se o dvoutlačítkovou myš. Pokud je ovšem AX po provedení služby rovno 0, není myš nainstalována (pozor! U služby 21h SW Reset musí být pro správné vykonání v AX $21 před i po INT 33!). Služba 0 má samozřejmě také více funkcí, které provádí, ale ty si můžete najít v ATHelpu v popisu daného přerušení. Občas se stane, že na adrese, kde by měl být ukazatel na ovladač myši, je 0:0 a tedy pokud byste zavolali INT 33h, pravdpodobně byste skončili v Resetu počítače nebo úplně jinde... Proto je vhodné nejprve ještě otestovat, zda ta adresa ukazuje na smysluplnný kód:
function ResetMysi : word; assembler; asm xor ax,ax mov ds,ax mov si,33h*4 cmp word ptr ds:[si+2],0 {ukazuje do segmentu 0, tedy blbost} jz @neni lds si,dword ptr ds:[si] cmp byte ptr ds:[si],0cfh {na adrese je RETF, ovladač není instalován} jz @neni xor ax,ax int 33h or ax,ax jne @ok @neni: mov bx,128 @ok: mov ax,bx end;
A teď, jak zjistit, kde se myš nachází a jaká má zrovna stisknutá tlačítka? Ovladač má na toto všechno jednu funkci, která zjistí vše potřebné. Avšak my většinou potřebujeme číst jen jednu proměnnou, takže ji musíme rozepsat do třech funkcí:
function MysX : word; assembler; asm mov ax,3 int 33h mov ax,cx end; function MysY : word; assembler; asm mov ax,3 int 33h mov ax,dx end; function MysTlac : word; assembler; asm mov ax,3 int 33h mov ax,bx end;
Jen pár poznámek. Souřadnice myši jsou vztažené ke standardnímu grafickému režimu, který je standardně 320x200 (pokud jej máte nastaven) nebo 640x200 a v tom je dneska problém, neboť většina z nás určitě využívá větší rozlišení, na které myš v určitém slova smyslu kašle. Jak to změnit si povíme později. Pokud pracujete v textovém režimu, tak musíte všechny souřadnice myši vydělit 8 pixely (resp. 16 v ose Y), a tedy upravit funkce takto:
function MysXtext : word; assembler; asm mov ax,3 int 33h mov ax,cx shr ax,3 end; function MysYtext : word; assembler; asm mov ax,3 int 33h mov ax,dx shr ax,4 end;
Tím dostanete pozici ne v pixelech, ale v řádcích a sloupcích textu. A co se týče funkce pro tlačítka, tak tady platí, že pokud je bit = 1, je tlačítko stisknuté, pokud 0, je zrovna puštěné. Levé tlačítko má vždy bit 0, pravé bit 1 a prostřední bit 2. Pokud chcete zjistit dvojklik, musíte testovat tlačítka postupně a zjišťovat, jestli bylo uvolněno a stisknuto, nebo použít funkce pro zjištění počtu uvolnení a stisku tlačítka (viz. funkce 5 a 6 v ATHelpu u přerušení $33). Pokud pracujete v textovém nebo nižším grafickém režimu (většinou jen 16 barev), může ovladač zajišťovat zobrazování kurzoru myši na obrazovce sám. K tomu existují dvě funkce, které skryjí a nebo zobrazí kurzor. Nutno podotknout dvě věci: některé ovladače mají počitadlo, tedy pokud např. 2x po sobě kurzor skryjete, musíte ho poté 2x zobrazit, než bude vidět. Jiné toto ignorují a berou vždy jen první výskyt volání takové funkce, proto je ideální to volat vždy jen 1x a nastřídačku. Ta druhá věc je, proč to vlastně používat. Jde o to, že obrazovku si jistě vykreslujete sám, ale kurzor kreslí ovladač. V tomto případě předtím, než začnete kreslit, musíte myš schovat, a až kreslení dokončíte, ji můžete zobrazit. Pokud byste to neudělali zrovna takto, tak poté, co byste dokreslili a někdo by hýbnul myší, tak by ovladač automaticky obnovil znak (či skupinu pixelů) po kurzorem na ten původní (který si ovšem načetl ještě předtím, než jste něco nakreslili) a pak by přesunul kuzor jinam. Výsledkem by bylo, že pod kurzorem by se objevilo to, co tam bylo před tím, než jste to smazali překreslením.
procedure Zobraz; assembler; asm mov ax,1 int 33h end; procedure Skryj; assembler; asm mov ax,2 int 33h end;
Jak vidíte, není to žádná věda. Menší problém bude asi v tom, že Vám bude kurzor jakoby blikat, když budete kreslit. Ale to vyřešíme později. Některé z Vás ale může třeba nudit volat neustále tyto funkce. Můžete se tzv. pověsit na časovač, který standardně 18.2x za vteřinu zjistí informace o myši a uloží je do nějakých Vašich proměnných. Tato metoda není ideální, neboť se myš zbytečně volá X krát za vteřinu, ale umožní nám se naučit používat přerušení, které později využijeme u klávesnice. Nejprve si musíme nadefinovat proměnné, které budeme potřebovat:
var X,Y,Tlac,Back : word;
Asi se divíte, k čemu je ta poslední proměnná. Ta obsahuje minulý stav tlačítek, který použijeme pro zajímavý trik. Jistě víte, že když kliknete myší na nějaké tlačítko ve Windows či Linuxu, tak se akce provede až poté, co tlačítko pustíte. Pokud jej stále držíte a přesunete se jinam, akce se neprovede. A to se nám nyní povede udělat (lze to samozřejmě udělat i s pomocí předchozích funkcí). Předpokládám, že víte, jak se dělá přerušovací rutina v Turbo Pascalu 7:
procedure InfoMys; interrupt; assembler; far; asm mov ax,back {záloha předchozího stavu tlačítka} mov back,ax mov al,20h {resetování řadiče přerušení} out 20,al mov ax,3 int 33h mov X,cx mov Y,dx mov Tlac,bx call Int1CSave {voláme původní obsluhu časovače} end;
Teď nám stačí upravit stávající funkce a připsat si některé další:
function MysX : word; assembler; asm mov ax,x end; function MysY : word; assembler; asm mov ax,y end; function MysTlac : word; assembler; asm mov ax,tlac end;
Pokud pracujete ve Free Pascalu, je vhodné tyto funkce napsat jako inline. Nové funkce otestují, zda je konkrétní tlačítko stisknuté, a ta druhá navíc otestuje, zda jej uživatel již pustil. Ta první se hodí, pokud chcete myší kreslit, ta druhá, pokud chcete ovládat tlačítka na ploše stejně jako v GUI. Číslo tlačítka je 1 pro levé, 2 pro pravé a 4 pro prostřední (i pokud je prohodíte, stále je 1=levé, i když je primární pravé!).
function Tlac(Cislo : word) : boolean; assembler; asm mov bx,cislo xor al,al test tlac,bx jz @skok inc al @skok: end; function Stisk(Cislo : word) : boolean; assembler; asm mov bx,cislo xor al,al test back,bx jz @konec test tlac,bx jnz @konec inc al @konec: end;
Myš nám zobrazuje nějaký kurzor. Co když ale chceme vlastní? K tomuto slouží funkce $9 a $A na INT 33h. Protože se ale moc často nepoužívají, nebudu je zde uvádět (pokud Však o ně máte zájem, ATHelp je Vám k dispozici). Snad jen podotknu, že se kurzor vždy kreslí pomocí masek AND a XOR. Nejprve se aplikuje AND, tj. pokud je daný bit v masce 1, převezme se jeho hodnota z pozadí, jinak se nastaví na 0. Poté se aplikuje XOR, které změní všechny bity z 0 na 1 a obráceně. Tedy může buď zobrazit samotný kurzor (pokud je maska AND rovna 0), nebo např. invertovat pozadí (pokud je celá AND rovna 1), vždy pro celou XOR masku rovnou 1 (resp. $ff na BYTE). Grafický kurzor má dvě masky o šířce 16x16 pixelů v 1 bitové barvě, tedy celkem 64 bytů (2 sady po 16 řádcích, na každém z nich 16 bitů, nebo-li 2 byty). Textový kurzor může být hardwarový, tj. blikající, kde nastavujete první a počáteční linku, viz. Klávesnice a její kurzor) nebo softwarový, kdy se nejprve aplikuje maska AND na znak a atribut, a poté maska XOR na to samé. Pokud např. chcete jen invertovat barvu znaku včetně jeho pozadí a jej samotný ponechat, nastavte AND pro atribut na 0, pro znak na $ff, a pro XOR to dejte opačně (v tomto případě se ale změní ovšem i bit blikání, resp. rozšířené barvy pozadí, pokud je nastavena). Ve většině grafických aplikací se však používá kurzor vlastní, kdy je myš skryta a programátor si od ní bere jen souřadnice a stav tlačítek. Samotný kurzor si kreslí sám jako obrázek (popř. ho postupně mění a tím dostane animaci). Při prvním spuštění programu, resp. změně grafiky si načtěte pozadí pod prvním objevením myši. Resp. uschovejte si oblast video paměti o velikosti kurzoru. Poté na toto místo vykreslete myš. Když se myš pohne, obnovte pozadí tím uschovaným a celou sekvenci opakujte pro nové souřadnice. Velice snadné a přitom efektivní.
Jak vidíte, je to celkem jednoduché. Jak ale vytvořit tlačítka na ploše, na která by myš mohla klikat? Jednoduše. Každé z nich bude mít 5 stavů: tlačítko není vidět, tlačítko je vidět, myš je nad tlačítkem (mohlo by se zesvětlit), myš stiskla tlačítko (mohlo by se prohnout, pokud myš najede mimo tlačítko, to se změní dočasně na "tlačítko je vidět", pokud stále držíme tlačítko a vrátíme se nad tlačítko, to se opět prohne, ale žádnou akci stále nevyvolá, dokud tlačítko také nepustíme nad jeho oblastí), a myš stiskla a pustila tlačítko nad tlačítkem (vyvoláme akci). Každé bude reprezentováno nějakým obrázkem (např. BMP), pokud to nebude textový režim. A vždy budete cyklicky testovat všechna nadefinovaná a podle jejich aktuálního stavu provedete nebo neprovedete nějakou akci (např. jejich vykreslení).
Jak ale zařídit, aby nám naše funkce pro přerušení zjišťovala dané hodnoty? Musíme ji na dané přerušení tzv. pověsit. Zvolíme si časovač $1C, neboť ten je pro tyto účely nejvhodnější. Předtím ale musíme uschovat adresu té rutiny, která tam už je, protože v tomto případě např. zajišťuje nastatovávání systémového času nebo zastavování disketové mechaniky. A abychom nemuseli před koncem programu myslet na to, že ji musíme zase odstranit, necháme Turbo Pascal, aby to provedl za nás. Stačí, když naši uklídovou proceduru pověsíme na výstupní kód programu.
var Int1CSave : pointer; KonecProc : pointer; Tlacitek : word; Mys : boolean; {$F+} procedure KonecMyse; {uklízecí procedura} begin ExitProc := KonecProc; SetIntVec($1C,Int1CSave); end; {$F-} procedure InitMys; {instalační procedura} begin Tlacitek := ResetMysi; Mys := Tlacitek <> 128; if Mys then begin GetIntVec($1C,Int1CSave); SetIntVec($1C,Addr(InfoMys)); {místo ADDR můžeme také použít @} KonecProc := ExitProc; end; end;
Zde se používají funkce jednotky DOS, takže jí musíme přidat do USES, jinak nám to nebude fungovat (pokud nechcete používat tuto jednotku, můžete si na mých WWW stránkách stáhnout její náhradu, která je sice zatím jen beta, ale základní funkce u ní už fungují, takže si je můžete zkopírovat). Pokud nerozumíte některým příkazům, není nic jednoduššího než si otevřít nápovědu přímo v Pascalu. Možná se ptáte, proč tu dosud není žádný kód ve Free Pascalu. To proto, protože jsme se stále nedostali k finálnímu kódu. Kód výše sice bude fungovat, ale rozhodně není moc vhodný, protože zbytečně zatěžuje procesor. Ale ten až později. Nejprve ještě to slíbené řešení s tím rozsahem souřadnic. Proč by někdo chtěl měnit citlivost myši? Jsou minimálně dvě příčiny. Buď se Vám myš pohybuje např. jen v horním levém výřezu obrazovky a nebo se pohybuje po příliš velkých skocích. Naštěstí i ovladač myši na toto myslí a proto můžete tyto parametry změnit. Pokud chcete např. zjemnit pohyb myši, stačí jen změnit citlivost. Ta se nastavuje tzv. jednotkách Mickeys/pixel. Jeden Mickey je 1/200 palce, tj. 0.0127 cm. Čím vyšší nastavíte číslo pro X nebo Y, tím rychleji se bude myš hýbat. Kromě rychlosti se nastavuje ještě zrychlení, tj. rychlost, při jejímž překročení se pohyb myši dočasně 2x zrychlí. Pokud jej nastavíte na 0, jedná se o 64. Tenkrát jde ale o mickeys/vteřinu ne na pixel. 64 mickeys/vteřinu se rovná asi 8 mm za vteřinu. Protože ale další programy před Vámi mohou mít nastavenu svou vlastní citlivost, měli byste při startu nejprve tu starou uschovat, pak nastavit svoji a před koncem programu obnovit tu původní.
procedure NastavCitlivost(X,Y,R : word); assembler; asm mov ax,1ah mov bx,x {v zásobníku jsou hodnoty} mov cx,y mo dx,r int 33h end; procedure CtiCitlivost(var X,Y,R : word); assembler; asm mov ax,1bh in 33h les di,x mov es:[di],bx {v zásobníku jsou ukazatele/adresy} les di,y mov es:[di],cx les di,r mov es:[di],dx end;
Ale stále jsme nevyřešili to, že nám myš skáče po velkých skocích, nebo že se pohybuje v příliš malém prostoru. To řeší funkce pro nastavení rozsahu. Ta používá minimální a maximální souřadnice, ve kterých se bude myš moci pohybovat. Standardně je rozsah 639x199, tj. až do tohoto rozlišení +1 je schopna myš se teoreticky pohybovat po 1 pixelu (v ideálním případě). Pokud Však máte vyšší rozlišení nebo se Vám myš i tak hýbe přes více pixelů i při sebe jemnějším pohybu, musíte změnit rozsah. První mez necháme na 0, ostatní nastavíme na velikost daného rozlišení. Takže za souřadnice X a Y dosaďte své rozlišení, např. 800x600.
procedure NastavRozsah(X,Y : word); assembler; asm mov ax,7 xor cx,cx mov dx,x dec dx int 33h inc ax mov dx,y dec dx int 33h end;
Pokud se Vám přesto zdá, že myš má citlivost stále malou, vynásobte souřadnice x2 nebo 4x (pak ale musíte také ty, které Vám myš vrátí tímto číslem vydělit, a také byste ji měli trochu zrychlit):
procedure NastavRozsah(X,Y : word); assembler; asm mov ax,7 xor cx,cx mov dx,x shl dx,1 dec dx int 33h inc ax mov dx,y shl dx,1 dec dx int 33h end;
Citlivost a rozsah je vhodné nastavovat až po změně rozlišení. Avšak některé druhy myší nezávisle na ovladači rozsah stejně nezmění. Naštěstí to jsou výjimky. V poslední době se do módy dostávají tzv. myši s kolečkem. Myslíte si, že to je výsada jen Windows? Omyl. Ovladač myši by měl kolečko podporovat také (ovšem většinou asi ne v DOSu, resp. pod zastaralým ovladačem). Nejprve se musíme přesvědčit, zda myš kolečko opravdu podporuje. To se provede následovně:
function Kolecko : boolean; assembler; asm xor dx,dx mov ax,$3f int 33h cmp ax,574dh jne @konec {API pro kolečko je přítomno} test cx,1 jz @konec inc dl {dokonce i myš má kolečko} @konec: mov al,dl end;
A teď, jak to kolečko vlastně přečíst. Pozice kolečka se čte stejnými funkcemi, jako stav normálních tlačítek a jejich os. Jeho hodnota se vrací v BH jako 8 bitové číslo se znaménkem (tj. ShortInt). A jedná se o tzv. naakumulovanou hodnotu od posledního čtení. Tedy pokud přečtete např. 10, a kolečko se pak nepohne, měla by tam příště být 0. Naopak pokud ho nebudete číst dostatečně často, může se stát, že se kolečko pohne tam a sem, překročí 2x nulu, ale Vy zjistíte jen ten poslední pohyb, resp. jejich součet. Kladná hodnota znamená, že se kolečko točilo obráceně, záporná normálně. Jeho hodnota se získá snadno:
function PohybKol : shorint; asm mov ax,3 int 33h mov al,bh end;
Teď už máme snad všechno. Je ale čas to přepsat na něco rychlejšího. Proč např. testovat neustále, zda se pohnula myš, když to může za nás udělat sám ovladač. Říká se tomu program na řízení událostí. Jednoduše se pověsíme na daný ovladač a on nás sám zavolá, když se něco semele. Jinými slovy, pokud se myš nehýbe, program si jí nevšímá a neztrácí čas testováním. Funkci musíme napsat se vzdáleným modelem, protože ovladač myši nemusí být v našem segmentu (a na 99% ani nebude). Buď to přikážeme Pascalu nebo si nakonec doplníme sami instrukci RETF. A aby nám myš neničila naše registry, napíšeme danou proceduru jako přerušení, čímž si pomůžeme ještě v jedné věci: TP7 za nás uschová registry. Pokud máme vracet nějaká data, musíme znát registr DS, ten nám ovšem myš laskavě přepíše na svůj segment. Co s tím? Musíme si ho někam schovat a to ještě, než vstoupíme do naší rutiny. Náš program bude vypadat nějak takto:
var Kolecko : shorint; procedure Handler; assembler; interrupt; far; asm push ds {oblast dat pro myš} mov ax,$f000 {viz.Install} mov es,ax {maska událostí je nám celkem volná} mov di,$fffc mov ax,es:[di] {získáme náš DS, pokud nám ho někdo nepřepsal} mov ds,ax mov ax,tlac mov back,ax mov tlac,bx mov kolecko,bh mov x,cx mov y,dx pop ds {vrátíme myši její DS, kdyby ho ještě chtěla} end; {spíše ho ale přepíše POPA z TP7 kvůli "přerušení"}
Ty instrukce PUSH a POP s DS tam jsou celkem zbytečné a myslím, že se vůbec nic nestane, když tam nebudou. Náš program musíme nejprve ale nainstalovat a po skončení našeho programu zase odinstalovat, jinak bude totiž fungovat i dál, bez nás a to nedopadne dobře:
var Puvodni : pointer; PuvMask : word; procedure Install(Adresa : pointer); assembler; asm mov ax,$14 mov cx,$ff {volání při všech událostech, včetně kolečka na bitu 7) mov dx,adresa.word[2] mov es,dx {do seg.reg.lze přímo dát jen konstantu} mov dx,adresa.word[0] int 33h {uschováme původní handler} mov puvodni.word[0],dx mov puvodni.word[2],es mov puvmask,cx mov ax,$f000 mov es,ax mov di,$fffc {na adrese $f000:$fffc by teoreticky nic být nemělo} mov ax,ds {pokud ano, tak třeba ještě 0:$4f0} mov es:[di],ax {uschováme si svůj DS kvůli myši} end; procedure UnInstall; assembler; asm mov ax,$14 {obnovíme původní obslužný program} mov cx,puvmask mov dx,puvodni.word[2] mov es,dx mov dx,puvodni.word[0] int 33h end;
Toto je sice dobré řešení (za předpokladu, že si zvolíme takovou adresu, kterou nám nikdo nebude přepisovat), ale něco tomu stále chybí (je to moc složité a navíc "prasárna"). Kdyby se Vám zdálo, že to je příliš zmatené, tak můžete zkusit ještě tuto variantu, která by údajně měla také celkem slušně fungovat:
procedure Install(Adresa : pointer); assembler; asm mov ax,$c mov cx,$ff les dx,adresa int 33h end; procedure Handler; assembler; interrupt; far; asm mov si,seg @data mov ds,si mov si,tlac mov back,si mov tlac,bx mov x,cx mov y,dx end; procedure UnInstall; assembler; asm mov ax,$c mov cx,0 int 33h end;
Tak, a už by nám to mělo fungovat. Tady bychom mohli klidně skončit. Pokud chcete ale znát více, čtěte dále.. Cco kdybychom si třeba napsali celý svůj celý ovladač na myš? Nejprve si definujeme některé nové proměnné (X,Y a TLAC necháme z minula):
const COM1INTR = $0c; COM1PORT = $3f8; var MouseHandler : pointer; {Můžete použít i typ PROCEDURE} ComBuffer : array[0..2] of byte; pocet : byte;
Abychom byli korektní, necháme si bázovou adresu sdělit přímo od BIOSu. Tedy řádek s COM1PORT přepíšeme takto:
var COM1PORT : word absolute 0:$400;
A teď, jak to celé funguje. Protože geniální věci bývají prosté a protože je myš geniální věc, funguje naprosto jednoduše: pokud se myš pohne, pošle po sériovém portu většinou 3 byty informací (některé myši 4 až 6). Takže stačí tyto informace pouze zpracovat. Protože se později navěsíme na obsluhu sériového portu, budeme mít myš jen sami pro sebe. Je nutné nejprve vědět, co nám ta myš vlastně posílá (podmínkou ovšem je, aby byla na sériovém portu! Na PS/2 myši toto zabírat nebude! Leda by byly tak inteligentní a jejich ovladač by simuloval i myš na sériovém portu, ovšem, proč by to dělal?). Myš typu MICROSOFT posílá standardně 3 byty po sobě:
bit: 7 6 5 4 3 2 1 0 byte 1 (sync) 0 1 L R y7 y6 x7 x6 byte 2 (dX) 0 0 x5 x4 x3 x2 x1 x0 byte 3 (dY) 0 0 y5 y4 y3 y2 y1 y0
První byte je takzvaný synchronizační. Pokud je jeho bit 6 = 1 (v tomto případě nejvyšší), pak musíte začít počítat byty od 0. Zároveň v něm také najdete první dvě tlačítka myši (1=tlačítko je puštěné!). V ostatních bytech jsou relativní souřadnice myši od posledního volání (+ je doleva nebo dolů podle osy). Pokud je MSB v daném bytu (po složení) = 1, jedná se o záporné číslo. Aby to nebylo tak jednoduché, myši značky MOUSE SYSTEMS posílají 5 bytů (poznají se tak, že mají nejvyšší bit (zde 7!) prvního byte roven 1 a 2 dvojice stejných bytů). Ty mají o trochu lépe organizovaná data, navíc umožňují čtení i středního (MB) tlačítka, avšak pozor, tentokrát 1=stisknuté. Co se týče pohybu, tak tady zase pro změnu + znamená vždy doprava nebo nahoru. A nesmíme zapomenout na myši, které posílají ještě informace o kolečku (který se posílá jako extra byte navíc, tedy ze 3 máte hned 4).
byte 1 1 0 0 0 0 LB MB RB byte 2 X7 X6 X5 X4 X3 X2 X1 X0 byte 3 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 byte 4 stejný jako byte 2 byte 5 stejný jako byte 3
Navíc musíte mít správně nastaven COM port:
Microsoft Mouse 1200 bps, 7 data bitů, 1 stop bit, bez parity Mouse Systems Mouse 1200 bps, 8 data bitů, 1 stop bit, bez parity
To se dělá pomocí přerušení $14 a služby 0:
procedure Nastav(info : byte); assembler; asm mov dx,1 {první COM port} xor ax,ax mov al,info int 14h end;
Kde BYTE "info" má toto složení:
bity 7-5: rychlost (pro myši 100) bity 4-3: parita (x0=žádná, 01=lichá, 11=sudá) bit 2: počet stop bitů minus 1 bity 1-0: délka slova/bytu (10=7 bitů, 11=8 bitů)
A teď už konečně napíšeme tu funkci, když už víme, jak na to. Pro zjednodušení budeme brát jen myši typu MS, protože většinou pod DOSem jiné neuvidíme (až na výjimky):
procedure MyMouseHandler; Interrupt; far; var dx, dy : integer; inbyte : byte; begin inbyte := Port[COM1PORT]; {přečteme, co nám myš chce} if inbyte and 64 = 64 then pocet := 0; {je to první byte} ComBuffer[Pocet] := inbyte; {uložíme data} inc(Pocet); if Pocet = 3 then {už máme všechny tři byty} begin dx := (ComBuffer[0] and 3) shl 6 + ComBuffer[1]; dy := (ComBuffer[0] and 12) shl 4 + ComBuffer[2]; if dx >= 128 then Dec(dx,256); if dy >= 128 then Dec(dx,256); {musíme převést číslo na záporné} inc(x,dx); inc(y,dy); {aktualizujeme souřadnice a tlačítka} Tlac := (ComBuffer[0] and 32 <> 32) or ((ComBuffer[0] and 16 <> 16) shl 1); Pocet := 0; end; Port[$20] := $20; {potvrdíme, že jsme zpracovali 8 bitové přerušení} end;
A teď ještě procedury, jak náš ovladač nainstalovat a po skončení práce v našem programu zase odinstalovat:
procedure InitDriver; {nainstalujeme si svůj vlastní ovladač} begin asm xor ax,ax int 33h {je vhodné provést alespoň reset myši} end; Pocet := 0; X := 0; {nastavíme souřadnice myši} Y := 0; Tlac := 0; {a také její tlačítka} GetIntVec(COM1INTR,MouseHandler); (* GetIntVec(COM1INTR,@MouseHandler); {pokud bychom MMH definivali jako PROCEDURE.} {pak jej ale můžeme volat i v TP7 bloku} {pointer můžeme volat jen v ASM pomocí CALL} *) SetIntVec(COM1INTR,@MyMouseHandler); end; procedure ZrusDriver; {odinstalujeme svůj ovladač} begin SetIntVec(COM1INTR,MouseHandler); end;
Tak, jsem si jist, že jste to všechno dobře pochopili :-) a tedy si třeba i vybrali variantu, kterou si můžete přepsat do Free Pascalu. Změn nebude potřeba mnoho, jen co se týče adresování, nesmíte zapomenout, že neexistují segmenty, DOSová paměť se adresuje přes selektor FS, pole PORT se musí nahradit OutPortX nebo InPortX a funkce nastavující vektory se změní na set_pm_interrupt, resp. get_pm_interrupt (ať už se jedná o softwarové nebo hardwarové přerušení). Nebo využijte příklady, které jsou na úplném konci této kapitoly dole a budou Vám určitě fungovat :-). Většina asemblerského kódu Vám fungovat bude, kromě adresování přes ES:[], atd., které budete muset upravit. Pokud nejste tak daleko, že víte jak, raději použijte jednodušší řešení. Také musíte vědět, že pokud si instalujete funkci, kterou bude volat rutina z reálného módu, tak jí musíte na rutinu v chráněném módu nainstalovat CallBack. A my se zatím odebere k další kapitole, a tou je klávesnice (proč to dělat jednoduše, když to jde složitě).
První, o co se pokusíme, bude prosté nahrazení jednotky CRT, která nám zprostředková dvě docela dobré funkce. Aniž bychom se dlouze rozepisovali, tak se jen zmíním, že klávesnice má své funkce na INT $16. Pokud se jimi rozhodneme nahradit CRT, bude nám stačit jen tento prostý kód (mohli bychom sice ještě použít INT 21h, ale proč vyhánět čerta ďáblem, že?):
function Readkey : word; assembler; asm xor ah,ah {MOV AL,0} int 16h or al,al {CMP AL,0} je @spec xor ah,ah jmp @konec @spec: mov al,ah mov ah,1 @konec: end;
Tato funkce se chová podobně jako ReadKey s tím rozdílem, že jí není nutné volat 2x v případě, že nám vrátí 0. Tato funkce vrací jednoduše celý WORD. Pokud je stisknuta normální klávesa, vrátí nám její ASCII kód v dolním bytu. Pokud je to rozšířená klávesa, je její ASCII kód také v Lo bytu, ale v Hi je 1. Scan kódy se tedy nevrací (kdybyste je potřebovali, tak jsou v AH, pokud je AL<>0). Nahradit druhou nejpoužívanější funkci je stejně jednoduché:
function Keypressed : boolean; assembler; asm xor bl,bl {mov bl,false} mov ah,1 int 16h jz @konec inc bl {mov bl,true} @konec: mov al,bl end;
Nyní ale zkusíme něco, co nám CRT už nenabízí. Zjistíme stisknuté klávesy CTRL, ALT a SHIFT. Sice bychom na to mohli použít funkci z INT 16h (buď $12 nebo starší $2), ale mnohem rychlejší je načíst si to ručně. Vezme si na pomoc BIOS a ten nám říká, že na adrese 0:$417 je stav speciálních kláves nebo-li přeřaďovačů.
function SpecKlav : byte; assembler; asm xor ax,ax mov es,ax mov bx,417h mov al,es:[bx] and al,15 end;
Tímto získáme stav kláves ALT, CTRL a levého či pravého SHIFTu. Pokud bychom chtěli získat všechny, jednoduše odmažeme poslední instrukci (prakticky, proč bychom ji vlastně měli vůbec používat, že?). Pak budou mít bity tento význam:
0 pravý SHIFT 1 levý SHIFT 2 CTRL (levý nebo pravý) 3 ALT (levý nebo pravý) 4 Scroll Lock zapnut 5 Num Lock zapnut 6 Caps Lock zapnut 7 Insert mód (přepisování / vkládání)
Pokud bychom funkci přepsali tak, aby vracela celý word, můžeme se dozvědět mnohem více informací, které jsou důležité:
function SpecKlav : word; assembler; asm xor ax,ax mov es,ax mov bx,417h mov ax,es:[bx] end;
Význam prvních 8 bitů je stejný, přibyly však bity další (MSB):
8 stisknut levý CTRL (pokud je zároveň bit 2 = 1) 9 stisknut levý ALT (pokud zároveň bit 3 = 1) 10 stisknut Print Screen (vyvolá zároveň INT 5) 11 stav PAUSE (obdoba Insert módu) 12 stisknuté tlačítko Scroll Lock (může být <> bitu 4) 13 stisknuté tlačítko Num Lock (může být <> bitu 5) 14 stisknuté tlačítko Caps Lock (může být <> bitu 6) 15 stisknuté tlačítko Insert (může být <> bitu 7)
Abychom mohli rychle testovat klávesy a nemusel znát jejich kódy, můžeme si je pojmenovat pomocí konstant:
const Alt = 8; Shift = 1; Ctrl = 4; {testujte SPECKLAV pomocí AND} PRShift = 1; PLShift = 2; PCtrl = 4; PAlt = 8; PScrLockM = 16; PNumLockM = 32; PCpsLockM = 64; PInsertM = 128; PLCtrl = 256; PLAlt = 512; PPrintScr = 1024; PPauseM = 2048; PScrLock = 4096; PNumLock = 8192; PCpsLock = 16384; PInsert = 32768; Enter = 13; {testujte READKEY pomocí =} Esc = 27; Nahoru = 256+72; Dolu = 256+80; Doleva = 256+75; Doprava = 256+77; CtrlLevo = 256+115; CtrlPravo = 256+115;
Můžeme si i napsat funkci, která přímo otestuje, zda je daný přepínač či speciální klávesa stisknuta či nikoliv:
function Kstav(Prep : word) : boolean; assembler; asm xor ax,ax mov di,$417 mov es,ax mov bx,es:[di] test bx,prep jz @ko inc al @ko: end;
A když jsme si takto urychlili jednu funkci, můžeme si urychlit i ty ostatní. Na to je ovšem potřeba znát trochu teorie.
Ovladač klávesnice ukládá postupně ASCII kódy kláves do tzv. fronty. Tato fronta je standardně velká 32 bytů. A standardně leží na adrese 0:$41e. Dvojice ASCII kód a Scan kód se ukládají cyklicky od začátku do konce (pokud klávesu držíte, posílá se její kód znovu po určité době, kterou si nastavujete ve Windows nebo v BIOSu; u některých novějších klávesnic přímo na nich). Pokud Vy přečtete z tohoto bufferu klávesu, čtete jí ze začátku fronty, který se tímto posune. Pokud Konec narazí na Začátek, je fronta plná a začne to pípat. Pokud je Konec = Začátek, je fronta prázdná. Protože se čte cyklicky, to, že jste dosáhli bytu 31 neznamená, že jste na konci fronty. Adresa klávesy, která se teď bude číst, je na adrese 0:$41a a má velikost 16 bitů. Jedná se vlastně o offset. Adresa, kam se bude ukládat příští klávesa je WORD na adrese 0:$41c. Ukazují standardně na jeden z těch 32 bytů. Protože adresa segment:offset se může napsat různými způsoby, můžeme vzít $40 jako segment a zbytek (tj. 0-$f) jako offset. Můžeme si to vysvětlit na následující funkci:
const OffsKlav : word = $40; function Keypressed : boolean; assembler; asm xor al,al {klávesa není stisknutá; AL=0} mov es,offsklav {segment bufferu klávesnice} mov bx,es:[$1a] {přečteme začátek fronty} xor bx,es:[$1c] {porovnáme ho s koncem} or bx,bx {je výsledek 0, tj. rovnost?} je @konec {pokud ano, buffer je prázdný} inc al {jinak je tam nějaká klávesa} @konec: end;
Funkce, která nám pak nahradí funkci ReadKey bude sice o trochu složitější, ale bude využívat mnohé z funkce Keypressed:
function ReadKey : word; assembler; asm mov es,offsklav mov di,es:[$1a] @cekej: cmp di,es:[$1c] {porovnáme adresu začátku a konce} je @cekej {je stejná? je = není klávesa, čekáme} mov ax,es:[di] {jinak danou klávesu načteme} add word ptr es:[$1a],2 {musíme posunout začátek o 2 byty} cmp word ptr es:[$1a],$3c {překročili jsme maximální mez?} jbe @zac mov word ptr es:[$1a],$1e {překročili, musíme nastavit na počátek} @zac: {ne, v pořádku} end;
Ovšem, BIOS pamatuje i na jisté výjimky. Pokud se podíváte na adresy 0:$480 a $482, zjistíte, že jsou zde údaje o počátku a konce bufferu pro klávesy. Standardně jsou zde hodnoty $1e a $3c, jakožto offsety od $400, avšak tvůrci zřejmě pamatovali i na aplikace, které potřebují frontu delší. Ty si ji např. mohou posunout až za (v ATHelpu 1.5 uvedený, ale je nutné počítat s tím, že dnes jsou v BIOSu uloženy další informace, např. co se týká otáček ventilátorů, správa napájení, atd.) 0:$4ff a vytvořit ji delší než 32 bytů. Pokud bychom měli jít do důsledků, měli bychom tyto údaje akceptovat, a pozměnit malinko funkci ReadKey (funkci Keypressed nemusíme, neboť ta není závislá na tom, kde je buffer):
var Zacatek : word absolute $0:$480; Konec : word absolute $0:$482; function ReadKey : word; assembler; asm mov es,offsklav mov di,es:[$1a] @cekej: cmp di,es:[$1c] je @cekej mov ax,es:[di] add word ptr es:[$1a],2 mov bx,konec cmp word ptr es:[$1a],bx jbe @zac mov bx,zacatek mov word ptr es:[$1a],bx @zac: end;
Pokud se rozhodnete si buffer rozšířit sami, nezapomeňte nastavit adresy začátku a konce fronty (ne bufferu) tak, aby byly v rozsahu, který si definujete v Zacatek a Konec.
A budeme pokračovat. Pokud se budete pokoušet měnit stav přeřaďovačů (Num Lock, atd.), tak tím samozřejmě změníte chování programů, které jsou na nich závislé, ovšem na klávesnici budou diody svítit stále stejně. Jsou dvě řešení, jak je rozsvítit. Buď k tomu přinutíte DOS takovým trikem (význam se nesnažte pochopit):
procedure DOSled; assembler; asm mov ah,$b int 21h end;
Nebo se o to pokusíte sami. Klávesnice AT je totiž programovatelný mikropočítač 8042 (osekaný/předchůdce 8051) a programuje se přes porty PPI. Zapisovat můžete buď na port $64 nebo $60. Ten první je novější, ten druhý je jistější (kompatibilita). Na tento port však nemůžete nic jen tak zapsat. Musíte si nejprve počkat, až bude klávesnice připravena. Sekvence instrukcí pro vyslání příkazu je následující:
function PPI(Prikaz,Data : byte) : boolean; assembler; asm Xor cx,cx @pockej: in al,64h test al,2 jz @posli loop @pockej jmp @chyba @posli: mov al,prikaz out 64h,al mov cx,2000h @cekej: loop @cekej mov al,data out 64h,al mov al,1 ret @chyba: xor al,al {vypršel limit, klávesnice nedopovídá} end;
Seznam všech příkazů je uveden v ATHelpu, nás bude zajímat příkaz $ed, kterým se rozsvícejí diody. Data pro tento příkaz složíte následovně:
xor ax,ax mov es,ax mov ax,es:[$417] and ax,$7f shr ax,4 mov data,ax
Tím bychom mohli skončit, ale znáte mne. Když už jsme se pokusili udělat si vlastní ovladač myši, proč ne vlastní ovladač klávesnice. Je známo, že klávesnice obsazuje přerušení INT 9h. Pokud se na něj pověsíme, vyřadíme tím defakto původní obsluhu. Proč bychom to chtěli dělat? Např. si chceme zřídit vlastní frontu. A abychom to nekomplikovali, nebudeme do ní ukládat ASCII kódy, ale Scan kódy. Proč? Pro každý z nich totiž bude stačit jen 1 byte. Dejme tomu, že si napíšeme rutinu se jménem KEYBOARD. Nainstalujeme a odinstalujeme ji obvyklým způsobem:
const MaxKodu = 512; var Int9Save : pointer; Buffer : array[0..MaxKodu-1] of byte; BufS,BufK : word; procedure InstallKeyb; begin GetIntVec(9,Int9Save); SetIntVec(9,@Keyboard); BufS := 0; BufK := 0; end; pocedure UnInstallKeyb; begin SetIntVec(9,Int9Save); end;
A nyní začneme psát naši rutinu, a to doslova :-). A musíme vyřešit jeden zajímavý problém. Jistě si pamatujete, že pokud je start a konec fronty na té samé adrese, je fronta prázdná. Jak ale definovat, že je fronta plná? Uděláme to např. tak, že 1 byte ve frontě zůstane nevyužitý a tedy bude platit, že pokud S=0 a K=MAXKODU-1, nebo K=S-1, tak je fronta plná a už nic zapisovat nebudeme (trochu si užijeme assembleru. Nevěřte tomu, že to je málo výkonný nástroj. Má sice málo instrukcí, ale mixér a počítač se také zapínají jedním a tím samým vypínačem, že? A přitom je mezi nimi dost velký rozdíl).
{$F+} procedure Keyboard; interrupt; assembler; asm mov ax,bufk mov bx,bufs or bx,bx jne @startneni0 or ax,ax {fronta je prázdná} je @muzemepsat mov cx,maxkodu dec cx cmp ax,cx je @konec {start = 0 a konec = max-1, fronta je plná} jmp @muzemepsat @startneni0: dec bx xor bx,ax {je konec = start - 1} jz @konec {ano, fronta je plná, nic ukládat nebudeme} @muzemepsat: in al,$60 {načteme Scan kód klávesy} {zjistíme adresu bufferu} mov di,offset buffer {LES nejde, protože to není adresa v zásobníku} {segment se uloží do odpovídajícího registru} {buď DS nebo ES, na 99.9% ten druhý :-)} add di,bufk {přičteme k ní konec} mov [di],al mov ax,bufk {jdeme na další pozici} inc ax cmp ax,maxkodu {jsme na konci bufferu?} jne @ok xor ax,ax @ok: mov bufk,ax {zapíšeme konec zpět} @konec: {teď musíme zajistit správnou funkci HW přerušení} in al,61H {zjistíme původní hodnotu} mov ah,al or al,80h {povolíme klávesnici} out 61H,al xchg ah,al {a zapíšeme opět původní hodnotu} out 61H,al mov al,$20 {potvrdíme zpracování přerušení} out $20,al end; {$F-}
Namísto všeho, co je za slovem KONEC můžeme napsat jen prosté:
jmp far int9save
Pokud chcete volat ještě původní obsluhu, i když nevím, k čemu to bude dobré zrovna v tomto případě (vyřadíte tak spoustu ostatních TRS programů, které čekají na konkrétní klávesu, což se může někdy hodit).
Výsledný kód ve Free Pascalu bude vypadat následovně:
procedure Keyboard; interrupt; far; assembler; asm mov ax,bufk mov bx,bufs or bx,bx jne @startneni0 or ax,ax je @muzemepsat mov cx,maxkodu dec cx cmp ax,cx je @konec jmp @muzemepsat @startneni0: dec bx xor bx,ax jz @konec @muzemepsat: in al,$60 mov edi,buffer add edi,bufk mov [edi],al {pozor! ADD DI projde, ale [DI] už ne!} mov ax,bufk inc ax cmp ax,maxkodu jne @ok xor ax,ax @ok: mov bufk,ax @konec: in al,61H mov ah,al or al,80h out 61H,al xchg ah,al out 61H,al mov al,$20 out $20,al end; procedure int9_dummy; begin end; procedure InstallKeyb; begin {HW(!) přerušení 0-15 musí mít zamčená data a kód!} lock_code(@keyboard,longint(@int9_dummy)-longint(@keyboard)); lock_data(buffer,sizeof(buffer)); lock_data(BufS,sizeof(BufS)+SizeOf(BufK)); get_pm_interrupt(9,Int9Save); {$1C nemusí...} set_pm_interrupt(9,@Keyboard); BufS := 0; BufK := 0; end; pocedure UnInstallKeyb; begin set_pm_interrupt(9,Int9Save); unlock_code(@keyboard,longint(@int9_dummy)-longint(@keyboard)); unlock_data(buffer,sizeof(buffer)); unlock_data(BufS,sizeof(BufS)+SizeOf(BufK)); end;
Rutina čte všechny kódy a ukládá je do bufferu. Je nutno ovšem podotknout, že některé novější klávesy posílají tzv. rozšířené kódy, které začínají kódem $e0 nebo $e1 a po nich následuje několik dalších kódů (po $e0 vždy 1, po $e1 vždy 2; navíc se $e1 opakuje ve dvojicích na jednu klávesu a pokud je za $e0 kód $aa nebo $46, následuje také další $e0 a ještě jeden kód). Standardně je teď načítáme do bufferu. Tam si je musí přebrat už čtecí rutina. Jednodušší by bylo tyto sekvence kódů převádět na 1 bytové kódy, které ještě nejsou obsazené, tj. od $59 nahoru (včetně), tzn. že musíte mít nějaký pracovní buffer o délce pár bytů (ne u procedury, ale v hlavním programu), kam si budete skládat ty delší kódy a až proběhnou všechny, tak z toho složit 1 byte a teprve ten zapsat do hlavního bufferu. Čtecí funkce pak mohou vypadat takto:
function KeyPressed : boolean; begin Keypressed := BufK <> BufS; end; function ReadKey : byte; begin if BufK = BufS then while not Keypressed do; Readkey := Buffer[BufS]; BufS := (BufS+1) and 511; {protože vel.buf.je mocnina dvou, můžeme namísto MOD 512} end; {použít tento trik, abychom udrželi BufS v mezích}
Použitelné scan kódy jsou opět v ATHelpu. Jmenujme alespoň několik těch nejzajímavějších:
1 ESC 2-11 1 až 0 28 Enter 57 Mezerník
Písmena jsou skenována postupně po řádcích, takže nejdou podle abecedy. Můžete si napsat funkci, která bude využívat tabulku, pro převod Scan kódu na ASCII znaky (vytvořil jsem jen prvních několik znaků). Zadaný scan kód nesmí být 0!
function ASCII(Scan : byte) : char; const znaku = 8; pole : array[1..znaku] of char = (#27,'1','2','3','4','5','6','7') begin if Scan <= znaku then ASCII := pole[scan]; end;
A to je všechno. Pokud ještě vůbec něco chápete, tak klobouk dolů. Já po těch 6 hodinách psaní už nechápu nic :-). Kdybyste našli chybu nebo chtěli něco doplnit, klidně mi (nebo spíše adminovi) napište. Pokud by se Vám zdálo, že je to nepřehledné, složité, nebo málo vysvětlené, můžete zavítat na http://www.programujte.com/, kde se čirou náhodou (když to člověk 5 let hledá, nenajde nic, ale jakmile o tom začne psát, tak zjistí, že už to jako napotvoru existuje) píše také o použití myši, grafiky, XMS a PCX (budeme brát příště). Něco z toho se dozvíte jen tady, něco jen tam, spousta je toho společného, i když řešeno různými způsoby (počítal bych na 99%, že alespoň ta ne-moje verze bude určitě fungovat :-D), takže určitě nic nezkazíte, když si pročtete obojí. A na závěr jako bonus mám použití myši a klávesnice ve Free Pascalu velice jednoduchou metodou...
Dva příklady pro využití myši a klávesnice ve všech systémech, které Free Pascal podporuje, jsem vybral z jeho dokumentace. Můžete je použít jak v DOSu, tak pro Win32 či Linux aplikace. Pro čtení joysticku budete muset buď použít DirectInput jednotku nebo najít nějakou jinou, která už přímo čtení Joysticku (a všech ostatních MIDI analogových (ta můžete připojovat a odpojovat i za chodu počítače) zařízení, jako jsou volanty s pedály či letecké páky) podporuje. Jedná se pouze o základní použití. Zvláště u myši najdete jistě i jiná použití, např. s využitím funkcí ShowMouse a HideMouse (viz. nápověda FP).
Myš funguje podobně jako v našich příkladech. Nejprve je inicializujeme, čímž získáme také počet aktivních tlačítek. Rozdíl oproti nám je ten, že musíme zase zrušit ovladač před ukončením programu (i když i v našich případech bylo nutné použít něco jako deaktivaci v případě, že jste měnili citlivost myši či instalovali svůj handler přerušení). Po správné detekci myši už jen čteme souřadnice a vypisujeme je, popř. čekáme na stisk pravého tlačítka myši, které zde testujeme s pomocí předdefinované konstanty. Pokud budeme chtít vidět i daný kurzor, musíme jej zobrazit pomocí ShowMouse. Pokud budete ale něco kreslit na obrazovku (např. texty), musíte před jejich kreslením provést HideMouse, pak to vypsat a poté zase ShowMouse, jinak se může stát, že v případě, kdy bude kurzor myši nad místem, kam se chystáte psát, bude po zápisu a pohnutí myši na jeho místě původní znak a ne ten, který jste zapsali nyní.
Uses mouse; Var Buttons : Byte; begin InitMouse; Buttons := DetectMouse; if Buttons = 0 then Writeln('Myš není připojena.') else Writeln('Nalezena myš se ',Buttons,' tlačítky.'); repeat X := GetMouseX; Y := GetMouseY; Writeln('X,Y= (',X,',',Y,')'); until GetMouseButtons = MouseRightButton; DoneMouse; end.
Při použití jednotky KEYBOARD je nutné používat tzv. události. Navíc je potřeba klávesnici stejně jako myš nejprve inicializovat a před skončením programu zase deaktivovat. Vždy musíte získat událost a teprve tu převést na klávesu. Je to sice trochu neohrabané a možná i pomalé, ale může to posloužit jako dobrý příklad. Až získáte znalosti, můžete si stáhnout zdrojový kód jednotky Keyboard a vložit si do svého kódu jen ty funkce, které potřebujete a případně je patřičně upravit.
uses keyboard; Var K : TKeyEvent; begin InitKeyBoard; Writeln('Tiskněte klávesy, stiskněte "q" pro ukončení.'); repeat K := GetKeyEvent; K := TranslateKeyEvent(K); Write('Ziskana klavesa s touto udalosti: '); case GetKeyEventFlags(K) of kbASCII : Writeln('ASCII klávesa'); kbUniCode : Writeln('Unicode klávesa'); kbFnKey : Writeln('Funkční klávesa'); kbPhys : Writeln('Somatická klávesa'); kbReleased : Writeln('Uvolnění klávesy'); end; K := TranslateKeyEvent(K); Writeln('Získaná klávesa : ',KeyEventToString(K)); until GetKeyEventChar(K) = 'q'; DoneKeyBoard; end.
A to je pro dnešek opravdu vše. Příště se podíváme na seriál o grafice, kde se naučíte načítat obrázky BMP, inicializovat různé režimy, používat XMS paměť. A později k tomu přidáme i zvuky WAVe na zvukových kartách.