home *** CD-ROM | disk | FTP | other *** search
- Program InputHandlerDemo;
-
- {
- Dieses Programm ist etwas für Spezialisten und demonstriert, daß man
- sogar so systemnahe Dinge wie Input-Event-Handler in Pascal schreiben
- kann.
-
- Der vom Programm installierte Input-Handler "vertauscht" einige
- Tasten mit ihren jeweiligen Nachbarn, läßt bei anderen Tasten den
- Bildschirm häßlich aufblitzen und sperrt wieder andere Tasten ganz.
- Als einzige nützliche Funktion ermöglicht der Handler es, mit der
- Help-Taste das gerade aktive Fenster nach vorn zu holen.
-
- Währenddessen versetzt sich das Programm selbst in den Waiting State
- (verbraucht also keine Rechenzeit), bis es vom Handler ein Signal
- erhält (das jener beim Empfang der Tastenkombination "Shift links -
- Shift rechts - Escape" sendet). Dann entfernt es den Handler wieder.
-
- Alternativ wäre vorstellbar, daß der Handler dem Hauptprogramm
- nicht nur ein Signal, sondern bei bestimmten Events eine Message
- an einen Messageport schickt. Dann kann man ein Utility schreiben,
- daß bei gewissen Tastenkombinationen bestimmten Operationen
- ausführt, und zwar unabhängig davon, welches Window beim Drücken
- dieser Tasten gerade aktiv ist...
-
- Ein Handler sollte übrigens möglichst keine Pascal-Prozeduren oder
- -Funktionen benutzen, denn das Laufzeitsystem von Kickpascal ist
- nicht als "Shared Library" ausgelegt, d. h. es kann wegen gewissen
- internen statischen Datenstrukturen nicht von zwei parallel laufenden
- Prozessen - z. B. einem Task und einem Input-Handler - gleichzeitig
- benutzt werden. Ein Beispiel: Wenn man mit "Write" eine Zahl ausgibt,
- verwendet die entsprechende Funktion der Laufzeitbibliothek einen
- bestimmten Pufferspeicher, um die Zahl in eine Zeichenfolge zu
- wandeln. Wenn nun das Hauptprogramm vom Handler unterbrochen wird,
- während es gerade eine Zahl ausgibt, und der Handler ebenfalls eine
- Zahl schreiben will, gibt es ein Chaos. Solche Konflikte dürften
- zwar in der Regel nicht zu einem Guru führen, aber die Programme
- werden dadurch instabil.
- }
-
- { MaxonPascal3-Anpassung / Test: Falk Zühlsdorff (PackMAN) 1994 }
-
- Uses ExecIO,Intuition;
-
- {$opt b-;
- incl "exec/interrupts.h",
- "devices/input.h",
- "devices/inputevent.h",
- "intuition/intuitionbase.h" }
-
- Type CodeTyp = Array[1..15] of Word;
-
- Var port : p_MsgPort; { Port zur Kommunikation mit dem Device }
- ioreq : ^IOStdReq; { Hier reicht Standard-Struktur }
- inter : Interrupt; { Interrupt-Struktur zum Handler }
- err : BYTE; { Fehlernummern der Device-Operationen }
- ThisTask : p_Task; { Zeiger auf diesen Task }
- Sig, SigBit : Long; { Signalnummern }
- _paslibbase : Byte; IMPORT; { Pascal-interne Adresse }
- InterfaceCode : Codetyp; { Speicher für kurze Assembler-Routine }
-
-
- {$opt b-,s- : Handler darf Stack nicht prüfen, da er sonst fälschlich
- einen Stacküberlauf feststellen und aussteigen würde - GURU! }
-
- Function Handler (data: Ptr; event: p_InputEvent) : p_InputEvent;
-
- { Diese Funktion wird (mit Umweg über den Assembler-Teil) bei jedem
- Input-Event aufgerufen. Als Parameter erhält sie einen Zeiger auf
- eine Liste von Input-Event-Strukturen und eine Kopie des Zeigers
- "is_Data" der Interrupt-Struktur (wird hier nicht benötigt). Die
- Funktion muß als Ergebnis einen Zeiger auf eine (evtl. veränderte)
- Liste zurückgeben.
- Beachten Sie, daß Kickpascal Parameter auf dem Stack in genau
- umgekehrter Reihenfolge als C erwartet. Deshalb ist "data" hier
- der erste Parameter und kann ersatzlos gestrichen werden. }
-
- Const Qual = IEQUALIFIER_LSHIFT or IEQUALIFIER_RSHIFT;
- { Qualifier für Abbruch-Taste }
-
- Var e2 : p_InputEvent;
- err : Boolean;
- ibase : p_IntuitionBase;
-
- Begin
- e2 := event;
- ibase:=IntuitionBase;
- While e2 <> Nil Do
- { Liste durchlaufen }
- Begin
-
- If (e2^.ie_Class and IECLASS_RAWKEY) <> 0 Then
- Case e2^.ie_Code and $7f of
- $00..$3F : e2^.ie_Code := e2^.ie_Code xor 1;
- { Tasten vertauschen }
- $40..$44 : DisplayBeep(Ibase^.FirstScreen);
- { Screen blitzen lassen }
- $45 : If (e2^.ie_Qualifier and Qual) = Qual Then
- { Ende-Signal abschicken: }
- Signal(ThisTask, 1 shl SigBit);
- $46..$5E : e2^.ie_Class := IECLASS_NULL;
- { Tasten-Ereignis unterdrücken }
- { $5F : err := WindowToFront (Ibase^.ActiveWindow);}
- { Window nach vorn holen }
- Otherwise ;
- End;
-
- e2 := e2^.ie_NextEvent { nächstes Element der Liste }
- End;
-
- Handler := event; { Zeiger auf alte Liste zurückgeben }
-
- End;
-
- {$opt s0}
-
-
-
- Begin { Hauptprogramm }
-
- { Zunächst wird hier auf eine etwas eigenwillige Methode
- Maschinencode eingebunden: }
-
- InterfaceCode :=
- CodeTyp($48E7, $7F00, { MOVEM.L d1-d7,-(a7) }
- $48E7, $00FE, { MOVEM.L a0-a6,-(a7) }
- $4BF9, Addr(_paslibbase) shr 16,
- Addr(_paslibbase)and$FFFF,{ LEA _paslibbase,a5 }
- $4EB9, Addr(Handler) shr 16,
- Addr(Handler) and $FFFF, { JSR Handler }
- $4CDF, $7F00, { MOVEM.L (a7)+,a0-a6 }
- $4CDF, $00FE, { MOVEM.L (a7)+,a1-a7 }
- $4E75 { RTS }
- );
- { Die Assembler-Routine macht folgendes:
- Alle Register werden sicherheitshalber auf den Stack gerettet,
- denn Kickpascal-Prozeduren tun dies nicht(!) selbst. Dies
- geschieht in zwei MOVEM-Zeilen, um zu erreichen, daß die Register
- a0 und a1 zuunterst auf dem Stack liegen und somit vom in Pascal
- geschriebenen eigentlichen Handler als Parameter übernommen werden
- (a0 zeigt auf die Event-Liste, a1 auf die hier nicht benötigten
- Daten). Kickpascal-Code benötigt in a5 einen Zeiger auf die
- "Paslibbase" (Anfangsadresse des Global Vector und Basis der
- Laufzeitbibliothek). Die Adresse kann vom Linker importiert
- werden und wird mit einer LEA-Anweisung ins Adressregister 5
- geschrieben.
- Dann wird per JSR in die Prozedur "Handler" eingesprungen.
- Zum Schluß werden die Register rekonstruiert, außer d0, das den
- Rückgabewert der Function enthält. }
-
- { Input-Device öffnen: }
- port:=CreatePort("Input-Device-Port",0);
- ioreq := CreateStdIO (port);
- Open_Device ('input.device', 0, ioreq, 0);
- Writeln('Device geöffnet.');
-
- { Alles für die Kommunikation zwischen Handler und Hauptprogramm
- vorbereiten: }
- ThisTask := FindTask (Nil);
- SigBit := AllocSignal (-1);
- If SigBit < 0 Then Error ('Kein freies Signal!');
-
- { Interrupt-Struktur initialisieren: }
- With inter, is_node Do
- Begin
- is_Code := ^InterfaceCode;
- is_Data := Nil;
- ln_Name := 'Input-Handler in Kickpascal';
- ln_Pri := 51 { Intuition hat die Priorität 50. Wir erhalten
- also die Events, noch bevor Intuition sie zu
- sehen bekommt. }
- End;
-
- { Jetzt wird's spannend: }
- ioreq^.io_Data := ^inter;
- ioreq^.io_Command := IND_ADDHANDLER;
- err:=DoIO(PTR(ioreq));
- Writeln ('Handler hinzugefügt - Tastatur vermurkst.');
- Writeln ('Programm kann mit Shift-Shift-Esc abgebrochen werden.');
-
- { Warten, bis der Event-Handler sich meldet: }
- sig := Wait (1 shl SigBit);
-
- { System wieder aufräumen: }
- ioreq^.io_Data := ^inter;
- ioreq^.io_Command := IND_REMHANDLER;
- err := DoIO (PTR(ioreq));
- writeln('Handler entfernt.');
-
- FreeSignal (SigBit);
-
- Close_Device (ioreq);
- DeleteStdIO (ioreq);
- DeletePort (port)
-
- End.
-
-