Schnittstellen-Dschungel: Neue Rechner - Neue Schnittstellen, Teil 4

Heute folgt endlich der letzte Teil der Listings, der das in Teil 1 angekündigte Terminalprogramm vervollständigt. Da das Programm regen Gebrauch von Fenstern macht, wollen wir hier nicht nur das Programm beschreiben, sondern die Gelegenheit nutzen, und uns zusätzlich näher mit einem Teil der Mensch-Maschine-Schnittstelle beschäftigen, nämlich der oft vernachlässigten Fensterprogrammierung.

Unserer Ansicht nach ist nämlich mittlerweile über die Programmierung von Dialogboxen und Menüs schon ziemlich viel geschrieben worden, so daß die meisten Programmierer problemlos in der Lage sind, GEM-Programme zu entwickeln, die diese Objekte benutzen. Das zeigt auch ein Blick auf viele PD-Programme, die zwar Dialoge und Menüs verwenden, ansonsten aber weder Fenster noch ein eigenes Desktop benutzen. Offenbar herrscht in dieser Beziehung noch ein Nachholbedürfnis, dem wir hiermit nachkommen wollen.

Daher beschreiben wir im folgenden nicht nur „graue Theorie“ mit Funktionsfragmenten, die niemand zu einem lauffähigen Programm zusammenstellen kann, sondern wir beziehen uns dabei auf unsere Listings. So kann das Zusammenwirken der verschiedenen Komponenten genau untersucht werden, was mit einzelnen Bruchstücken nicht möglich wäre. Jeder kennt die üblichen GEM-Kurse, deren einzelne Listings zwar funktionieren, jedoch im Zusammenspiel mit anderen Komponenten oftmals unerwartete Ergebnisse hervorrufen oder in der veröffentlichten Form für eigene Zwecke meist nicht zu gebrauchen sind. Zudem beginnen die meisten GEM-Kurse mit den Grundlagen, verschweigen jedoch die komplizierteren, aber dadurch auch interessanten Details, die sich der Programmierer dann in mühevoller Kleinarbeit selbst erarbeiten muß.. Wir setzen deshalb die Grundlagen (C-Programmierung und GEM-Grundkenntnisse) voraus und fangen hoffentlich da an, wo andere Kurse aufhören. Es läßt sich allerdings nicht vermeiden, daß wir dabei ganz vorne beginnen.

Am Anfang war das Fenster...

Beginnen wir also mit der Fensterprogrammierung. Der Anfang aller Dinge ist hierbei die Funktion wind_create(). Wir wollen hier nicht jeden einzelnen Parameter bis ins Detail beschreiben, das kann in jedem Handbuch nachgelesen werden. Vielmehr wollen wir die Zusammenhänge verdeutlichen, die zwischen den einzelnen Funktionen bestehen, um die Vorgehensweise bei der Fensterprogrammierung besser verständlich zu machen.

Wie wohl bekannt sein dürfte, muß vor dem Öffnen eines Fensters dieses erst einmal mittels oben genannter Funktion erzeugt werden. wind_create() legt dazu im Speicher eine Datenstruktur an, die das Fenster beschreibt, und liefert ein Handle zurück, mit dem das Fenster in Zukunft angesprochen wird. Unter anderem verlangt die Funktion als Parameter die Position und die maximale Größe des Fensters.

Wie man jedoch bei einer späteren Größenänderung des Fensters feststellen kann, ist es ohne weiteres möglich, das Fenster beliebig groß zu machen, sei es nun mit wind_open() oder durch Aufziehen des Fensters mit der Maus. Wer sich nun fragt, wozu wind_create() die Maximalgröße eigentlich wissen will, fragt sich dies sicherlich nicht ganz zu Unrecht.

Vom Window verweht

Zuerst einmal: es ist tatsächlich möglich, dem Fenster jegliche beliebige Größe zu geben, auch wenn sie die bei wind_create() angegebene Maximalgröße überschreitet. Diese Maximalgröße kann jedoch zu einem späteren Zeitpunkt (z.B. bei Anklicken der Fullbox des Fensters) mit wind_get(..., WF FULLXYWH,...) wieder abgefragt werden, um das Fenster auf die vorher festgelegte Maximalgröße zu setzen. Man kann jedoch auch auf die hier beschriebene Möglichkeit verzichten und die Maximalgröße bei jedem Anklicken der Fullbox neu berechnen. Schließlich kann es auch Vorkommen, daß die spätere Maximalgröße des Fensters bei seiner Erzeugung noch gar nicht bekannt ist. Das ist in unserem Terminalprogramm zwar nicht der Fall, trotzdem wird die Maximalgröße hier zum Beispiel über ein Flag verwaltet (siehe Funktion wm_fulled() im Listing WINDOW.C).

Soll das Fenster auf die größtmögliche Größe, d.h. in der Regel Bildschirmgröße, gesetzt werden, so kann es nützlich sein, die Größe des Desktops abzufragen. Dazu muß man wissen, daß das Desktop immer das Fenster-Handle 0 besitzt. Mittels wind_get(0,WF_WORKXYWH,...) kann nun die Größe des Desktops festgestellt werden.

Jetzt ist der Zeitpunkt gekommen, Fenstertitel sowie Infozeile (sofern vorhanden) mittels wind_set() zu setzen. Dies muß unbedingt vor dem Öffnen des Fensters geschehen, damit die entsprechenden Fensterkomponenten einen definierten Inhalt haben. Ansonsten kann es passieren, daß der Rechner beim Öffnen des Fensters abstürzt. Zu beachten ist ferner, daß die übergebenen Zeichenketten in einem statischen Speicherbereich liegen müssen, denn das AES greift bei einem Redraw immer direkt auf die vom Programmierer übergebenen Adressen zu!

Jetzt könnten wir das Fenster eigentlich an jeder beliebigen Stelle des Bildschirms öffnen - doch wer will das schon? Mit einem einfachen Trick läßt sich zumindest bei der Ausgabe von Text und bei der Benutzung von vro_cpyfm() die Arbeitsgeschwindigkeit erheblich beschleunigen. Dazu muß lediglich die x-Koordinate des Arbeitsbereiches unseres Fensters auf eine Zeichengrenze gelegt werden. Da jedoch bei dessen Öffnen die Abmessungen des gesamten Fensters angegeben werden müssen, ist zunächst mittels wind_calc(WC_BORDER,...) von der Größe des Arbeitsbereiches auf die Gesamtgröße des Fensters umzurechnen. Hierbei ist für die x-Koordinate des Arbeitsbereiches auch die Null zulässig, so daß der ein Pixel breite linke Rand des Fensters außerhalb des Desktops liegt.

Fenster zum Hof

Nach dem Initialisieren des Fensters wird es jetzt endlich mit wind_open() geöffnet. Als Parameter werden nur die Anfangsposition und -größe übergeben. Mancher wird sich jetzt vielleicht fragen, warum wind_create() und wind_open() nicht in einem Betriebssystemaufruf zusammengefaßt worden sind. Die Antwort ist jedoch ganz einfach: Durch die zwei getrennten Aufrufe können beispielsweise unbedingt benötigte Fenster zu Beginn des Programmes erzeugt werden; und falls nicht genügend Fenster erzeugt werden konnten, läßt sich rechtzeitig eine Fehlermeldung generieren. Die Fenster sind somit reserviert und bei Bedarf zu öffnen, ohne den Fall betrachten zu müssen, daß zu einem kritischen Zeitpunkt keine Fenster mehr verfügbar sind. Ein solches Vorgehen sollte jedoch nur dann in Betracht gezogen werden, wenn das entsprechende Programm ohne eine Mindestanzahl von Fenstern nicht auskommt. Ansonsten würden nur unnötig Fenster-Handles belegt, die beispielsweise auch von Accessories benutzt werden. Schließlich ist der Atari bekanntermaßen nur mit acht Fenstern gesegnet, von denen das Desktop bereits eines darstellt.

