Ein Hauch von GEM: Fensterausgabe in Modula-2

Wie fast alle anderen Sprachen auch bietet das LPR-Modula-2-System eine integrierte Entwicklungsumgebung, innerhalb derer die Terminal-Fenster umgeleitet werden. So erhalten Anfänger in Programmierkursen schnell akzeptable Ergebnisse. Wenn dann jedoch das Programm zum ersten Mal gelinkt und außerhalb der Entwicklungsumgebung gestartet wurde, ist die Enttäuschung groß: Die Ausgaben werden einfach „irgendwo“ auf den Desktop geschmiert, keine Spur mehr von Fenstern oder von GEM.

Dabei macht es das LPR-Modula-2 den Anfängern sehr leicht, auch den fertig gelinkten Programmen einen „Hauch von GEM“ zu verleihen. Im Systemordner findet sich das Modul TermWindow, mit dem das Terminal-Fenster innerhalb der Entwicklungsumgebung geschrieben wurde. Mit ein wenig Geschick läßt es sich auch in eigenen Programmen anwenden. Dabei muß man wie im Modul TermTest verfahren.

Die Ein- und Ausgaben dürfen natürlich nur noch mit den aus TermWindow importierten Funktionen erfolgen. Hier ist Vorsicht geboten, denn aus Inout oder Terminal importierte Schreib- oder Leseprozeduren funktionieren innerhalb der Entwicklungsumgebung problemlos; erst im gelinkten Programm passiert dann Seltsames.

Zuerst einmal läßt sich so mit vier zusätzlichen Befehlen das übliche „Hello-World“-Programm aufpeppen. Bald jedoch wird man merken, daß der einzige _Read-Befehl des Moduls für einen Programmierkurs doch recht wenig ist. Ich habe daher in den Beispielprogrammen ein ReadString und ein ReadReal eingebaut. Dazu werden allerdings die Prozeduren StringToReal und RealToStringFixed aus Modula.LPR (Maxon-PD 247 oder 285) benötigt. Ohne die kommt aber - wegen der verbesserten MathLib0 - sowieso kein ernsthafter LPR-Programmierer aus.

Da erfahrungsgemäß das SetPos(y,x) bei Programmieranfängern Verwirrung stiftet, könnte man noch folgendes GotoXY einbauen;

PROCEDURE GotoXY(x, y : INTEGER);
BEGIN
    SetPos(y,x); (* Aus TermWindow *)
END GotoXY;

Natürlich sind die so erstellten Programme keine „echten“ GEM-Programme. Solange das Terminal-Fenster z.B. nur mit Read angehalten wird, haben andere Applikationen keine Chance. Aber um die Motivation von Programmieranfängern zu erhöhen oder selbst mal schnell ein kleines „unsauberes“ Programm zu schreiben (z.B. um einen Algorithmus zu testen), ist TermWindow recht gut geeignet.

Zum Schluß noch zwei Hinweise. Erstens: InitTerminal füllt die in WindowBase beschriebene Struktur „windowtype“, auf die u.a. NewTitle zugreift. Daher darf NewTitle erst nach InitTerminal (sonst erhält man - außerhalb der Entwicklungsumgebung - die Fehlermeldung „ungültiges Handle“) aufgerufen werden. Zweitens; Theoretisch müßte es möglich sein, mit wenigen Befehlen TermWindow auch als echte GEM-Anwendung zu nutzen. Die im Definitionsmodul enthaltenen Prozeduren ReactOnMessage, Redraw und WaitForRedraw legen dies nahe. Leider ist das Modul überhaupt nicht dokumentiert, und eigene Tests haben bisher noch keinen Aufschluß über den Funktionsumfang dieser Prozeduren ergeben.

Die Beispielprogramme zeigen die Anwendungsmöglichkeiten des Moduls TermWindow speziell in Programmierkursen.

MODULE TermTest;
FROM TermWindow IMPORT InitTerminal, OpenTerminal, CloseTerminal, 
                       DeleteTerminal, Read, WriteString, WriteLn, ... ;
BEGIN
    InitTerminal;   (* Scheint alle nötigen GEM-    *) 
    OpenTerminal    (* Anmeldungen zu erledigen     *)
    ...
    (* Jetzt die eigenen Ein- und Ausgaben *)
    ...
    CloseTerminal;
    DeleteTerminal;
END TermTest.

MODULE Kreis;   (* Kreisdarstellung n. Bresenham *) 
                (* Autor: Jörg Kantel            *)
                (* (c) MAXON Computer 1993       *)

FROM TermWindow IMPORT Read, Write, WriteString, 
                       SetPos, NewTitle, 
                       InitTerminal,
                       OpenTerminal, 
                       CloseTerminal, 
                       DeleteTerminal;

VAR x, y, xMitte,yMitte, r, rHoch2 : INTEGER; 
    taste                          : CHAR;

