home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Zodiac Super OZ
/
MEDIADEPOT.ISO
/
FILES
/
13
/
COMMIO0B.ZIP
/
SYSKEYS.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1996-05-14
|
13KB
|
321 lines
unit SysKeys;
{
This unit is a companion to the COMMIO communications unit.
Written by Jason Morriss a.k.a. Lief O'Pardy
Copyright (C) 1995,1996 by Jason Morriss
This is the Sysop Function key unit. This unit allows you to create
procedures that will get called automatically when the sysop presses a
Function Key (F1-F12) on the local keyboard (you can't pass function
keys through the comport through normal means). And as long as you follow
a few simple rules, your procedure will "Multi-Task" WITH the current user
playing the door, meaning that the user won't even know your using a
function key procedure!
-But like i said, thats only if you follow a few rules, and in some cases
you won't want it to "Multi-Task".
A maximum of 32 procedures can be assigned at one time. (you can change
that by changing the max_tasks const in the MTASK unit; you will have
to recompile, ofcourse)... But the more TASKS you create the more HEAP
is taken. BTW: when setting up a procedure, the StackSize variable needed
is the amount of HEAP the procedure will take for its stack+procedure size,
BUT that space is NOT taken from the heap until the procedure gets called.
So don't worry about creating 12 procedures, each requiring 5k. Most of
the time you will only call 1 of the SysKeys at a time, so only 5k will be
used at one time.
1) In order for your procedure to Multi-Task with the user you must
use a procedure called: "switch_task()". If your procedure is
small and it doesn't have to get any input from the sysop then
you probably don't even need to call it, even if the user does get
a small delay, he/she will just think it was line noise or something.
But for larger procedures that take a little time to complete you
will want to call it a couple times. Or if the procedure needs to
get input from the sysop then that is the best time to call
Switch_Task. Make a simple repeat..until loop like:
repeat Switch_task until keypressed; (*LOCAL keypress routine*)
(it might be a good idea to add a counter to that loop so that it
only switches like every 100 or so interations)
If the procedure is supposed to interact with the sysop AND the user
then you should not call Switch_Task, there's no need, and the input
would interfere with the user's game. (as long as you use the "sio"
input/output routines everything will be fine, since they will switch
tasks when needed)
One major limitation you should remember is that the Multi-Task
abilities of this unit are limited to INSIDE your program. If
you call any outside programs (ie: shell to dos) then everything
in the door stops until you return from the external program.
2) The procedure you create must be of the same type as "KeyProc" below.
The procedure has 1 Untyped variable as a parameter. When the
procedure is called, the key that activated it will be sent in the
parameter. To use it you have to Typecast it as a CHAR (or some
other type if you need to) like this:
procedure mykeyF5(var param); far;
var key:char absolute param;
begin
end;
The above is the easist way to do it, but not the only way.
The reason the param is UNTYPED is because thats how MTASK is setup,
and i can't change it...
3) The procedure you create must be FAR. Look in the above example
and notice the word "far" in the procedure header, thats one way of
doing it.
4) The procedure you create should only use normal write routines so that
the text written is only displayed locally... (unless the procedure
you have setup is supposed to be displayed remotely). The same goes
for the Readkey type functions. The DOORIO unit has "WriteStr()" proc.
that is perfect for this. It uses direct screen writes, so the cursor
will not move, and the user's color will not get messed up.
5) The procedure you create must not be in a unit that is being OVERLAYED!
That would be bad... And most of the procedures that get created for
function keys are so small that they really don't need to be overlayed.
6) Some global variables should be saved then altered while in the
procedure... and then restored right before the procedure exits.
Door.UpdateLocal:=False;
This one is optional. It really depends on what the procedure does.
Door.LocalInputON:=False;
If the procedure requires input from the sysop, then this should
be set to false, so that the COMMIO routines won't intercept
the sysop's keypresses while IN the sysop function (unless the
procedure is a CHAT type procedure).
Syskey1[key].open:=False;
Right before your procedure exits you _MUST_ set the variable
above to false. This will allow you to call this procedure
again. If you don't set it to false, then you won't be able to
call that procedure again, until the door is rerun. You MUST
besure to use the correct "Syskey??" variable though! There
are four different arrays for holding the key procedures.
Just look at the ranges in the arrays below.
7) I've figured out a crude way to figure out how big a procedure is.
Create the procedure just like i said above. Then DIRECTLY AFTER the
procedure (right after the "end;"), put a VARiable of type word. like
this:
procedure MyTask(var p); far;
begin
<your code here>
end;
var MyTask_size:word;
Do not make it a const (ie: const MyTask:word=0;), that will NOT work.
Because TP will move the variable to a different location (it groups
all initialized consts to the data segment; but the VAR will be right
where you declare it, in the code segment).
Then later sometime before you call "create_task()" do the following
calculation on the MyTask_size var:
MyTask_size:=ofs(MyTask_size) - ofs(@MyTask^) + 512;
I add the 512 for safety. This ONLY gives you the size of the procedure
ITSELF... the STACKSIZE parameter is the size of the procedure+all stack
needed by the procedure. So for every procedure/function inside your
syskey procedure, you should add more to the stacksize parameter. Its
just trial and error... i dont know any other way to figure it out,
exactly.
}
interface
uses _input,mtask;
const
F1 = #59; F2 = #60; F3 = #61; F4 = #62; F5 = #63; F6 = #64;
F7 = #65; F8 = #66; F9 = #67; F10= #68; F11= #133; F12= #134;
AltF1 = #104; AltF2 = #105; AltF3 = #106; AltF4 = #107;
AltF5 = #108; AltF6 = #109; AltF7 = #110; AltF8 = #111;
AltF9 = #112; AltF10 = #113; AltF11 = #139; AltF12 = #140;
{^ These are all the keys you can create procedures for. }
type
KeyProc = procedure(var param);
TKeyRec = record
proc : KeyProc;
size : integer;
open : boolean;
key : char;
end;
const
NumProcs : byte = 0; {how many procedures have been created}
var
SysKey1 : array[F1..F10] of TKeyrec;
SysKey2 : array[F11..F12] of TKeyrec;
SysKey1a : array[AltF1..AltF10] of TKeyrec;
SysKey2a : array[AltF11..AltF12] of TKeyrec;
Function SetSysopKey(key:char; proc:KeyProc; stacksize:integer):boolean;
{^ Sets a function key for the sysop to use locally. Returns true or false
if it was successful.
key = Which Key to reprogram, valid values are F1..F12, AltF1..AltF12.
stacksize = How much memory the procedure needs (this is not just STACK
for the proc. This is memory for the entire procedure).
The minimum is 512k, but most procedures will need more.
proc = The procedure that gets called. If "NIL" is passed as the proc
then that key procedure is removed. }
Procedure RemoveKey(key:char);
{^ Remove a key procedure. This is just like if you passed "nil" to the
above proc... this is just easier to call. }
Procedure Callkey(key:char);
{^ This calls the procedure of KEY. ie: CallKey(F10); calls the F10 key
procedure. If the procedure for a certain key is nil, nothing happens.
Once this is called the procedure is then multi-tasked with the rest. }
implementation
{─────────────────────────────────────────────────────────────────────────}
Procedure ExampleKeyProc(var param); far;
var key:char absolute param;
begin
{save the variables that need it; then change them for your procedure}
{ Door.UpdateLocal:=False;
Door.LocalInputON:=False;}
{your code here; dont forget to call switch_task if needed}
{use only one of the below, depending on the key}
syskey1[key].open:=false;
{ syskey2[key].open:=false;
syskey1a[key].open:=false;
syskey2a[key].open:=false;}
end;
{ Setup like this (the stacksize will vary ofcourse):
SetSysopKey(F1,ExampleKeyProc,2048);}
{─────────────────────────────────────────────────────────────────────────}
Function SetSysopKey;
begin
SetSysopKey:=false;
if key in [F1..F10] then begin
if (@Syskey1[key].proc=nil)and(@proc<>nil)and(NumProcs<Max_Tasks) then begin
inc(NumProcs);
Syskey1[key].proc:=proc;
Syskey1[key].size:=stacksize;
Syskey1[key].open:=false;
Syskey1[key].key :=key;
SetSysopKey:=true;
end else if (@Syskey1[key].proc<>nil)and(@proc=nil)and(NumProcs>0) then begin
dec(NumProcs);
Syskey1[key].proc:=nil;
Syskey1[key].size:=0;
Syskey1[key].open:=false;
Syskey1[key].key :=key;
end;
end else if key in [F11..F12] then begin
if (@Syskey2[key].proc=nil)and(@proc<>nil)and(NumProcs<Max_Tasks) then begin
inc(NumProcs);
Syskey2[key].proc:=proc;
Syskey2[key].size:=stacksize;
Syskey2[key].open:=false;
Syskey2[key].key :=key;
SetSysopKey:=true;
end else if (@Syskey2[key].proc<>nil)and(@proc=nil)and(NumProcs>0) then begin
dec(NumProcs);
Syskey2[key].proc:=nil;
Syskey2[key].size:=0;
Syskey2[key].open:=false;
Syskey2[key].key :=key;
end;
end else if key in [AltF1..AltF10] then begin
if (@Syskey1a[key].proc=nil)and(@proc<>nil)and(NumProcs<Max_Tasks) then begin
inc(NumProcs);
Syskey1a[key].proc:=proc;
Syskey1a[key].size:=stacksize;
Syskey1a[key].open:=false;
Syskey1a[key].key :=key;
SetSysopKey:=true;
end else if (@Syskey1a[key].proc<>nil)and(@proc=nil)and(NumProcs>0) then begin
dec(NumProcs);
Syskey1a[key].proc:=nil;
Syskey1a[key].size:=0;
Syskey1a[key].open:=false;
Syskey1a[key].key :=key;
end;
end else if key in [AltF11..AltF12] then begin
if (@Syskey2a[key].proc=nil)and(@proc<>nil)and(NumProcs<Max_Tasks) then begin
inc(NumProcs);
Syskey2a[key].proc:=proc;
Syskey2a[key].size:=stacksize;
Syskey2a[key].open:=false;
Syskey2a[key].key :=key;
SetSysopKey:=true;
end else if (@Syskey2a[key].proc<>nil)and(@proc=nil)and(NumProcs>0) then begin
dec(NumProcs);
Syskey2a[key].proc:=nil;
Syskey2a[key].size:=0;
Syskey2a[key].open:=false;
Syskey2a[key].key :=key;
end;
end;
end;
{───────────────────────────────────────────────────────────────────────────}
Procedure RemoveKey;
begin
SetSysopKey(key,nil,0);
end;
{───────────────────────────────────────────────────────────────────────────}
Procedure CallKey;
var id,r:word;
begin
if key in [F1..F10] then begin
if @Syskey1[key].proc<>nil then begin
r:=0;
if not Syskey1[key].open then begin
Syskey1[key].open:=true;
create_task(Syskey1[key].proc,Syskey1[key].key,Syskey1[key].size,id,r);
if r<>0 then begin
write(^G);
{show error message to sysop "No Memory"}
end{ else switch_task};
end;
end;
end else if key in [F11..F12] then begin
if @Syskey2[key].proc<>nil then begin
r:=0;
if not Syskey2[key].open then begin
Syskey2[key].open:=true;
create_task(Syskey2[key].proc,Syskey2[key].key,Syskey2[key].size,id,r);
if r<>0 then begin
write(^G);
{show error message to sysop "No Memory"}
end;
end;
end;
end else if key in [AltF1..AltF10] then begin
if @Syskey1a[key].proc<>nil then begin
r:=0;
if not Syskey1a[key].open then begin
Syskey1a[key].open:=true;
create_task(Syskey1a[key].proc,Syskey1a[key].key,Syskey1a[key].size,id,r);
if r<>0 then begin
write(^G);
{show error message to sysop "No Memory"}
end;
end;
end;
end else if key in [AltF11..AltF12] then begin
if @Syskey2a[key].proc<>nil then begin
r:=0;
if not Syskey2a[key].open then begin
Syskey2a[key].open:=true;
create_task(Syskey2a[key].proc,Syskey2a[key].key,Syskey2a[key].size,id,r);
if r<>0 then begin
write(^G);
{show error message to sysop "No Memory"}
end;
end;
end;
end;
end;
{───────────────────────────────────────────────────────────────────────────}
end.