Erst jetzt könnte ins Fenster gezeichnet werden, doch sollte man dies auf keinen Fall „von Hand“ tun! Denn nach dem Öffnen des Fensters wird automatisch ein Redraw-Ereignis vom Betriebssystem generiert. Dieses Ereignis wird in der Regel dazu benutzt, um eine Redraw-Routine aufzurufen, die den Fensterinhalt oder Teile davon neu zeichnet. Die Redraw-Routine muß zu jedem Zeitpunkt wissen, was gerade im Fenster dargestellt wird, denn sie kann jederzeit aufgerufen werden, um einen Teil des Fensters neu zu zeichnen.

Bei einem Textfenster (wie z.B. in unserem Programm) genügt es, sich für jede Zeichenposition ein Byte zu merken; ein objektorientiertes Programm müßte für jedes Objekt eine entsprechechend aufgebaute Struktur verwalten, und bei einem Malprogramm müßte sogar der gesamte Fensterinhalt als Bitmap im Hintergrund gehalten werden.

Hat sich der Fensterinhalt geändert, muß die Information nur im Hintergrund aktualisiert werden; anschließend genügtes, die Redraw-Routine „manuell“ aufzurufen. Das kann zum Beispiel der Fall sein, wenn ein Scroll-Balken betätigt wurde, dazu aber später noch mehr.

American Graffiti

Jetzt jedoch zum Redraw selbst. In folgenden Fällen fordert das AES das Programm auf, Teile oder den gesamten Fensterinhalt neu zu zeichnen:

  1. Das Fenster wurde gerade geöffnet.
  2. Teile des Fensterinhalts waren bisher verdeckt und sind jetzt sichtbar geworden, z.B. weil ein Fenster nach vorne gebracht oder verschoben wurde.
  3. Das Fenster wurde vergrößert. Hierbei ist anzumerken, daß keine Redraw-Message erzeugt wird, wenn das Fenster verkleinert wurde! Verwaltet man in seinem Fenster beispielsweise eine Anzahl von Icons, die bei einer Größenänderung des Fensters in ihrer Position entsprechend angepaßt werden sollen, muß der Fall der Fensterverkleinerung vom Programmierer also selbst abgeprüft und der Fensterinhalt gegebenenfalls neu gezeichnet werden.

Für jedes Fenster wird eine Rechteckliste verwaltet, die die sichtbaren Teile eines Fensters in Form von Rechtecken beschreibt. Ein weitverbreiteter Irrtum ist, daß eine Rechteckliste nur nach einem Redraw-Ereignis abgefragt werden könne. Tatsächlich kann sie jedoch jederzeit abgefragt werden! Sie dient dazu, beim Zeichnen nur diejenigen Bereiche auch neu zu zeichnen, die gezeichnet werden dürfen, weil sie sichtbar sind. Hierzu wird dem VDI ein sogenannter Clipping-Bereich mitgeteilt (ebenfalls ein Rechteck), auf den alle folgenden Ausgaben beschränkt werden, bis der Clipping-Bereich wieder abgeschaltet oder verändert wird. Ausgaben auf Bereiche außerhalb des Clipping-Bereiches zeigen dabei keine Wirkung. So wird vermieden, daß „aus Versehen“ andere Fenster zerstört werden. Um nun aber die Rechtecke zu ermitteln, die wirklich neu gezeichnet werden müssen, müssen sämtliche Rechtecke in der Rechteckliste mit dem bei der Redraw-Message mitgelieferten Rechteck geschnitten werden. Nur die so berechneten Bereiche müssen letztendlich gezeichnet werden.

An dieser Stelle ist ein weiterer Trick zur Beschleunigung der Textausgabe angebracht, der eine sehr deutliche Geschwindigkeitssteigerung bringt. Die VDI-Funktion v_gtext() prüft nämlich bei jedem auszugebenden Zeichen ab, ob es im Clipping-Bereich liegt. Paßt jedoch der gesamte String in den Clipping-Bereich, so wird überhaupt nicht geprüft, ob ein Zeichen noch innerhalb des Clipping-Bereiches liegt oder nicht. Die sich dadurch ergebende Zeitersparnis ist enorm!

Abb. 1: Mit diesem Dialog können die bis zu vier seriellen Schnittstellen einzeln (und unterschiedlich) konfiguriert werden.
Abb. 2: So präsentiert sich das Terminalprogramm, wenn alle vier Fenster und der Info-Dialog geöffnet sind.

Um dies ausnutzen zu können, müssen jedoch einige Voraussetzungen gegeben sein: Einerseits muß außer der x-Koordinate auch die Breite des Fensterarbeitsbereiches auf Zeichengrenzen eingestellt werden, und andererseits muß das neu zu zeichnende Fenster in voller Breite sichtbar sein. Damit v_gtext() auch erkennt, daß der auszugebende Text ganz in den Clipping-Bereich hineinpaßt, wird dessen Breite einfach noch etwas breiter gemacht. Ist die auszugebende Zeichenkette länger, als das Fenster breit ist, muß sie natürlich an der richtigen Stelle abgeschnitten werden, damit ohne Clipping nicht über den rechten Fensterrand hinausgeschrieben wird.

Maverick & Goose, Iceman & Slider...

Einer der Vorteile, die Fenster bieten, besteht darin, daß es möglich ist, mit seiner Hilfe mehr Daten darzustellen, als eigentlich auf den Bildschirm passen. Um an diese Daten heranzukommen, sind die allgemein bekannten Rollbalken, neudeutsch auch „Slider“ genannt, eingeführt worden. Wie man diese in ihrer richtigen Größe an der richtigen Stelle darstellt, ist auf den ersten Blick (und auch auf den zweiten) gar nicht mal so einfach.

Im folgenden wollen wir uns nur auf den vertikalen Slider beziehen, alle Angaben gelten in analoger Weise jedoch auch für den horizontalen.

Das eigentliche Problem besteht nämlich nicht darin, sie anzuzeigen, sondern ihre Größe und Position zu verwalten. Wenden wir uns zuerst der Größe zu. Die Größe wird durch Werte von 1 bis 1000 repräsentiert, wobei 1 die kleinste Slider Größe darstellt; bei einer Slider-Größe von 1000 bedeckt der Slider den gesamten ihm zur Verfügung stehenden Bereich. Die erste Hürde besteht darin, das Verhältnis von gesamter Objektgröße zum dargestellten Teil in Werte von 1 bis 1000 umzurechnen.

Die Formel für diese Umrechnung sieht in C-Syntax jedoch relativ einfach aus:

groesse = min (1000L, 1000L * sichtbarer_teil / gesamtgroesse);

Dabei müssen natürlich die Variablen sichtbarer_teil und gesamtgroesse die gleichen Einheiten besitzen; würde die Variable sichtbarer_teil beispielsweise die Höhe des Fensters in Pixeln und gesamtgroesse die Gesamtlänge eines Textes in Zeilen angeben, so ginge die obige Umrechnung garantiert schief.