BEGIN
    xMitte := 38; yMitte := 10; r      := 8;
    x      := 0;  y      := r;  rHoch2 := r*r;

    InitTerminal(TRUE);
    NewTitle("Bresenham-Kreis");
    OpenTerminal(0,0, 0,0);
    (* Wenn w und h bei OpenTerminal = (0,0), 
       dann wird das Terminal über die gesamte 
       Bildschirmbreite geöffnet.
    *)

    REPEAT
        SetPos(yMitte+y, xMitte+2*x); Write("*"); 
        SetPos(yMitte+x, xMitte+2*y); Write("*"); 
        SetPos(yMitte-x, xMitte+2*y); Write("*"); 
        SetPos(yMitte-y, xMitte+2*x); Write(“*"); 
        SetPos(yMitte-y, xMitte-2*x); Write("*"); 
        SetPos(yMitte-x, xMitte-2*y); Write("*");
        SetPos(yMitte+x, xMitte-2*y); Write("*"); 
        SetPos(yMitte+y, xMitte-2*x); Write("*"); 
        INC(x);
        IF (x*x + y*y - y - rHoch2 >= 0) THEN 
            DEC(y);
        END;
    UNTIL x >= y;
    SetPos(20,1);
    WriteString("Ende mit beliebiger Taste");
    Read(taste);
    CloseTerminal;
    DeleteTerminal;
END Kreis.

MODULE TermTst2;
(* Testet die Eingabe-Möglichkeiten des Modules 
   TermWindow.
   Autor: Jörg Kantel 
   (c) MAXON Computer 1993
*)

FROM TermWindow IMPORT InitTerminal, 
                       OpenTerminal, 
                       CloseTerminal, 
                       DeleteTerminal, NewTitle, 
                       WriteString,
                       Write, WriteLn, Read; 
FROM ReallnOut  IMPORT StringToReal,
                       RealToStringFixed;

(* "ReallnOut" ist aus "Modula.LPR", Maxon-PD 247. *)

TYPE string = ARRAY[1..20) OF CHAR;

VAR c           : CHAR;
    s           : String;
    i           : CARDINAL;
    r           : REAL;
    zahl        : BOOLEAN;

    PROCEDURE ReadString(VAR s : String);
    (* Liest einen String ein, der mit einem 
       Leerzeichen oder mit [ENTER] abgeschlos-
       sen wird. Die maximale Länge des Strings 
       wird in TYPE String vereinbart.
       Während der Eingabe des Strings kann mit 
       [Backspace] korrigiert werden.
    *)
    VAR c : CHAR;
        i : INTEGER;
    BEGIN
        i := 1;
        REPEAT
            Read(c); Write(c);
            IF (i <= 20) THEN s[i] := c; END;
            IF ((i > 1) AND (c = 10C)) THEN 
            (* Eingabe Backspace? *) 
                i := i - 1;
            ELSE
                i := i + 1;
            END;
        UNTIL ((c = ' ') OR (c = 15C)); 
        s[i-1] := CHR(0C);
    END Readstring;

    PROCEDURE ReadReal(VAR s : String;
                       VAR r : REAL) : BOOLEAN; 
    (* Prozedur liest einen String ein und kon-
       vertiert ihn zu einer Real-Zahl. Dabei
       werden Komma oder Dezimalpunkt ebenso 
       berücksichtigt, wie die Exponent-/Man-
       tisse-Darstellung (25e-10, 25E10 etc.)
       Der String wird auf jeden Fall zurück-
       gegeben - auch wenn ein Fehler auftritt.
    *)
    VAR c  : CHAR;
        i  : INTEGER; 
        ok : BOOLEAN;
    BEGIN
        i := 1;
        REPEAT
            Read(c); Write(c);
            IF (i <= 20) THEN s[i] := c; END;
            IF (c = ',') THEN s[i] := '.'; BND;
            IF (c = 'e') THEN s[i] := 'E'; END;
            IF ((i > 1) AND (c = 10C)) THEN 
            (* Eingabe Backspace? *) 
                i := i - 1;
            ELSE
                i := i + 1;
            END;
        UNTIL ((c = ' ') OR (c = 15C);
        s[i-1] := CHR(0C);
        StringToReal(s, r, ok);
        RETURN ok;
    END ReadReal;

BEGIN
    InitTerminal(TRUE);
    NewTitle('Ein- und Ausgabe-Test'); 
    OpenTerminal(8,20, 230,380);

    WriteLn;
    WriteString('      Terminal-Test       ');
    WriteLn;
    WriteString('--------------------------');
    WriteLn;
    WriteString('Geben Sie ein Wort ein: '); 
    WriteLn;
    WriteString('\>');
    ReadString(s);
    WriteLn;
    WriteString('Die Eingabe lautet: ');
    WriteLn;
    WriteString(' ''); WriteString(s); WriteLn; 

    WriteLn;
    WriteString('Und nun eine Zahl: ');WriteLn; 
    WriteString('\>'); 
    zahl := ReadReal(s, r);
    WriteLn;
    WriteString('Ihre Eingabe: ');WriteLn; 
    WriteString(' '); WriteString(s); WriteLn; 
    WriteLn;
    IF (NOT zahl) THEN 
        WriteString(s);
        WriteString(' ist nicht korrekt!');
    ELSE
        r := r*r;
        RealToStringFixed(s, r, 12,4);
        WriteString!'Die Eingabe quadriert: '); 
        WriteLn;
        WriteString(s);
    END;
    WriteLn;

    WriteLn;
    WriteString('und nun das Scrollen:'); WriteLn; 
    WriteString('Weiter mit [ENTER]');    WriteLn; 
    WriteString('\>');
    REPEAT Read(c) UNTIL (c = 15C);

    WriteLn;
    FOR i := 1 TO 100 DO
        WriteString('Test'); WriteLn;
    END;
    WriteLn;
    WriteString('Das waren 100 Zeilen.'); WriteLn; 
    WriteLn;

    WriteLn;
    WriteString('Ende mit ESC'); WriteLn; 
    WriteString('\>');
    REPEAT Read(c) UNTIL (c = 33C);
    CloseTerminal;
    DeleteTerminal;

END TermTst2.

Jörg Kantel
Aus: ST-Computer 07 / 1993, Seite 68

Links

Copyright-Bestimmungen: siehe Über diese Seite