I. A REXXUtil segédfüggvények

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 ]