Warum nun das min() in der Formel? Die Antwort ist ganz einfach: Ist sichtbarer_teil (d.h. das Fenster) größer als gesamtgroesse, würde der rechte Teil der Formel einen Wert größer als 1000 liefern, was jedoch nicht zulässig ist. Ebenso sollte darauf geachtet werden, daß gesamtgroesse nicht 0 wird; in diesem Fall sollte groesse ebenfalls auf den Wert 1000 gesetzt werden.

Unbedingt sollte darauf geachtet werden, daß mit Langwörtern gerechnet wird, da sonst sehr leicht der Integer-Zahlenbereich verlassen werden kann. Laut Kernighan & Ritchie sollte es genügen, wenn wie oben ein Operand vom Typ „long“ ist. Der Compiler sollte dann die übrigen Operanden ebenfalls nach „long“ wandeln; darauf kann man sich jedoch nicht bei jedem Compiler unbedingt verlassen. In Zweifelsfällen sollten deshalb alle Operanden mittels Casting in den Typ „long“ umgewandelt werden.

Die so berechnete Größe kann dann mittels wind_set(..., WF_VSLSIZE,...) (bzw. WF_VHSLSIZE bei horizontalem Slider) gesetzt werden.

Wird das Fenster in der Größe verändert, generiert das AES eine WM_SIZED-Message. Daraufhin sollten auch die Slider-Größen neu berechnet und eingestellt werden. Ansonsten ist eine Berechnung der Slider-Größen nur notwendig, wenn das Fenster zum ersten Mal geöffnet wurde oder sich die Gesamtlänge des dargestellten Objektes geändert hat (z.B. wenn in einem Text eine Zeile eingefügt wurde).

Die Position des Sliders wird ebenfalls durch eine Zahl von 1 bis 1000 dargestellt. Die Formel zur Positionsberechnung eines Sliders sieht so aus:

position = 1000L * anfang / ausserhalb;

Dabei ist anfang der Anteil, der vor dem Beginn des Fensters liegt, und ausserhalb der Gesamtanteil, der außerhalb des Fensters liegt; er berechnet sich in der Regel aus der Gesamtgröße abzüglich des sichtbaren Teils. Ein Beispiel soll das verdeutlichen: Nehmen wir an, im Fenster soll ein Text dargestellt werden, der 25 Zeilen lang ist, das Fenster kann zur Zeit jedoch nur 9 Zeilen darstellen. Die erste im Fenster sichtbare Zeile sei die Zeile 4 (beginnend mit 0). Daraus ergibt sich, daß anfang den Wert 4 (Zeilen 0 bis 3) und ausserhalb den Wert 25 - 9= 16 hat. Somit ergibt sich für position der Wert 250.

Auch die Position des Sliders wird wieder mit Hilfe der Funktion wind_set (...,WF_VSLIDE,...) (bzw. WF_HSLIDE für den horizontalen Slider) eingestellt.

Die Position der Slider sollte außer bei Öffnen eines Fensters immer dann neu berechnet und gesetzt werden, wenn sich die Position des Fensterausschnitts relativ zum Gesamtobjekt geändert hat, sei es durch Verändern der Objektgröße oder durch irgendeine Fenstermanipulation von seiten des Benutzers.

Das Anklicken des Sliders selbst kann nur eine Nachricht generieren: WM_HSLID bzw. WM_VSLID. Dieser liefert die vom Benutzer gewünschte neue Slider-Position im Bereich von 1 bis 1000 zurück. Dieser Wert kann entweder benutzt werden, um den Slider direkt zu setzen, oder erst nach Einflußnahme des Programmierers (z.B. wenn der Slider lediglich in 100er-Schritten verschoben werden soll). Natürlich muß daraufhin auch der Fensterinhalt neu gezeichnet werden. Das Betriebssystem liefert hier keine Re-draw-Message! Meistens kann man jedoch mit der Zahl im Bereich von 1 bis 1000 wenig anfangen; zuerst muß diese Zahl wieder in eine brauchbare Form umgerechnet werden. Dazu benötigen wir die Umkehrfunktion der oben aufgeführten Formel, mit der im Textbeispiel die erste sichtbare Zeile im Fenster berechnet werden kann:

anfang = message_buffer[4] * ausserhalb / 1000L;

Die oben angeführten Berechnungen müssen ebenfalls durchgeführt werden, wenn der schraffierte Bereich außerhalb des Sliders angeklickt wird. In diesem Fall erhält man die Meldung WM_ARROWED mit einem der Untertypen WA_UPPAGE, WA_DNPAGE, WA_LFPAGE oder WA_RTPAGE. Mit anderen Worten: es soll um eine Seite in eine der angegebenen Richtungen gescrollt werden. Die Größe einer Seite wird dabei vom Programmierer festgelegt.

Beim Anklicken der Pfeiltasten (sofern vorhanden) wird wiederum die Meldung WM_ARROWED erzeugt, diesmal aber mit einem der Untertypen WA_UPLINE, WA_DNLINE, WA_LFLINE oder WA_RTLINE erzeugt. Hier sollte jeweils um einen entsprechend geringeren Betrag in die angegebene Richtung gescrollt werden.

Bei all diesen Meldungen sollte einerseits die Slider-Position aktualisiert und andererseits auch darauf geachtet werden, daß der Fensterausschnitt nicht über das Ende (oder den Anfang) des Dokuments hinaus gescrollt wird.

Vorhang zu!

Klickt der Benutzer das Schließfeld eines Fensters an, so erzeugt das AES eine WM_CLOSED-Meldung. Das Programm sollte dann das Fenster mit wind_close() schließen. Ist das geschehen, befindet sich die Fensterstruktur jedoch immer noch im Speicher und kann mit wind_open() jederzeit wieder geöffnet werden. Erst wenn wind_delete() aufgerufen wird (unbedingt nach Aufruf von wind_close()), werden die entsprechenden internen Datenstrukturen des AES freigegeben und sind für andere Fenster verfügbar. Ab TOS 1.04 ist zusätzlich die Funktion wind_new() verfügbar, die alle Fenster schließt und löscht.

Somit haben wir jetzt den vollständigen „Lebenszyklus“ eines Fensters durchlaufen. Die Verwaltung von mehr als einem Fenster ist jedoch auch nicht viel schwieriger. Bei allen Nachrichten, die mit Fenstern zu tun haben, liefert das AES das Handle des angesprochenen Fensters mit. Der Programmierer möchte in der Regel mit Fensternummern arbeiten und nicht mit Handles, so daß es sich als praktisch erwiesen hat, ein Array anzulegen. In dieses wird für jedes Fenster das entsprechende Handle eingetragen, wodurch es möglich wird, schnell und einfach vom Handle auf Fensternummer umzurechnen und umgekehrt.

Da Fenster-Handles normalerweise jedoch nur Werte von 0 bis 7 annehmen können, gibt es einige Programmierer, die auf die Umrechnung von Handles in Fensternummern verzichten und den entsprechenden Array-Eintrag direkt ansprechen. Das sollte man jedoch auf gar keinen Fall tun! Erstens ist nirgends dokumentiert, daß man nur Werte von 0 bis 7 als Handles geliefert bekommt (das kann sich in zukünftigen TOS-Versionen schnell ändern), und zweitens gibt es bereits Programme, die GEM wesentlich mehr Fenster zur Verfügung stellen (siehe [1]) und demzufolge auch größere Handles liefern.

Es bietet sich an, in dem genannten Array gleich weitere fensterspezifische Informationen abzulegen, die für die Fensterverwaltung benötigt werden. In unserem Programm ist das ein Array vom selbstdefinierten Typ WINDOW, der alle benötigten Werte enthält. Bei einem Mausklick in ein Fenster wird das Handle des betroffenen Fensters zwar nicht angegeben, jedoch läßt es sich leicht mit der Funktion wind_find() ermitteln.

