A REXXUtil és más kiegészítô könyvtárak
A könyvtár (library) fogalma alatt ebben az esetben olyan függvénytárat értünk, amelyet több program is használhat egyszerre. A REXX programok számára használható könyvtárak minden esetben úgynevezett dinamikusan csatolható állományok (Dynamic Link Library, DLL). A REXXUtil könyvtár egy olyan DLL, amelyet mellékelnek az OS/2-höz és a benne található segédfüggvényekkel kiterjeszthetjük az alap REXX képességeit. Más kiegészítô könyvtárak is léteznek, ezeket azonban saját magunknak kell beszereznünk. A kiegészítô könyvtárakat rendszerint C vagy C++ nyelven írják.
A segédfüggvények használata
Mielôtt a kiegészítô függvényeket programunkban használni tudnánk, regisztrálni kell ôket. A regisztrálás azt jelenti, hogy a függvényt úgymond hozzáadjuk a REXX függvénykészletéhez, és az egészen addig használható marad, amíg el nem távolítjuk, vagy újra nem indítjuk gépünket. A regisztrálás az RxFuncAdd függvénnyel történhet:
rc = RxFuncAdd(függvénynév, modul, belépési_pont)
A függvénynév paraméter a regisztrálandó függvény nevét adja meg. A modul paraméter értéke meg kell hogy egyezzen a regisztrálandó függvényt tartalmazó DLL nevével. A belépési pont az esetek 99%-ában megegyezik a függvény nevével. Ha nem ez lenne a helyzet, akkor azt a könyvtárral adott dokumentációban feltüntetik. Amennyiben sikeres a függvény regisztrációja, a visszatérési érték 0, ellenkezô esetben pedig 1 lesz. A legtöbb könyvtár tartalmaz egy olyan függvényt is, amellyel az összes bôvítôfüggvény egy lépésben is regisztrálható. Amennyiben nem ez lenne az eset, akkor sajnos minden egyes segédfüggvényt külön-külön kell regisztrálnunk az RxFuncAdd függvénnyel.
A REXXUtil esetében a SysLoadFuncs függvénnyel lehet a többi segédfüggvényt egy lépésben regisztrálni. Persze elôbb magát a SysLoadFuncs-ot kell regisztrálnunk, hogy késôbb aztán meghívhassuk:
CALL RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs' CALL SysLoadFuncs
Az alap REXX-ben megtalálható függvénnyel, az RxFuncQuery segítségével lehet lekérdezni, hogy egy adott segédfüggvényt regisztráltunk-e már. Ha igen, akkor a visszatérési érték 0, ha nem, akkor pedig 1 lesz. Az alábbi programrészlet jó példa ezen függvények használatára. Az eljárás segítségével regisztráltatjuk a REXXUtil függvényeket, ha éppen szükségünk van rájuk:
LoadREXXUtil: PROCEDURE
IF RxFuncQuery('SysLoadFuncs') THEN
DO
IF RxFuncADD('SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs') THEN
DO
SAY 'Nem lehet betölteni a REXXUtil könyvtárat!'
RETURN 1
END
CALL SysLoadFuncs
END
RETURN 0
Egy egyszerû menü létrehozása
Nagyon sok program mûködéséhez van szükség menüre. A legegyszerûbb menüforma az, amikor egy szám begépelésével választjuk ki a megfelelô mûveletet. Bár ez a fajta megvalósítás nagyon könnyen programozható, használata mégsem ajánlott, mivel a felhasználók nem szeretik. Ráadásul könnyen rossz menüpontot választhatunk, ha például elgépeljük a számot. A következôkben ezért egy olyan menüt fogunk megvalósítani, amelyben fordított színekkel kiemeljük az éppen kiválasztott menüpontot, és a felhasználó a nyilak segítségével változtathatja a kiemelést. Ha megvan a választás, akkor az ENTER lenyomására végrehajtódik a kiválasztott menüponthoz rendelt mûvelet. A megvalósításhoz szükségünk van a REXXUtil függvényekre. A következôkben áttekintjük a program megírásához szükséges függvényeket és aztán elkészítjük a menüt.
A lenyomott billentyûk feldolgozása
Az alap REXX-ben sajnos nincsen olyan függvény, amellyel fel lehetne dolgoztatni a billentyûmegnyomásokat. A karakterek beolvasására szolgáló függvények ugyanis azt kívánják, hogy a felhasználó az adatbevitel végén megnyomja az ENTER-t. A REXXUtil függvények között található SysGetKey függvény azonban megoldja ezt a problémát:
key = SysGetKey([opció])
A függvény visszatérési értéke (példánkban key) tartalmazza a megnyomott billentyû kódját. Az opció paraméter értéke ECHO vagy NOECHO lehet. Az alapértelmezett értéke az ECHO, amely azt eredményezi, hogy a lenyomott billentyû a képernyôn is megjelenik. A NOECHO megadásával ez elkerülhetô, ami például jelszó bevitelekor lehet hasznos. A SysGetKey segítségével szinte bármelyik billentyû, vagy billentyûkombináció megnyomását érzékelni lehet. Egyes billentyûk megnyomása speciális, úgynevezett kiterjesztett kódokat produkál. Ezek az egyébként két bájtot elfoglaló kódok minden esetben a hexadecimális 00-ával vagy E0-ával kezdôdnek, és a SysGetKey-t kétszer kell meghívni, ha meg akarjuk kapni a kód második bájtját is. Bizonyos billentyûkombinációkat lefoglalhat az operációs rendszer. Jó példa erre a CTRL-ALT-DEL vagy a CTRL-ESC, amelyek kódjait nem lehet lekérdezni. A következô példaprogrammal kitapasztalhatjuk, hogy az egyes billentyûk megnyomásakor a SysGetKey milyen értékeket ad vissza.
/* A billentyûkódok kitapasztalása */
IF LoadREXXUtil() THEN
EXIT
nyomtathato = XRANGE(' ', '~')
SAY 'Nyomjon le egy billentyût! (Q=kilépés)'
DO UNTIL key = 'Q'
key = SysGetKey('NOECHO')
SELECT
WHEN key = '00'x | key = 'E0'x THEN
DO
c = '-kiterjesztett-'
key = key || SysGetKey('NOECHO')
END
WHEN POS(key, nyomtathato) <> 0 THEN
c = '>'key'<'
OTHERWISE
c = '-nem nyomtatható-'
END
SAY Right(C2X(key), 4) c
END
EXIT
LoadREXXUtil: PROCEDURE
IF RxFuncQuery('SysLoadFuncs') THEN
DO
IF RxFuncADD('SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs') THEN
DO
SAY 'Nem lehet betölteni a REXXUtil könyvtárat!'
RETURN 1
END
CALL SysLoadFuncs
END
RETURN 0
A példaprogram elôször is leellenôrzi, hogy be vannak-e már töltve a REXXUtil segédfüggvények. Ha nem, akkor regisztrálja ôket. Ezután definiáljuk a nyomtatható karakterek tartományát az XRANGE utasítással. A program gerincét képezô ciklus egészen addig figyeli a billentyûket, amíg le nem nyomjuk a Q billentyût. A SysGetKey által visszaadott értéket teszteljük és ha kiterjesztett kódról van szó, akkor újra meghívjuk a függvényt, hogy a második bájtot is beolvassuk. Ha nem kiterjesztett kódról van szó, akkor megvizsgáljuk, hogy megjeleníthetô-e a karakter. A SAY utasítás aztán kinyomtatja a lenyomott billentyû kódját és amennyiben maga a karakter is nyomtatható, akkor azt is megjeleníti.
A kurzor pozícionálása és elrejtése
A SysCurPos függvény segítségével pozícionálhatjuk a kurzort és ezáltal a kiíratott szöveget a képernyôn. A függvény visszatérési értéke megadja a kurzor pillanatnyi pozícióját is:
pos = SysCurPos([sor, oszlop])
A pos változó tartalmazza a kurzor pozíciójának mindkét koordinátáját egy szóközzel elválasztva (sor oszlop, pl. 5 27). A legegyszerûbben a PARSE utasítással lehet a koordinátákat kiolvasni:
PARSE VALUE SysCurPos() WITH sor oszlop SAY 'A kurzor pozíciójának koordinátái: sor:' sor', oszlop:' oszlop
A képernyô bal felsô sarkának koordinátái a 0,0. Az utolsó sor koordinátája egy 25 soros képernyôn tehát 24, az utolsó oszlop pedig 80 oszlopos esetben 79.
A képernyôn villogó kurzor nem mindig szép látvány. Az eltüntetés a SysCurState függvénnyel lehetséges:
CALL SysCurState állapot
Az állapot paraméter ON vagy OFF lehet, attól függôen, hogy be vagy pedig ki szeretnénk kapcsolni a kurzort.
A menü összeállítása
A menü megjelenítése elôtt törölnünk kell a képernyôt. Ezt többféleképpen is megtehetjük. Használhatjuk az OS/2 CLS parancsát, a megfelelô ANSI vezérlôkarakter-láncot, vagy pedig a SysCls segédfüggvényt. A SysCls hátránya az, hogy a törlés után fekete hátteret állít be. Amennyiben színes hátterünk van, akkor inkább használjuk a megfelelô ANSI szekvenciát.
Amikor a felhasználó kiválaszt egy menüpontot, akkor a menüprogramnak erre valamilyen módon reagálnia kell. Ez például történhet úgy, hogy visszaad egy kódot, amely a választott funkciót reprezentálja. A kódot megvizsgálva aztán eldönthetô, hogy mit is kell a továbbiakban csinálni. A legjobban akkor járunk, ha a visszaadott kódot egy olyan összetett változóval valósítjuk meg, amelynek neve egyben leírja a végrehajtandó funkciót is. Ekkor ugyanis könnyedén lefuttathatjuk az INTERPRET instrukció felhasználásával a változó nevébe foglalt parancsot:
INTERPRET kifejezés
Az INTERPRET után megadott kifejezést a REXX kiértékeli és az eredményül kapott programkódot végrehajtja. Az INTERPRET alkalmazásával tehát lehetôség nyílik arra, hogy programkódot tároljunk a változók nevében és azt csak késôbb hajtassuk végre. Az OS/2 REXX támogatásának telepítése után rendelkezésre álló REXXTRY.CMD program például arra használja az INTERPRET instrukciót, hogy interaktív üzemmódban beolvassa a felhasználó REXX parancsait majd pedig végrehajtsa azokat. Az alábbi kódrészletben láthatunk erre egy példát:
DO FOREVER SAY SAY 'Gépeljen be egy REXX utasítást! (EXIT = kilépés)' PARSE PULL utasitas INTERPRET utasitas END
Ezek után pedig lássuk végre az egyszerû menüprogramot!
/* menü */
IF LoadREXXUtil() THEN
EXIT
CALL CharOut 'CON:', '1B'x || '[37;40m'
CALL SysCls
CALL CharOut 'CON:', Center('Kezdetleges menü', 80)
menu.0 = 3
menu.1.sor = 5; menu.1.oszlop = 34
menu.1.szoveg = 'Elsô sor'
menu.1.feladat = 'CALL sor1'
menu.2.sor = 6; menu.2.oszlop = 34
menu.2.szoveg = 'Második sor'
menu.2.feladat = 'CALL sor2'
menu.3.sor = 7; menu.3.oszlop = 34
menu.3.szoveg = 'Kilépés'
menu.3.feladat = 'EXIT'
CALL DoMenu
CALL SysCurState 'ON'
'CLS'
EXIT
/* Menüsor 1 */
sor1: PROCEDURE
CALL SysCurPos 24, 1
szoveg = Left('A menü elsô sorát választotta ki!', 60)
CALL CharOut 'CON:', szoveg
RETURN
/* Menüsor 2 */
sor2: PROCEDURE
CALL SysCurPos 24, 1
szoveg = Left('A menü második sorát választotta ki!', 60)
CALL CharOut 'CON:', szoveg
RETURN
/* A menü megjelenítése és lekezelése */
DoMenu: PROCEDURE EXPOSE menu.
colorNormal = '1B'x || '[37;40m'
colorReverse = '1B'x || '[30;47m'
keyEnter = '0D'x
keyFel = '48'x
keyLe = '50'x
keyPrefix1 = '00'x
keyPrefix2 = 'E0'x
CALL SysCurState 'OFF'
CALL CharOut 'CON:', colorNormal
DO i = 1 TO menu.0
CALL SysCurPos menu.i.sor, menu.i.oszlop
CALL CharOut 'CON:', menu.i.szoveg
END
i = 1 /* Az elsô menüpont aktív */
DO FOREVER
CALL SysCurPos menu.i.sor, menu.i.oszlop
CALL CharOut 'CON:', colorReverse || menu.i.szoveg
CALL CharOut 'CON:', colorNormal
key = SysGetKey('NOECHO')
kovetkezo = i
SELECT
WHEN key == keyENTER THEN
INTERPRET menu.i.feladat
WHEN key == keyPrefix1 | key == keyPrefix2 THEN
DO
key = SysGetKey('NOECHO')
SELECT
WHEN key == keyFel THEN
IF i > 1 THEN
kovetkezo = i - 1
WHEN key == keyLe THEN
IF i < menu.0 THEN
kovetkezo = i + 1
OTHERWISE
NOP;
END
END
OTHERWISE
NOP;
END
IF kovetkezo <> i THEN
DO
CALL SysCurPos menu.i.sor, menu.i.oszlop
CALL CharOut 'CON:', menu.i.szoveg
i = kovetkezo
END
END
RETURN
LoadREXXUtil: PROCEDURE
IF RxFuncQuery('SysLoadFuncs') THEN
DO
IF RxFuncADD('SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs') THEN
DO
SAY 'Nem lehet betölteni a REXXUtil könyvtárat!'
RETURN 1
END
CALL SysLoadFuncs
END
RETURN 0
A program képernyôtörléssel kezdi a mûködést, amelyet a Kezdetleges menü felirat középre igazított kiíratása követ. A menu. összetett változóban tároljuk el a menü három elemét. Minden egyes elem esetében elrakjuk a megjelenített szöveget, annak koordinátáit és az elvégzendô feladatot. Ezután a DoMenu eljárást hívjuk meg, amely megjeleníti a menüt és lekezeli a felhasználó akcióit. A menü két elsô pontja által elvégzett feladatot a Sor1 és a Sor2 eljárások írják le. Esetünkben nem csinálnak semmi különöset, csupán megjelenítenek egy üzenetet a képernyô alján, amelybôl tudjuk, hogy melyik menüpontot hajtottuk éppen végre. A DoMenu eljárás elején inicializálunk néhány változót, majd pedig elrejtjük a kurzort és beállítjuk a szöveg és a háttér színét. A menüpontok megjelenítése a kurzorpozíció változtatásával és a megfelelô szöveg kiíratásával történik. A DO FOREVER ciklus a program lelke. Az éppen kiválasztott menüsort az i változóval követjük nyomon és inverz színekkel emeljük ki. A felhasználó akcióit a SysGetKey utasítással figyeljük. A függvény által visszaadott értékeket pontos összehasonlítással ellenôrizzük. Az i változó pillanatnyi értékét a kovetkezo változóban mentjük el, majd pedig a lenyomott billentyût a SELECT utasítással dolgoztatjuk fel. Amennyiben a felhasználó ENTER-t nyomott meg, akkor az INTERPRET segítségével elvégezzük a megfelelô feladatot. Ha a felhasználó kiterjesztett kódú billentyût nyomott meg, akkor még egyszer meg kell hívni a SysGetKey-t, hogy a második bájtot is begyûjthessük. Amennyiben a felfelé mutató nyilat nyomta meg, és az i értéke nagyobb 1-nél, akkor az i értékét megnöveljük eggyel. Ha a lefelé mutató nyilat nyomták meg, és az i értéke kevesebb, mint a menüsorok száma, akkor pedig csökkentjük értékét. Minden más billentyût figyelmen kívül hagyunk. Amennyiben a feldolgozás végén a kovetkezo értéke eltér az i értékétôl, az aktív menüsort megváltoztatjuk és az i felveszi a kovetkezo értékét.
REXX GYÍK:
K1. Milyen más kiegészítô könyvtárak vannak még REXX-hez?
V1. Nagyon sok shareware könyvtárat lehet találni az Interneten. Ezekkel SQL adatbázisokat vagy dBase fájlokat kezelhetünk, hálózati kommunikációt valósíthatunk meg, valamint jobban kihasználhatjuk az OS/2 által nyújtott szolgáltatásokat.
K2. Lassítja-e a REXX programok futását, ha sok függvényt regisztrálok programomban?
V2. Többnyire nem érzékelhetô lassulás. A REXXUtil függvények használata pedig a legtöbb esetben kismértékû gyorsulást jelent, amennyiben azokat az OS/2-es rendszerparancsok kiváltására használjuk.
K3. Egyes REXXUtil függvények ugyanazt csinálják, mint bizonyos ANSI parancsok. Melyiket érdemes használni?
V3. Ezt nem könnyû eldönteni. A REXXUtil segédfüggvények használata egyszerûbb, ugyanakkor ezekkel nem lehet a színeket optimálisan kezelni. Az ANSI parancsokkal a színek kezelése jól megoldható, az ANSI támogatásnak viszont engedélyezve kell lennie. A legjobb az, ha a REXXUtil és az ANSI parancsok feladatra optimalizált keverékét használjuk.
Gyakorlatok:
1. Alakítsa át a menüprogramot oly módon, hogy az elsô menüpont a config.sys-t, a második pedig az autoexec.bat fájlt töltse be egy szövegszerkesztôbe!
| Kádár Zsolt 1998. 08. 28. | [ Következô lecke | Tartalom ] |