Lange wurde er vermisst, endlich ist er da! War unser ATARI bis jetzt noch völlig schutzlos allem Schmutz und Unflat ausgeliefert, so gibt es nun endlich eine wirksame Abwehr von Beleidigungen, übler Nachrede und hässlichen Worten aller Art auf unserem Bildschirm - der ATARI-KNIGGE wacht!
KNIGGE überprüft ständig den Inhalt des Tastaturpuffers und kontrolliert, ob der über die Tastatur eingegebene Text mit einem der in einer Liste stehenden Wörter/ Textausschnitte übereinstimmt. Diese Liste ist hier in das Programm integriert und kann vor der Kompilierung beliebig erweitert werden. So wird etwa zuverlässig überwacht, ob der Anwender seinem Ärger über den jüngsten Bombenhagel (nach Eintippen von achtzehn Seiten Diplomarbeit mit derbrandneuen Super-Wahnsinns-Textverarbeitung) mit dem Ausdruck SCH.......PROGRAMM Luft zu verschaffen sucht - nach den ersten sieben Buchstaben erscheint eine freundliche Mahnung. und nach einer angemessenen Entschuldigung ("Pardon"-Button) wird die Eingabe umgehend wieder gelöscht!
Bei KNIGGE handelt es sich um ein mit CCD Pascal plus 2.xx geschriebenes Accessory, das sich nicht in die Menüzeile einträgt. Dies läßt sich einfach durch Weglassen des Menu_Register-Befehls erreichen. Die Accessory-typische Endlosschleife erwartet dann nur Timer-Events, d. h. alle soundsoviel Millisekunden wird die Hauptprozedur ausgeführt, die in diesem Fall den Tastaturpuffer betrachtet und auf unflätige Eingaben hin kontrolliert. Diese Lösung ist zwar nicht so schön wie ein speicherresidentes, im Interrupt hängendes Assembler-Programm, aber dafür kann man bequem in Pascal programmieren - und Knigge läuft auch so in allen GEM-Textverarbeitungen, die Accessories unterstützen.
Wie kann ich aber nun in meinem Accessory auf Tastatureingaben warten, ohne dabei etwa die gerade laufende Textverarbeitung zu stören? Interessant ist dies sicher auch für all diejenigen, die die unzähligen (elf) Shift-Shift-Alt-Control-Tastenkombinationen leid sind. Wer gerade wieder einmal festgestellt hat, daß sowohl der Speicheraufteiler als auch der vollhydraulische Tastatur-Reset über Shift-Shift-Alternate aufgerufen werden, sucht für eigene Programme sicherlich nach Alternativen. Die Lösung wurde in einer früheren Ausgabe der "ST Computer" für C-Programmierer bereits vorgestellt: die XBIOS-Funktion IORec. KNIGGE ist ein Beispiel für den Umgang mit dieser in Pascal. IORec liefert einen Zeiger auf den Tastaturpuffer sowie Zusatzinformationen.
Der Tastaturpuffer faßt im Normalfall 64 Zeichen, auf ihm bewegen sich ein Schreibund ein Lesezeiger. Ist eine Taste gedrückt worden, wird das Zeichen an der Stelle des Schreibzeigers im Puffer eingetragen und der Schreibzeiger weiterbewegt. Die Ausgabe auf dem Bildschirm wird über einen Vergleich von Schreib- und Lesezeiger gesteuert. Ist der Schreibzeiger schon weiter als der Lesezeiger, wird an der Stelle des Lesezeigers ein Zeichen aus dem Puffer geholt und der Lesezeiger weitergesetzt - solange, bis er den Schreibzeiger wieder eingeholt hat. Jedes Zeichen wird in vier Bytes abgelegt - unten der ASCII-, oben der Scan-Code. Bei Erreichen des Pufferendes wird jeweils am Anfang wieder begonnen.
Damit bei allen Vergleichen bequem mit Pascal-String-Operationen gearbeitet werden kann, unterhält KNIGGE einen eigenen Zeichenpuffer, in dem die letzten 255 Tastendrücke gespeichert werden. Das Vorhandensein neuer Zeichen wird hier über einen Vergleich von momentaner und voriger Position des Schreibzeigers geprüft, bei Ungleichheit das erste Zeichen des Puffer-Strings entfernt und dafür das neue Zeichen am Ende angehängt, Klein- in Großbuchstaben umgewandelt. Soll ein unerlaubtes Wort gelöscht werden, schreibt KNIGGE eine Sequenz von Backspace-Zeichen in den Puffer (Scan- und ASCII-Code!), setzt nach jedem den Schreibzeiger "von Hand" weiter und wartet einen Moment, damit die Textverarbeitung auch wirklich löschen kann.
Da im Tastaturpuffer alle über die Tastatur eingegebenen Zeichen stehen, wird immer nur eine unterbrechungsfreie Zeichenkette erkannt - also etwa M, I, S, T ohne jede Betätigung einer Cursor-, Funktions- oder ähnlichen Taste. Während KNIGGE ein Wort löscht, sollten keinerlei Tastatureingaben stattfinden, da sonst nur diese neuen Zeichen eliminiert werden.
Mit KNIGGE als Grundlage kann man seine Accessories nun beispielsweise auch über beliebige Tastenkombinationen aufrufbar machen oder eine komplette Tastaturbelegung mit Makros in Pascal programmieren (Alt P gedrückt? Dies löschen, stattdessen PROGRAM in den Tastaturpuffer schreiben! usw.). Eine zusätzliche Abfrage von Control- und Shift-Taste wäre etwa über die BIOS-Funktion KbShift zu erreichen.
{$A+,S2,D-,S-,T-,P-} { Accessory, 2 k Stack }
PROGRAM Knigge;
{ KNIGGE.ACC V 1.0
Demonstrationsprogramm zur Auswertung und
Modifikation des Tastaturpuffers
Überwachung auf Eintippen von Schimpfwörtern
u.ä., ggf. Ausgabe einer Zurechtweisung, in-
nerhalb von Textverarbeitungen Löschen der
Eingabe
von Andreas Hill
(c) MAXON Computer GmbH
Letzte Modifikation: 20. April 1989
Entwickelt unter CCD Pascal plus 2.0x auf ST}
{ Linker: ACC erstellen und PASTRIX einbinden }
CONST {$I GEMCONST} { div. GEM-Libraries }
TYPE {$I GEMTYPE}
{$I GEMSUBS}
CONST Zeilenlaenge = 255; { Interne Pufferlänge }
Momentchen = 150; { ms Wartezeit }
TYPE IORec_Type = PACKED RECORD
IBuf : LONG_INTEGER; { Pufferzeiger }
IBufSiz : SHORT_INTEGER; { Pufferlänge }
IBufHd : SHORT_INTEGER; { Lesezeiger }
IBufTl : SHORT_INTEGER; { Schreibzeiger}
IBufLow : SHORT_INTEGER;
IBufHi : SHORT_INTEGER;
END;
Schmutzpuffer_Typ = STRING[Zeilenlaenge];
VAR Appl : SHORT_INTEGER;
IORec_Adresse : LONG_INTEGER;
IO_Record : IORec_Type;
Alte_Position : SHORT_INTEGER;
Pruefzeile : Schmutzpuffer_Typ;
IDummy : SHORT_INTEGER;
FUNCTION IORec (Geraet : SHORT_INTEGER)
: LONG_INTEGER;
XBIOS (14);
{ Folgende Deklarationen TRIXSUBS.PAS entnommen}
FUNCTION LPeek (Adresse: LONG_INTEGER)
: LONG_INTEGER;
EXTERNAL;
PROCEDURE WPoke (Adresse: LONG_INTEGER;
Wert : SHORT_INTEGER);
EXTERNAL;
PROCEDURE LPoke (Adresse, Wert : LONG_INTEGER);
EXTERNAL;
PROCEDURE Evnt_Timer (Zeit : LONG_INTEGER);
VAR Msg : Message_Buffer; { <Zeit> ms warten }
BEGIN
IDummy := Get_Event (E_Timer, 0,0,0, Zeit,
False, 0,0,0,0, False, 0,0,0,0,
Msg, IDummy, IDummy,IDummy,
IDummy,IDummy, IDummy);
END;
PROCEDURE Peek_IORec (Adresse: LONG_INTEGER;
VAR Rec : IORec_Type);
VAR Magic : RECORD CASE BOOLEAN OF
False: (Adr : LONG_INTEGER);
True : (Ptr : ^IORec_Type)
END;
BEGIN
Magic.Adr := Adresse;
Rec := Magic.Ptr^;
END;
PROCEDURE Pruefzeile_leeren;
VAR i : SHORT_INTEGER; { Vergleichszeile anfangs}
BEGIN { mit Leerzeichen füllen}
Pruefzeile := '';
FOR i := 1 TO Zeilenlaenge DO
Pruefzeile := Concat (Pruefzeile,' ');
END;
PROCEDURE Zeichen_ausgeben (Zeichen: LONG_INTEGER);
BEGIN
Peek_IORec (IORec_Adresse, IO_Record);
WITH IO_Record DO BEGIN
IBufTl := IBufTl + 4; {Nächste Schreibpos.}
IF IBufTl >= IBufSiz THEN { Pufferende ? }
IBufTl := 0; {Dann an Anfang}
LPoke (IBuf+IBufTl,Zeichen); { Eintragen }
WPoke (IORec_Adresse+8,IBufTl); { Zeiger->}
END;
Evnt_Timer (Momentchen); { Kontrolle abgeben }
END;
PROCEDURE Pruefe (Schmutz : Schmutzpuffer_Typ);
VAR Laenge, i : SHORT_INTEGER;
BEGIN
Laenge := Length (Schmutz); { Eingabe prüfen }
IF Copy(Pruefzeile,Zeilenlaenge-Laenge+1,Laenge)
= Schmutz THEN BEGIN
IDummy := Do_Alert ('[0][ Aktion|SAUBERER BILDSCHIRM | Pfui! Pfui! Pfui!| Diese Eingabe|wird gelöscht][ Pardon ]',1);
Evnt_Timer (Momentchen); { Kurz warten }
FOR i := 1 TO Laenge DO { Backspaces }
Zeichen_ausgeben($000E0008); {Scan/ASC}
Write (Chr(7)); { Klingeling }
END;
END;
PROCEDURE Pruefliste_bearbeiten;
BEGIN {Alles in Großbuchstaben !!!}
Pruefe ('MIST') ;
Pruefe ('SCHEIP');
Pruefe ('SCHEIß');
Pruefe ('DOOF');
END;
PROCEDURE IORec_Inhalt_untersuchen;
VAR Zeichen : LONG_INTEGER;
ASCII_Code : SHORT_INTEGER;
BEGIN
{ IO-Record lesen }
Peek_IORec (IORec_Adresse, IO_Record);
WITH IO_Record DO
{ Wurde Pufferzeiger weitergesetzt ? }
IF IBufTl <> Alte_Position THEN BEGIN
{ Ältestes Zeichen löschen }
Delete (Pruefzeile,1,1);
{ Neues Zeichen aus Puffer lesen }
Zeichen := LPeek (IBuf+IBufTl);
ASCII_Code := Int (Zeichen);
{ Kleine in Großbuchstaben umwandeln }
IF ASCII_Code IN [97..122] THEN BEGIN
ASCII_Code := ASCII_Code - 32;
END;
Pruefzeile := Concat (Pruefzeile,Chr(ASCII_Code));
{ Neue Zeigerposition merken }
Alte_Position := IBufTl;
{ Alles Verbotene bemäkeln }
Pruefliste_bearbeiten;
END;
END;
PROCEDURE Ereignisverwaltung;
BEGIN
WHILE True DO BEGIN { Endlosschleife }
Evnt_Timer(50); { Meldung alle 50ms}
IORec_Inhalt_untersuchen;
END;
END;
BEGIN
IORec_Adresse:= IORec(1); { Adresse IO-Record}
Pruefzeile_leeren; { Mit Blanks füllen}
Appl := Init_GEM; {Accessory anmelden}
IF Appl >= 0 THEN BEGIN { Kein Menüeintrag }
Message (' KNIGGE ist installiert');
WriteLn (' A. Hill 4/89');
WriteLn;
Ereignisverwaltung; { Endlosschleife }
END;
END.