Kommen wir jetzt noch zur letzten, bisher nicht erwähnten Fensterfunktion: wind_update(). Sie wird immer dann benötigt, wenn in ein Fenster (bzw. auf den Bildschirm) gezeichnet werden soll. Wird nämlich wind_update(BEG_UPDATE) aufgerufen, ist es nicht mehr möglich, daß andere Programme inklusive des AES den Bildschirm verändern und möglicherweise eigene Zeichenoperationen stören; das betrifft insbesondere Drop-Down-Menüs. Ist das Zeichnen ins Fenster beendet, muß anschließend ein wind_update(END_UPDATE)-Aufruf erfolgen. Ansonsten bleibt die Bildschirmausgabe für andere Programme auf ewig gesperrt.

Es sollte auch nicht vergessen werden, vor allen Bildschirmausgaben die Maus abzuschalten (und hinterher wieder einzuschalten). Manchmal kann es notwendig sein, daß ein Programm alle Mausklicks erhalten soll, also auch die, die auf Randelemente eines Fensters oder auf die Menüleiste erfolgen. Solche Programmteile müssen dann mit wind_update(BEG_MCTRL) und wind_update(END_MCTRL) geklammert werden. Das bewirkt also nichts anderes, als daß für die Dauer dieses Programmteils der Bildschirm-Manager abgeschaltet wird. Diese Funktion benötigt man beispielsweise, wenn man einen eigenen Dialog-Handler schreiben möchte.

Des Desktops neue Kleider

Jetzt wissen wir also, wie wir Fenster zu öffnen und zu verwalten haben. Leider liegen sie aber noch auf dem bekannten grauen (oder grünen) Hintergrund. Wie aus vielen Programmen (u.a. Ataris Desktop) bekannt ist, ist es zusätzlich möglich, auf dem Hintergrund Icons anzulegen. Dazu stellt das AES mit der Funktion wind_set(...,WF_NEWDESK,...) die Möglichkeit zur Verfügung, einen eigenen Objektbaum als Desktop anzumelden. Dies hat gegenüber einem selbstverwalteten Hintergrund den Vorteil, daß sämtliche Redraws vom AES übernommen werden. Dazu muß im Resource Construction Set ein normaler Dialog angelegt werden, der später als Desktop dargestellt werden kann. Da bei der Erstellung des Desktops keine Annahmen über die später verwendete Bildschirmgröße gemacht werden können, sollten Breite und Höhe des Desktops nach Programmstart ermittelt und in die ob_width- und ob_height-Komponenten des Wurzelobjektes des neuen Desktops eingetragen werden. Das Programm muß anschließend nur noch den Objektbaum einmal mit objc_draw() zeichnen. Da ein solcher Objektbaum ein Standard-Objektbaum ist, kann er nicht nur Icons beinhalten, sondern auch beliebige andere Objekte, wie z.B. Buttons oder benutzerdefinierte Objekte. In unserem Programm haben wir jedoch der Einfachheit halber nur Icons verwendet.

Ist bei Start eines Programmes nicht bekannt, wieviel Icons insgesamt verwendet werden sollen, muß der Objektbaum die maximal mögliche Zahl Icons enthalten; nicht benötigte Icons können dann einfach durch Setzen des HIDETREE-Flags von der Oberfläche entfernt werden. In unserem Terminalprogramm werden so je nach Rechnertyp nur die Icons angezeigt, für die auch eine entsprechende Schnittstelle vorhanden ist. Beim Anklicken eines Icons wird dieses im Gegensatz zu Dialogboxen nicht automatisch selektiert; das muß vom Programm übernommen werden. Dazu wird bei evnt_multi() u.a. auf Mausklicks gew artet. Ist ein Mausereignis eingetreten, muß mittels der wind_find()-Funktion das Fenster ermittelt werden, das sich unter den Mauskoordinaten befindet. Handelt es sich dabei um das Desktop, wird das Fenster-Handle 0 zurückgegeben. Nun wissen wir also, daß der Mausklick auf das Desktop erfolgt ist. Den benötigten Objektindex ermittelt jetzt die Funktion objc_find(). Soll das Objekt selektiert dargestellt werden, genügt es nicht, das SELECTED-Flag zu setzen, das Objekt muß auch noch selbst gezeichnet werden. Das nimmt uns das AES leider nicht ab, da es natürlich keine Kenntnis darüber hat, daß sich das entsprechende Flag geändert hat.

Hier darf im Normalfall allerdings nicht - wie von Dialogboxen gewohnt - die Funktion objc_change() oder objc_draw() benutzt werden, um das Objekt selektiert darzustellen. Denn es ist ohne weiteres möglich, daß ein Icon teilweise von einem Fenster verdeckt ist. Ein Neuzeichnen des gesamten Objektes würde in diesem Fall einen Teil des Fensters zerstören. Es darf also nur der Teil des Icons neu gezeichnet werden, der nicht von Fenstern verdeckt wird. Da das Desktop jedoch ein Fenster wie (fast) jedes andere ist, verfügt es auch über eine Rechteckliste, die wie üblich abgefragt werden kann. Jetzt muß jedes Rechteck in der Liste mit dem Objektrechteck geschnitten werden. Existiert ein Schnittbereich, wird er als Clipping-Bereich für objc_draw() benutzt und der entsprechende Teil des Icons neu (selektiert) gezeichnet. Analog ist beim Deselektieren vorzugehen.

Soll ein Icon verschoben werden (was in unserem Programm nicht möglich ist), genügt es, die x- und y-Koordinate des Icons entsprechend zu verändern und anschließend den Objektbaum an der neuen und alten Position mit oben beschriebener Methode neu zu zeichnen. Es ist dabei völlig irrelevant, ob ein Icon ganz oder teilweise von einem Fenster verdeckt ist, da die obige Methode das berücksichtigt.

Behind the Scenes

Jetzt kommen wir endlich zur Programmbeschreibung. Wer fleißig die Listings der letzten Teile abgetippt hat, wird schon wissen, daß das Programm aus vier Modulen besteht. Der Rest der Welt weiß es eben jetzt. Die Aufgabenverteilung ist dabei wie folgt:

Die globalen Variablen befinden sich in der Header-Datei VARIABLE.H. Dazu sind sie als GLOBAL deklariert, was in allen Modulen außer dem Hauptmodul als „extern“ definiert ist. Das hat den Vorteil, daß die globalen Variablen nicht über alle Module verteilt sind, sondern zentral in einer Datei existieren. Dieses Verfahren stammt aus [2]. Sämtliche Prototypen befinden sich in der Header-Datei PROTO.H. In TT44TT.H liegen die vom RCS erzeugten Objektindizes. Die restlichen (allgemeinen) Definitionen befinden sich im Header TERMDEFS.H.

Beginnen wir mit dem Modul TT44TT.C. Was soll eigentlich dieser komische Name? Ganz einfach, er ist die Abkürzung für „Terminal Times Four For TT’s“. Der erste interessante Punkt ist die Bestimmung der Anzahl der seriellen Schnittstellen in der Funktion main(). Abhängig von der TOS-Version wird dazu die Funktion Bconmap() (siehe Teil 1 dieser Serie) benutzt, ansonsten wird angenommen, daß nur die beim ST übliche eine Schnittstelle vorhanden ist.

Als nächstes wird mittels Giaccess() geprüft, ob der LAN-Port aktiv ist. Ist das der Fall, steht eine Schnittstelle weniger zur Verfügung. Übrigens läßt sich der LAN-Port durch Offgibit(0x7f) ein- und durch Ongibit(0x80) wieder ausschalten.

Des weiteren werden in main() ein eigenes Desktop installiert und die Fenster initialisiert. Darauf wollen wir hier jedoch nicht weiter eingehen, da wir die Vorgehensweise oben ausführlich beschrieben haben.

Nach den Initialisierungen wird dann die Funktion events() aufgerufen, die die Hauptschleife und das unvermeidliche evnt_multi() enthält. Und dabei kommen wir auch schon zum eigentlichen Clou des Programmes.

Normalerweise kommunizieren Programme nämlich nur über das aktive (d.h. oberste) Fenster. Da das AES für jedes Fenster zu jedem Zeitpunkt eine Rechteckliste verwaltet (wie oben beschrieben), ist es jedoch ohne weiteres möglich, auch in inaktive Fenster auszugeben. Und genau das wird hier gemacht. Das Programm muß hierzu merken, wann in welches Fenster welche Daten ausgegeben werden müssen und diese an der richtigen Position auch ausgeben.
Die Eingabe kann dagegen nicht in alle Fenster gleichzeitig erfolgen; daher werden die Zeichen, die von der Tastatur kommen, immer an die zum aktiven Fenster gehörende Schnittstelle gesendet. Wer sich wundert, warum die eingetippten Zeichen nicht im Fenster erscheinen, dem sei gesagt, daß es sich nicht um eine Textverarbeitung, sondern um ein Terminalprogramm ohne Echo-Funktion handelt. Mit anderen Worten: alle eingegebenen Zeichen müssen von der Gegenseite (z.B. Mailbox) zurückgesendet werden.

Special Effects

Jetzt zurück zur Ausgabe: Alle 100 Millisekunden (ausgelöst durch evnt_multi()) werden alle Schnittstellen überprüft, ob Zeichen zur Ausgabe anliegen. Ist das der Fall, wird die Funktion do_output() aufgerufen, um den Puffer zu entleeren und ins Fenster auszugeben. Es wird also quasi Multitasking ausgeführt, auch wenn es sich dabei nur um das eingeschränkte Multitasking handelt, das GEM zur Verfügung stellt. Die Routine do_output() befindet sich (logischerweise) im Modul OUTPUT.C. Um jederzeit in der Lage zu sein, den Fensterinhalt neu zu zeichnen, gehört zu jedem Fenster ein „virtuelles Terminal“. Dabei handelt es sich um ein zweidimensionales Feld vom Typ „char“, das ein 80 x 25 Zeichen großes Terminal darstellt. Alle Ausgaben geschehen zunächst in dieses virtuelle Terminal, so daß zum Beispiel bei einem Linefeed hier der gesamte Inhalt um eine Zeile nach oben kopiert werden muß. Das ist notwendig, da das virtuelle Terminal die Grundlage für alle Fensterausgaben darstellt.

Leider war es wegen der Länge des Listings nicht möglich, einen bestimmten Terminaltyp zu realisieren. Das Programm ist als Ausgangspunkt für ein eigenes Terminalprogramm zu sehen, in dem die gewünschte Terminalemulation eingebaut werden kann. Dazu ist bereits die Funktion handle_escape() vorgesehen, die allerdings bisher noch ein karges Dasein fristet.

Des weiteren wären folgende Erweiterungen denkbar:

Wer das Programm erweitern möchte, wird früher oder später nicht darum herumkommen, neue Menüpunkte und Dialoge einzubauen. Wir haben uns deshalb dazu entschlossen, die RSC-Datei mit auf die Diskette zum Heft zu packen, da größere Modifikationen in der RSH-Datei fast unmöglich sind. Außerdem haben wir das Programm im Monochrommodus entwickelt, weshalb die Dialogboxen in einer anderen Auflösung etwas „durcheinandergewürfelt“ aussehen. Dies war ein weiterer Anlaß, die Resource-Datei für eigene Änderungen beizufügen.

Erwähnenswert ist an dieser Stelle noch das vertikale Scrolling in den Fenstern. Anstatt jedes Mal den gesamten Fensterinhalt mit v_gtext() neu auszugeben (was viele Programme tun), kopieren wir stattdessen mit Hilfe von vro_cpyfm() soviel wie möglich und geben nur unbedingt notwendige Zeichen mit v_gtext() aus. Im Gegensatz dazu haben wir beim horizontalen Scrolling nicht solchen Aufwand getrieben, da dieser Fall relativ selten vorkommt und nur von Hand im aktiven Fenster ausgelöst werden kann. Dort ist jedoch die weiter oben beschriebene schnelle Textausgabe mit Abschalten des Clippings möglich.

Im Modul WINDOW.C befindet sich die Behandlung der fensterbezogenen Ereignisse. Wer den ersten Teil dieses Artikels aufmerksam gelesen hat, sollte keine Probleme haben, die Funktionsweise der einzelnen Funktionen zu durchschauen.

Das Modul CONFIG.C enthält schließlich die komplette Dialogbehandlung zur Konfiguration der Schnittstellen und benutzt nur hinreichend oft beschriebene Routinen zur Dialogbehandlung, auf die wir hier nicht zum x-ten Male eingehen wollen.

Make-Up

Nachdem wir die Funktionsweise des Programms in groben Zügen besprochen haben, kommen wir noch zu dessen Bedienung. Nach Programmstart erscheint auf dem Desktop für jede Schnittstelle ein Icon. Wird ein Icon einfach angeklickt, ist es selektiert und im Menü kann unter „Optionen/Parameter...“ die Schnittstelle konfiguriert werden. Ist kein Icon selektiert, bezieht sich der Parameter-Dialog (siehe Abbildung 1) auf das jeweils aktive Fenster (sofern vorhanden).

Bei einem Doppelklick auf ein Icon wird das zur Schnittstelle gehörige Fenster geöffnet und das entsprechende virtuelle Terminal gelöscht. Alternativ läßt sich ein Fenster wie im Desktop auch dadurch öffnen, daß ein Icon selektiert und anschließend der Menüpunkt „Datei/Öffnen“ angewählt wird. Ist das angesprochene Fenster bereits geöffnet, wird es bei der Anwahl des Icons lediglich in den Vordergrund gebracht. Ein Beispiel mit allen vier geöffneten Fenstern und dem unvermeidlichen Info-Dialog zeigt Abbildung 2.

Damit nach einem Programmstart die gewohnte Arbeitsumgebung wiederhergestellt wird, können mit den Menüpunkten „Optionen/INF-Datei laden...“ bzw. „Optionen/INF-Datei sichern...“ alle augenblicklich eingestellten Parameter geladen bzw. gespeichert werden. Betroffen sind davon die Einstellungen im Parameter-Dialog und die Fensterpositionen. Nach einem Programmstart wird die INF-Datei mit dem Namen „TT44TT.INF“ (sofern vorhanden) automatisch eingeladen.

The Making of...

Mit Hilfe der abgedruckten Projektdatei läßt sich aus den Quelltexten unter Turbo-C problemlos das lauffähige Programm erzeugen. Voraussetzung ist natürlich, daß Sie alle Listings dieser Serie fehlerfrei abgetippt haben oder sich im Besitz der Diskette zum Heft befinden.

Damit wollen wir nun diesen Artikel (und damit vorläufig auch diese Serie) beenden. Wir hoffen, daß wir damit vielen Programmierern weitergeholfen und die (neuen) Schnittstellen des Mega STE/TT etwas transparenter gemacht haben.

Oliver Scholz & Uwe Hax

Literatur:

[1] WINX - Viele Fenster zum Hof, ST-Computer 9/91, S. 195

[2] Dieter & Jürgen Geiß: Vom Anfänger zum GEM-Profi, Hüthig Verlag

Korrektur

Leider sind uns im zweiten Teil unserer Schnittstellenserie Fehler im Listing OUTPUT.C unterlaufen. Die Zeile 157 ist folgendermaßen zu ändern:

CHAR nonprintable[4];

Nach Zeile 165 ist folgende Zeile einzufügen:

nonprintable[3]=EOS;
/*
 * TT44TT.PRJ
 * Projektdatei für Turbo-C
 * Copyright (c) 1991 by MAXON Computer
 * Autoren Oliver Scholz & Uwe Hax 
 */

TT44TT.PRG      ;Name des erzeugten Programms

=

TCSTART.O       ;Startup-Code

TT44TT.C        ;die vier Module
WINDOW.C 
CONFIG.C 
OUTPUT.C

TCSTDLIB.LIB    ;Standard-Bibliothek
TCTOSLIB.LIB    ;TOS-Bibliothek
TCGEMLIB.LIB    ;AES- und VDI-Bibliothek

/*
 * PROTO.H
 * Prototyp-Header für TT44TT
 * Copyright (c) 1991 by MAXON Computer
 * Autoren Oliver Scholz & Uwe Hax 
 */

VOID clipping(GRECT *rect,WORD mode);
VOID do_output(WORD wind_index);
VOID init_terminal (WORD wind_index);
VOID handle_escape(WORD wind_index,CHAR c);
VOID insert_char(WORD wind_index, CHAR c);
VOID print_char(WORD wind_index, CHAR c);
VOID term_lf(WORD wind_index);
VOID scroll (WORD wind_index, WORD direction);
VOID pos_slider(WORD wind_index, WORD vh_flag); 
WORD rc_intersect(GRECT *r1,GRECT *r2);
VOID cursor(WORD wind_index, WORD flag);
VOID init_dial(VOID);
VOID into_dial(CONF_RS *port, OLDSET *indices); 
VOID read_port(WORD index);
VOID get_baud_string(WORD rate_index, CHAR *buf); 
VOID write_port(WORD index);
VOID main(VOID);
VOID open_vwork(WORD phys_handle);
VOID get_addresses(VOID);
WORD open_window(WORD i);
VOID wind_snap(WORD *x,WORD *y,WORD *w,WORD *h);
VOID events(VOID);
WORD mn_selected(WORD *mesg_buff);
VOID wm_topped(WORD whandle);
VOID wm_redraw(WORD whandle, WORD x,WORD y,WORD w,WORD h);
VOID wm_moved(WORD *mesg_buff);
VOID wm_fulled(WORD *mesg_buff);
VOID wm_arrowed(WORD *mesg_buff);
VOID wm_vslid(WORD *mesg_buff);
VOID wm_hslid(WORD *mesg_buff);
VOID wm_closed(WORD whandle);
VOID wm_sized(WORD *mesg_buff),
VOID wind_max(WORD *x,WORD *y,WORD *w,WORD *h);
VOID clipping(GRECT *rect,WORD mode),
WORD rc_intersect(GRECT *r1,GRECT *r2);
VOID show_info(VOID);
VOID ienable(WORD flag);
VOID draw_icon(WORD iconidx);
WORD fileselect(CHAR *inpath, CHAR *insel, WORD *exbutton, CHAR *label); 
VOID loadinf(CHAR *pfad,WORD init);
VOID saveinf(CHAR *pfad);
VOID get_tos_version(VOID);
VOID wind_info(WORD device);
VOID scroll(WORD wind_index, WORD direction); 
VOID get_baud_string(WORD rate_index, CHAR *buf);
VOID size_slider(WORD wind_handle),
VOID init_ports(CONF_RS port[4]);
WORD conf_port(CONF_RS *port);
VOID pos_slider(WORD wind_index, WORD vh_flag); 
VOID do_output(WORD wind_index);
VOID init_terminal(WORD wind_index);
VOID read_port(WORD device);
VOID write_port(WORD device);
WORD get_index (WORD whandle)
VOID adjust(WORD whandle);
VOID update_cursor(WORD wind_index);
VOID update_pos(WORD wind_index);
LONG _bconmap(WORD devno);
OBJECT *get_traddr(WORD tree_index);
/*
 * TERMDEFS.H
 * Definitionen für TT44TT
 * Copyright (c) 1991 by MAXON Computer
 * Autoren: Oliver Scholz & Uwe Hax 
 */

/* Alarmboxen */

#defme APP_NOT_STARTED  "[3][Applikation konnte nicht|initialisiert werden!][ Ok ]" 
#define NO_WINDOW       "[1][Es konnte kein weiteres|Fenster mehr geöffnet werden!][ Ok ]" 
#define INF_WRERR       "[1][Kann INF-Datei nicht schreiben][OK]" 
#define INF_RDERR       "[1][Kann INF-Datei nicht lesen!][OK]"

/* allgemeine Definitionen */

#define TRUE        1
#define FALSE       0
#define DESKTOP     0
#define EOS         0
#define HORIZONTAL  1
#define VERTICAL    0
#define CURSOR_OFF  0
#define CURSOR_ON   1
#define SCROLL_UP   0
#define SCROLL_DOWN 1

#define CHAR        char
#define _sysbase    0x4F2L

/* Terminal-Definitionen */

#define TERM_WIDTH  80
#define TERM_HEIGHT 25
#define WAITING     0

/* ASCII Codes */

#define BELL        7
#define BACKSPACE   8
#define TAB         9
#define LF          10
#define CR          13
#define ESCAPE      27
#define DELETE      127

/* Handshakearten für rsconf() */

#define P_NONE      0 
#define P_XON       1 
#define P_RTS       2

/* Default UCR: 8N1 */

#define DEFUCR      0x88

/* verschiedene Makros */

#define show_mouse() graf_mouse(M_ON,0L) 
#define hide_mouse() graf_mouse(M_OFF,0L) 
#define min(a,b)     ((a)<(b) ? (a) (b))
#define max(a,b)     ((a)>(b) ? (a) (b))

/* Typdefinitionen */

typedef struct {    /* Einstellung eines Ports */
    int baudrate, 
    int flowctrl, 
    int ucr;
} CONF_RS;

typedef struct {    /* alte Porteinstellung */
    int iflow; 
    int idata; 
    int istop; 
    int ipar;
} OLDSET;

typedef struct {    /* Virtuelles Terminal */
        CHAR screen[TERM_HEIGHT][TERM_WIDTH+1];
        WORD x,y;
        WORD escape;
        BYTE auxiliary;
        WORD tmp_x,tmp_y;
} TERMINAL;

typedef struct {    /* Fensterinformationen */
    WORD handle;
    CHAR title[TERM_WIDTH];
    CHAR info[TERM_WIDTH];
    WORD x, y, w, h;
    WORD fulled,
    WORD x_corner,y_corner;
} WINDOW;

/*
 * TT44TT.H
 * Resource-Header für TT44TT
 * Copyright (c) 1991 by MAXON Computer
 * Autoren: Oliver Scholz & Uwe Hax
 */

#define PORTS 0         /* TREE */
#define ABOUTBOX 1      /* TREE */
#define NEWDESK 2       /* TREE */
#define MENU 3          /* TREE */
#define RTSCTS 5        /* OBJECT in TREE #0 */
#define NOPROT 6        /* OBJECT in TREE #0 */
#define XONXOFF 7       /* OBJECT in TREE #0 */
#define PARO 11         /* OBJECT in TREE #0 */
#define PARN 12         /* OBJECT in TREE #0 */
#define PARE 13         /* OBJECT in TREE #0 */
#define BAUDRATE 19     /* OBJECT in TREE #0 */
#define BAUDUP 20       /* OBJECT in TREE #0 */
#define BAUDDWN 21      /* OBJECT in TREE #0 */
#define BITS8 24        /* OBJECT in TREE #0 */
#define BITS7 26        /* OBJECT in TREE #0 */
#define PORTABRT 29     /* OBJECT in TREE #0 */
#define STOP2 33        /* OBJECT in TREE #0 */
#define STOP1 35        /* OBJECT in TREE #0 */
#define PORTOK 37       /* OBJECT in TREE #0 */
#define ABOUTOK 9       /* OBJECT in TREE #1 */
#define MFP2 1          /* OBJECT in TREE #2 */
#define SCC1 2          /* OBJECT in TREE #2 */
#define SCC2 3          /* OBJECT in TREE #2 */
#define MFP1 4          /* OBJECT in TREE #2 */
#define ABOUT 8         /* OBJECT in TREE #3 */
#define OPEN 17         /* OBJECT in TREE #3 */
#define CLOSE 18        /* OBJECT in TREE #3 */
#define QUIT 20         /* OBJECT in TREE #3 */
#define PORT 22         /* OBJECT in TREE #3 */
#define ZOOM 24         /* OBJECT in TREE #3 */
#define LOADINF 26      /* OBJECT in TREE #3 */
#define SAVEINF 27      /* OBJECT in TREE #3 */
/*
 * VARIABLE.H
 * Globale Variablen für TT44TT
 * Copyright (c) 1991 by MAXON Computer
 * Autoren: Oliver Scholz & Uwe Hax
 */

GLOBAL WORD gl_apid;
GLOBAL WORD distances[5],effects[3];
GLOBAL WORD num_aux,aux_offset;
GLOBAL WORD curr_icon=-1;
GLOBAL WORD curr_device=-1;
GLOBAL WORD top_window=-1;
GLOBAL WORD vdi_handle;
GLOBAL WORD dummy;
GLOBAL WORD zoomflag = TRUE;
GLOBAL WORD wchar,hchar,wbox,hbox;
GLOBAL WORD tos_version;
GLOBAL CHAR inf_path[128];
GLOBAL CHAR inf_name[14];
GLOBAL OBJECT *menu,*newdesk,*port_dial, *info_box;
GLOBAL CONF_RS port[4];
GLOBAL TERMINAL terminal[4];
GLOBAL WORD element s=NAME | CLOSER | FULLER | MOVER | INFO | SIZER | UPARROW | DNARROW | VSLIDE | LFARROW | RTARROW | HSLIDE; 
GLOBAL WORD iconlist[4]= { MFP1, SCC2, MFP2, SCC1 };
GLOBAL WINDOW window[4]=
{ -1," Modem 1 ","",8,32,304,192,   FALSE,0,0, 
  -1," Modem 2 ","",320,32,304,192, FALSE,0,0, 
  -1," Serial 1 ","",8,224,304,192,  FALSE,0,0, 
  -1," Serial 2 ","",320,224,304,192,FALSE,0,0,
};

/*
 * TT44TT.RSH
 * Konvertierte Resourcedatei für TT44TT
 * Copyright (c) 1991 by MAXON Computer
 * Autoren: Oliver Scholz & Uwe Hax 
 */

WORD IMAG0[] = {
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
    0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
};

WORD IMAG1[] = {
    0x0000, 0x03E0, 0x0000, 0x0C7C,
    0x0000, 0x1186, 0x0000, 0x6203,
    0x0000, 0x8401, 0x0001, 0x0841,
    0x0002, 0x10A1, 0x0004, 0x1152,
    0x0008, 0xF0A2, 0x0011, 0xD844,
    0x0023, 0xCC08, 0x0046, 0xA610,
    0x008F, 0x73E0, 0x011A, 0xAB00,
    0x013D, 0xDC00, 0x022A, 0xA800,
    0x0477, 0x7000, 0x04AA, 0xA000,
    0x091D, 0xC000, 0x110A, 0x8000,
    0x1207, 0x0000, 0x23E2, 0x0000,
    0x2412, 0x0000, 0x490C, 0x0000,
    0x5284, 0x0000, 0x6544, 0x0000,
    0x4AA4, 0x0000, 0x4548, 0x0000,
    0x2290, 0x0000, 0x2120, 0x0000,
    0x1040, 0x0000, 0x0F80, 0x0000
};

ICONBLK rs_iconblk[] = {
    IMAG0, IMAG1, "", 4096,6,23, 
    0,0,32,32,16,32,0,7,
    IMAG0, IMAG1, "SERIAL 1", 4096,6,23,
    12,0,32,32, 0,32,57,7,
    IMAG0, IMAG1, "SERIAL 2", 4096,6,23,
    12,0,32,32, 0,32,57,7,
    IMAG0, IMAG1, "MODEM 2", 4096,6,23,
    12,0,32,32, 0,32,58,7,
    IMAG0, IMAG1, "MODEM 1", 4096,6,23, 
    12,0,32,32, 0,32,58,7
};

TEDINFO rs_tedinfo[] = {
    "Protokoll", "", "", 3, 6, 0, 0x1180,
    0x0, -1, 10,1,
    "Parität", "", "", 3, 6, 0, 0x1180,
    0x0, -1, 8,1,
    "Port konfigurieren", "", "", 3, 6, 2, 0x1071,
    0x0, -1, 19,1,
    "19200", "", "", 3, 6, 2, 0x1180,
    0x0, -1, 6,1,
    "Baudrate", "", "", 3, 6, 0, 0x1180,
    0x0, -1, 9,1,
    "Datenbits”, "", "", 3, 6, 0, 0x1180,
    0x0, -1, 10,1,
    "TEXT", "", "", 3, 6, 0, 0x1180,
    0x0, -1, 5,1,
    "2 Bits", "", "", 3, 6, 0, 0x1180,
    0x0, -1, 7,1,
    "1 Bit", "", "", 3, 6, 0, 0x1180,
    0x0, -1, 6,1,
    "Stopbits", "", "", 3, 6, 0, 0x1180,
    0x0, -1, 9,1,
    "TT44TT", "", "", 3, 6, 2, 0x1071,
    0x0, 255, 7,1,
    "Terminal Times Four For TT's", "", "",
    5, 6, 2, 0x1180, 0x0, 255, 29,1,
    "(C) 1991 by Oliver Scholz & Uwe Hax", "", "",
    5, 6, 2, 0x1180, 0x0, 255, 36,1
};

OBJECT rs_object[] = {
    -1, 1, 37, G_BOX, NONE, OUTLINED,
    0x21100L, 0,0, 1325,2064,
    8, 2, 7, G_BOX, NONE, NORMAL,
    0xFF1100L, 2,2563, 1549,773,
    3, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"keines", 1028,3840, 6,1,
    4, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"RTS/CTS", 1028,1538, 7,1,
    5, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"XON/XOFF", 772,3075, 8,1,
    6, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF1170L, 1537,2306, 257,2304,
    7, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF1170L, 1537,769, 257,2304,
    1, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF117OL, 1537,3843, 257,2304,
    15, 9, 14, G_BOX, NONE, NORMAL,
    0xFF1100L, 2,3849, 1549,773,
    10, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"keine", 1284,257, 6,1,
    11, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"ungerade", 772,2819, 8,1,
    12, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF1170L, 1537,4, 257,2304,
    13, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF1170L, 1537,1025, 257,2304,
    14, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF1170L, 1537,2562, 257,2304,
    8, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"gerade", 772,1538, 6,1,
    16, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[0], 1027,259, 9,1,
    17, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[1], 1027,1545, 7,1,
    18, -1, -1, G_BOXTEXT, NONE, OUTLINED,
    (LONG)&rs_tedinfo[2], 773,1, 34,1,
    22, 19, 21, G_BOX, NONE, NORMAL,
    0xFF1100L, 785,3849, 1817,2562,
    20, -1, -1, G_BOXTEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[3], 520,3840, 8,1,
    21, -1, -1, G_BOXCHAR, TOUCHEXIT, NORMAL, 
    0x3FF1100L, 1552,3840, 1026,1,
    18, -1, -1, G_BOXCHAR, TOUCHEXIT, NORMAL, 
    0x4FF1100L, 517,3840, 1026,1,
    27, 23, 26, G_BOX, NONE, CROSSED,
    0xFF1101L, 1041,2563, 1548,3075,
    24, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)”8 Bits", 1028,1538, 6,1,
    25, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF1170L, 1793,2306, 257,2304,
    26, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"7 Bits", 1028,3840, 6,1,
    22, -1, -1, G_BOX, 0x51, OUTLINED,
    0xFF1170L, 1793,513, 257,2304,
    28, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[4], 19,1801, 8,1,
    29, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG) &rs_tedinfo[5] , 275,3, 9,1,
    30, -1, -1, G_BUTTON, 0x5, NORMAL,
    (LONG)"Abbruch", 1558,270, 9,1,
    31, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[6] , 1303,1540, 512,512,
    36, 32, 35, G_BOX, NONE, NORMAL,
    0xFF1100L, 1567,2563, 1291,3075,
    33, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[7], 260,1282, 6,1,
    34, -1, -1, GJ30X, 0x51, OUTLINED, 
    0xFF1170L, 1281,2306, 257,2304,
    35, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[8], 260,3840, 5,1,
    31, -1, -1, G_BOX, 0x51, OUTLINED, 
    0xFF1170L, 1281,513, 257,2304,
    37, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[9], 801,259, 8,1,
    0, -1, -1, G_BUTTON, 0x27, NORMAL,
    (LONG)"Ok", 1569,526, 265,3840,
    -1, 1, 9, G_BOX, NONE, OUTLINED,
    0x21100L, 0,0, 1827,3340,
    2, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"Das erste Terminalprogramm", 1284,2052, 26,1,
    3, -1, -1, G_BOXTEXT, NONE, OUTLINED,
    (LONG) &rs_tedinfo [10], 1290,3584, 526,1,
    4, -1, -1, G_TEXT, NONE, NORMAL, 
    (LONG)&rs_tedinfo[11], 1542,1538, 791,3584,
    5, -1, -1, G_ICON, SELECTABLE, NORMAL, 
    (LONG)&rs_iconblk[0], 1026,3328, 4,1794,
    6, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"für den Atari TT und Mega STE", 515,2309, 29,1,
    7, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"mit bis zu 4 Schnittstellen ", 260,2566, 28,1,
    8, -1, -1, G_IBOX, NONE, OUTLINED,
    0x11100L, 2,3587, 288,2564,
    9, -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&rs_tedinfo[12], 1284,1289, 283,3072, 
    0, -1, -1, G_BUTTON, 0x27, NORMAL,
    (LONG)"OK", 1292,3594, 1289,257,
    -1, 1, 4, G_BOX, NONE, NORMAL,
    0x1141L, 0,0, 45,20,
    2, -1, -1, G_ICON, SELECTABLE, NORMAL, 
    (LONG)&rs_iconblk[1], 2,10, 263,1794,
    3, -1, -1, G_ICON, SELECTABLE, NORMAL, 
    (LONG)&rs_iconblk[2], 2,13, 263,1794,
    4, -1, -1, G_ICON, SELECTABLE, NORMAL, 
    (LONG)&rs_iconblk[3], 2,7, 519,1794,
    0, -1, -1, G_ICON, 0x21, NORMAL,
    (LONG)&rs_iconblk[4], 2,4, 519,1794,
    -1, 1, 6, G_IBOX, NONE, NORMAL,
    0x0L, 0,0, 90,25,
    6, 2, 2, G_BOX, NONE, NORMAL,
    0x1100L, 0,0, 90,513,
    1, 3, 5, G_IBOX, NONE, NORMAL,
    0x0L, 2,0, 25,769,
    4, -1, -1, G_TITLE, NONE, NORMAL,
    (LONG)" TT44TT ", 0,0, 8,769,
    5, -1, -1, G_TITLE, NONE, NORMAL,
    (LONG)" Datei ", 8,0, 7,769,
    2, -1, -1, G_TITLE, NONE, NORMAL,
    (LONG)" Optionen ", 15,0, 10,769,
    0, 7, 21, G_IBOX, NONE, NORMAL,
    0x0L, 0,769, 340,19,
    16, 8, 15, G_BOX, NONE, NORMAL,
    0xFF1100L, 2,0, 20,8,
    9, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)" Zum Programm...   ", 0,0, 20,1,
    10, -1, -1, G_STRING, NONE, DISABLED,
    (LONG)"---------------------", 0,1, 20,1,
    11, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"1", 0,2, 20,1,
    12, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"2", 0,3, 20,1,
    13, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"3", 0,4, 20,1,
    14, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"4", 0,5, 20,1,
    15, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"5", 0,6, 20,1,
    7, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"6", 0,7, 20,1,
    21, 17, 20, G_BOX, NONE, NORMAL, 
    0xFF1100L, 10,0, 18,4,
    18, -1, -1, G_STRING, NONE, NORMAL, 
    (LONG)"  Öffnen", 0,0, 18,1,
    19, -1, -1, G_STRING, NONE, NORMAL, 
    (LONG)"  Schließen", 0,1, 18,1,
    20, -1, -1, G_STRING, NONE, DISABLED,
    (LONG)"-------------------", 0,2, 18,1,
    16, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"  Ende", 0,3, 18,1,
    6, 22, 27, G_BOX, NONE, NORMAL,
    0xFF1100L, 17,0, 23,6,
    23, -1, -1, G_STRING, NONE, NORMAL, 
    (LONG)"  Parameter...", 0,0, 23,1,
    24, -1, -1, G_STRING, NONE, DISABLED,
    (LONG)"-----------------------", 0, 1,23,1,
    25, -1, -1, G_STRING, NONE, NORMAL, 
    (LONG)"  Zoomboxen”, 0,2, 23,1,
    26, -1, -1, G_STRING, NONE, DISABLED,
    (LONG)"------------------------",0,3,23,1,
    27, -1, -1, G_STRING, NONE, NORMAL, 
    (LONG)" INF-Datei laden...", 0,4, 23,1,
    21, -1, -1, G_STRING, LASTOB, NORMAL, 
    (LONG)" INF-Datei sichern...",0,5,23,1
} ;

#define NUM_OBS 81 
#define NUM_TREE 4


Aus: ST-Computer 12 / 1991, Seite 124

Links

Copyright-Bestimmungen: siehe Über diese Seite