Pipes, die letzte Grenze

Tom Hudson, der Programmierer so bekannter Programme wie »DEGAS Elite« oder »CAD-3D«, er gründet, wie man die Fähigkeiten von GEM in eigenen Anwendungen besser nutzt.

Bis vor kurzem hatte ich eigentlich nicht recht viel mit Accessories im Sinn. Wie die meisten Leute sah ich in ihnen lediglich kleine Programme, die es erlauben, die Farbe des Desktop zu ändern, den Drucker zu installieren, die Uhr zu stellen oder andere einfache Aufgaben zu übernehmen.

Während ich im Sommer 1986 »DEGAS Elite« entwickelte, studierte ich das Handbuch des AES (Application Environment Services) und entdeckte dabei einige sehr interessante Funktionen, die ich bis zu. diesem Zeitpunkt noch nie benutzt hatte.

Die erste Funktion, appl_find(), erlaubt es einer GEM-Anwendung (entweder einem Programm oder einem Accessory) eine andere GEM-Anwendung zu orten und deren ap_id (»application identifier«) zu ermitteln. Dieser Identifier ist eine Zahl, die GEM an jedes Programm und Accessory, das sich gerade im Speicher befindet, einmal vergibt. »Das ist ja ganz nett«, stellte ich skeptisch fest, »aber was nutzt das?« Ich las es gleich: Eine Anwendung muß die ap_id einer weiteren Anwendung kennen, bevor sie mit ihr kommunizieren kann! Was soll das? Bedeutet das, daß ein Programm mit jedem anderen sprechen kann?

Meine Frage wurde fast augenblicklich beantwortet, als ich mich mit den beiden anderen Kommunikations-Funktionen, appl_read() und appl_write(), auseinandersetzte. Diese beiden Funktionen haben sich als wahre Goldminen erwiesen, und ich zeige im folgenden, wie man seine Programmfunktionen erweitert, ohne dabei das Hauptprogramm zu ändern.

Im Kern verfügt GEM über ein einfaches Nachrichtensystem, die »Message Pipe«. Wer bereits eine GEM-Anwendung geschrieben hat, ist mit diesem System vertraut. Bei den Funktionen evnt_mesag() oder evnt_multi() wartet das Programm, daß ihm GEM eine Nachricht sendet, wenn der Benutzer eine Aktion ausgelöst hat, auf die es reagieren muß. GEM verfügt dabei über einen Satz vordefinierter Nachrichten für Funktionen wie Menüpunktauswahl und so weiter. Jede dieser Nachrichten ist standardmäßig 16 Byte lang.

Das Interessante an dieser Sache ist nun, daß eigene Programme leicht Nachrichten an andere im System befindliche Anwendungen senden können, ja sogar an sich selbst, indem sie sich als GEM-Event-Manager ausgeben. Standardnachrichten sind 16 Byte lang, aber wenn nötig, dürfen vom Anwender entworfene Nachrichten auch länger sein. Dieser Vorgang ist wirklich sehr einfach, aber es gibt ein paar Richtlinien, die man befolgen und einige Probleme, auf die man achten sollte. Setzt man sie richtig ein, so ist eine Message Pipe ein zuverlässiger Weg, Informationen zwischen Programmen auszutauschen.

Standardmäßig ist eine GEM-Nachricht ein 16 Byte langer Datenblock, der wie folgt in acht Worten organisiert ist:

WORT 0:	Nachrichten-Nummer
WORT 1:	ap_id des Senders
WORT 2:	zusätzliche Bytes der Nachricht, wenn mehr 
        als 16 Byte benötigt werden
WORT 3:	Nachricht
WORT 4:	Nachricht
WORT 5:	Nachricht
WORT 6:	Nachricht
WORT 7:	Nachricht

Wort 0, die Nachrichten-Nummer, muß ein Wert zwischen $0000 und $FFFF sein, der im System nur einmal vorkommen darf. 0 bis 41 nutzt das GEM, daher sollte man beim Festlegen seiner Nummern darauf achten, diese nicht zu nehmen, da andernfalls die empfangende Applikation unter Umständen abstürzt.

Wort 1 ist die Identifikation der sendenden Anwendung. Es ist wichtig, hier die richtige ap_id zu plazieren. Ohne die passende ap_id an dieser Stelle weiß das empfangende Programm nicht, woher die Nachricht kommt. Sollte das Programm versuchen zu antworten, stürzt GEM unter Umständen ab. Glücklicherweise findet man die ap_id eines Programmes immer in der globalen Variablen »gl_apid«. In »C« definiert man diese Variable so:

extern int gl_apid;

Sendet man eine Nachricht, so schiebt man gl_apid in Wort 1 der Nachricht.

Wort 2 der Nachricht enthält die Anzahl der Extra-Bytes der Nachricht. Bei einer Standard-Nachricht von 16 Byte Länge ist der Wert auf 0 gesetzt, bei einer 20-Byte-Nachricht würde er 4 (20-16 = 4) betragen.

Die verbleibenden fünf Worte einer Standard-Nachricht sind für Daten verfügbar. Entwirft man Nachrichten, so lassen sich fünf Wort-große Datenblöcke für eine Standard-Nachricht aufstellen. Hat man mehr Daten, so muß man die Anzahl der Bytes in Wort 2 spezifizieren, um die Extra-Bytes mit der appl_write()-Funktion durch die Pipe zu senden. Die empfangende Applikation ist in der Lage, diese weiteren Bytes mit der app_read-Funktion zu lesen. Bei sehr langen Datenblöcken ist es vorteilhaft, Pointer auf die Daten mit einer Standard-Nachricht zu übermitteln.

Um eine Nachricht an eine andere Anwendung oder an ein anderes Accessory zu senden, muß man unbedingt die ap_id dieser Applikation kennen. Ohne die richtige ap_id des empfangenden Programms verhält es sich genauso, als würden Sie einen Brief ohne die Anschrift des Empfängers in einen Briefkasten werfen.

Die AES-Funktion appl_find() erlaubt es, die Anwendung, an die man eine Nachricht senden möchte, zu finden und die korrekte ap_id zu ermitteln.

Das Format dieser Funktion ist:

target_id = appl_find ("APPLNAME");

»APPLNAME« ist ein nullterminierender String, der den Namen der Anwendung, die man sucht, spezifiziert. »target_id« ist eine Variable, in der die ap_id dieser Anwendung abgelegt wird. Der Programmname ist acht Buchstaben und das terminierende Zeichen »0« lang und besteht einfach aus dem Hauptteil des Dateinamens. Wenn Sie nach dem Programm »RATTFINK.PRG« suchen, hat der Funktionsaufruf folgendes Aussehen:

appl_find("RATTFINK");

Ähnlich verhält es sich bei der Suche nach »CLOCKSET.ACC«:

appl_find("CLOCKSET");

Sollte der Dateiname wie bei »SHORT.PRG« kürzer als acht Buchstaben sein, so sind die fehlenden Stellen mit Leerzeichen zu füllen:

appl_find ("SHORT   ");

Die ap_id der Zielanwendung wird an target_id übergeben. Findet GEM die Anwendung nicht, übergibt es eine -1. An dieser Stelle muß ich Sie vor einem Bug warnen: Wenn die Ziel-Anwendung ein Programm ist, das als letztes lief, übergibt ein Fehler in den ROMs einen scheinbar gültigen Wert an target_id, während das Programm in Wirklichkeit nicht mehr resident im Speicher steht. Versucht nun das Programm, gewöhnlich ein Accessory, mit der Ziel-Anwendung zu kommunizieren und wartet auf eine Antwort, so wartet es sehr lang. Wir werden später sehen, wie man diese Klippe umschifft.

Kennt man einmal die ap_id der Ziel-Anwendung, so ist man bereit, mit ihr zu sprechen. Bilden Sie einfach eine Nachricht in einem Acht-Wort-Feld:

message[0] = 0x4000;
message[1] = gl_apid;
message[2] = 0;
message[3] = 1234;
message[4] = 0;
message[5] = 0;
message[6] = 0,
message[7] = 0;

In diesem Beispiel senden wir eine Nachricht mit der Nummer $4000. Die ap_id unserer Applikation (gl_apid) finden wir in message[1]. Die Nachricht ist 16 Byte lang, entspricht also der Standardlänge. Die Nachricht in diesem Fall ist ein einfacher numerischer Wert, 1234, in message[3].

Okay, wir haben nun unseren Nachrichten-Puffer gebildet, zum Senden benutzen wir appl_write(). Diese Funktion hat folgende Parameter:

success = appl_write (target_id, msg_size, msg_buff);

»target_id« ist ein Wort, das GEM die ap_id der Ziel-Applikation mitteilt, »msg_size« ist ein Wort, in dem die Anzahl der Nachrichten-Bytes steht, und »msg_buff« ist ein Pointer auf das Feld »Nachrichten-Puffer«. Die Variable »success« wird auf Null gesetzt, sollte ein Fehler eintreten, oder auf einen positiven Wert, wenn das Schreiben erfolgreich war. Nehmen wir an, daß wir obige Nachricht an das Accessory »TESTER.ACC« schicken. Folgender Code würde diese Aufgabe erledigen:

target_id = appl_find ("TESTER  ");
if(target_id >= 0)
	success = appl_write (target_id, 16, message);

Einfach genug? Sicher! Wir wissen bis jetzt aber noch nicht, wie wir TESTER.ACC dazu bewegen, nach dieser Nachricht zu sehen und sie zu quittieren. Normalerweise wartet eine GEM-Anwendung darauf, daß eine Nachricht in seine Message Pipe kommt. Dies erreichen die Funktionen evnt_mesag() und evnt_multi(). Der Einfachheit halber verwenden wir die evnt_mesag-Funktion.

Irgendwo in TESTER.ACC ist ein evnt_mesag-Aufruf, der etwa so aussehen könnte:

evnt_mesag (mess_in);

Ein inaktives Accessory empfängt nur eine von zwei möglichen Nachrichten des GEM-Event-Manager: 40 (AC_OPEN) oder 41 (AC_CLOSE). Das Accessory erkennt unsere maßgeschneiderte Nachricht nur dann, wenn es die Nachrichten-Nummer der ankommenden Nachricht auf unseren besonderen Code hin überprüft. Der folgende Programmteil in TESTER.ACC ließe das Accessory auf die $4000-Nachricht reagieren:

int mess_in[8];
.
.
.
evnt_mesag (mess_in);
if (mess_in[O] = 0x4000)
{
	(WE GOT THE MESSAGE!)
	(PRINT OUT mess_in[3])
}

Der 16-Byte-Nachrichten-Puffer (mess_in) wird mit der 16-Byte-Nachricht des Senders gefüllt und ist bereit, vom Accessory genutzt zu werden.

Häufig hat man aber Anwendungen, die Nachrichten senden und dann auf eine Antwort warten. Beispielsweise könnte ein Accessory die Adresse eines Daten-Puffers innerhalb der sendenden Anwendung benötigen. Diese Nachrichtenart ist nur durch einen Dialog zu realisieren, der komplizierter ist. Wenn ich einen Dialog entwerfe, arrangiere ich es gewöhnlich so, daß Nachrichten-Nummern mit einem Low-Byte von $00 bis $7F senden und Nachrichten-Nummern mit einem Low-Byte von $80 bis $FF antworten. Beispielsweise könnte die Bitte um eine Mitteilung die Nachrichten-Nummer $4000 und die Antwort $4080 haben. Die Antwort hat immer eine um $80 höhere Nachrichten-Nummer. Dies garantiert ein einheitliches Nachrichtensystem und schützt davor, daß Programme durch verschiedene Nachrichten abstürzen.

Ändern wir nun obiges Beispiel so, daß die Anwendung um eine Information durch das Accessory bittet. Der Programmteil unserer Anwendung könnte etwa so aussehen:

/* SEND MESSAGE TO ACCESSORY */
target_id = appl_find ("TESTER  ");
if (target_id >= 0)
{
	message[O] = 0x4000;
	message[1] = gl_apid;
	message[2] = 0;
	message[3] = 1;
	message[4] = message[5] = message[6] = message[7] = 0;
	appl_write (target_id, 16, message);
	/* WAIT FOR REPLY
	do
	{
		event_mesag (message);
	} 	while message [0] != 0x4080,
}
/* REPLY RECEIVED, CONTINUE PROCESSING */

Wie man sieht, schreibt das Programm die Nachricht normal und geht dann in eine Schleife, in der es auf ein evnt_mesag mit der Nachrichten-Nummer $4080, die Antwort auf $4000, wartet. Solange das Accessory geladen ist, sollte alles perfekt funktionieren. Empfängt das Programm andere Nachrichten als $4080, so ignoriert sie diese und wartet weiter. Beachten Sie folgendes: Es ist in seltenen Fällen nötig, auf andere Nachrichten innerhalb dieser Schleife zu reagieren, wie beispielsweise auf redraw-Nachrichten. Dies ist aber gewöhnlich nicht der Fall.

Das Accessory muß, um diese Nachricht zu verarbeiten, folgende Anweisungen enthalten:

int mess_out[8];
/* WAIT FOR MESSAGE FROM THE PIPE */
msg_wait:
	evnt_mesag (mess_in);
	if (mess_in[O] = 0x4000)
	{
		mess_out[0] = 0x4080;
		mess_out[1] = gl_apid;
		mess_out[2] = 0;
		mess_out[3] = 1;
		mess_out[4] = 2;
		mess_out[5] = 3;
		mess_out[6] = 4;
		mess_out[7] = 5;
		appl_write(mess_in[1],16, mess_out);
	}
	goto msg_wait;

Empfängt das Accessory die Nachrichten-Nummer $4000, so antwortet es mit der Nummer $4080, schreibt seine ap_id in mess_out[1] und bildet den Rest der Nachricht entsprechend. Anschließend schreibt es die Antwort mit der Funktion appl_write() in die Message Pipe. Beachten Sie, daß es den Wert, den es in mess_in[1] empfangen hat, als ap_id des Empfängers verwendet. Dies ist extrem wichtig! Sollte der Sender seine ap_id nicht mit der Nachricht übermitteln, dann kann die Anwendung nicht antworten. Vergessen Sie also niemals, gl_apid im zweiten Wort des Nachrichten-Puffers zu plazieren!

Nun eine Zusammenfassung dieser Operationen:

  1. Programm # 1 ermittelt die ID des Programmes, mit dem es in Verbindung treten will.
  2. Programm # 1 bildet und sendet seine Nachricht.
  3. Programm # 1 geht in die Schleife evnt_mesag() wait.
  4. Programm # 2 empfängt die Nachricht.
  5. Programm # 2 stellt die Antwort zusammen und sendet diese zum Originalsenden
  6. Programm # 2 wartet auf neue Nachrichten.
  7. Programm # 1 empfängt die Antwort von Programm # 2 und läuft weiter.

Dies ist ein faszinierender Prozeß, der meistens sehr nützlich ist. Wir sehen gleich eine ernsthafte Anwendung dieser Technik. Bei meinem letzten Einsatz der Message Pipe bei CAD-3D V2.0 nutzt das Hauptprogramm Accessories, um zusätzliche Operationen wie Animations-Scripts, Animations-Abläufe und Grafik-Antialiasing (Glätten kantiger Linien) auszuführen. Jedes dieser Accessories ist von den anderen unabhängig. Der Kommunikationspfad kann etwas kompliziert werden, wenn er von einem Accessory zum Hauptprogramm, von dort zu einem zweiten Accessory und wieder zurück läuft.

Für mich liegt der Nutzen darin, daß man in der Lage ist, komplexere Programme aus kleineren Stücken oder Modulen zusammenzufügen. Diese Module lassen sich so entwerfen, daß sie periodisch durch neuere Versionen, die ihre Aufgabe effizienter erfüllen, ersetzt werden, ohne das Hauptprogramm dabei ändern zu müssen. Weiter können sie ein ausgedehntes Kommunikationsprotokoll im Hauptprogramm realisieren, das es erlaubt, das Programm durch ein Accessory fernzusteuern. Dieser Weg ist auf den neueren ST-Modellen praktikabler, da diese über mehr RAM verfügen.

Einige warnende Bemerkungen: Der erfolgreiche Einsatz der Message Pipe erfordert ein durchdachtes Kommunikations-Protokoll. Sie sollten die Nachricht am besten in der Standard-Länge von 16 Byte senden. Benutzen Sie Pointer, wenn Sie längere Datenblöcke übertragen müssen. Falls Ihre Anwendung nacheinander sehr schnell eine Menge Nachrichten senden muß, verwenden Sie das oben gezeigte Dialog-System, um eine Verstopfung der Pipe zu verhindern. Der Empfänger hat vielleicht eine Menge an jeder ankommenden Nachricht zu arbeiten - seien Sie also vorsichtig.

Wie ich bereits vorher bemerkt habe, kann ein Accessory eine gültige ID eines Programmes erhalten, das bereits terminierte. Geschieht dies, so wartet das Accessory ewig auf eine Antwort. Ich zeige hier eine gute Lösung dieses Dilemmas. Man sendet eine Nachricht, die nur eine Antwort erwartet, aber keine Aktivitäten auslöst.

Anschließend ruft man die Funktion evnt_multi() auf, die auf die Antwort oder einen Timer Event wartet. Setzen Sie die Zeit des Timers auf 2000 Millisekunden. Wenn Sie nun die Antwort erhalten, bevor der Timer Event eintritt, ist die Anwendung tatsächlich im Speicher, und man fährt im Programm fort. Verfahren Sie jedesmal so, wenn das Accessory aus dem DESK-Menü aufgerufen wird. Ruft eine Anwendung das Accessory auf, ist dieses Verfahren nicht nötig. Der evnt_multi-Aufruf könnte so aussehen:

check_it:
	event
= evnt_multi (Ox0030, -1, -1, -1,
	                    0, 0, 0, 0, 0,
	                    0, 0, 0, 0, 0,
	                    mgbuf,
	                    2000, 0,
	                    &dum,&dum,&dum,
	                    &dum,&dum,&dum);
	if (event & 0x0020)
		(APPLICATION NOT PRESENT)
	else
		if (mgbuf [0] = REPLY)
			(APPLICATION PRESENT)
	else
		goto Check_it;

Die Datei »DECOMM.C« auf unserer Leserservice-Diskette zu dieser Ausgabe, ist ein realistisches Beispiel einer Message Pipe. Diese Datei ist der Quell-Code für ein Accessory, das zu DEGAS Elite »spricht« und Informationen wie den gerade benutzten Screen, Blockgröße und mehr liefert. DEGAS Elite wurde so groß, daß es nicht auf 512-KByte-Systemen lief. Deshalb entschloß ich mich, das Programm durch Accessories erweiterbar zu machen. Elite unterstützt fünf Kommandos für die Message Pipe, die von $DE00 (DE - Degas Elite) bis $DE04 durchnumeriert sind. Sehen wir uns die Kommandos näher an:

$DE00 ermittelt die Adressen der Screens. Dieses Kommando fordert DEGAS Elite auf, dem Accessory die Adressen seiner Bildschirme zu übergeben. Das Nachrichten-Format:

message[0] = $DE00
message[1] = Accessory ap_id
message[2] = 0
message[3] = Ungenutzt
message[4] = Ungenutzt
message[5] = Ungenutzt
message[6] = Ungenutzt
message[7] = Ungenutzt

DEGAS Elite antwortet mit $DE80:

message[0] = $DE80
message[1] = DEGAS Elite ap_id
message[2] = 0
message[3] = Low-Wort des Pointers auf das Screen-Puffer-Pointer-Array
message[4] = High-Wort des Pointers auf das Screen-Puffer-Pointer-Array
message[5] = Anzahl der Pointers im Array
message[6] = Ungenutzt
message[7] = Ungenutzt

Diese Antwort ist schön offen. Nimmt man die Worte 3 und 4 zusammen, so bilden sie einen Zeiger auf ein Zeigerfeld, das auf die unterschiedlichen Screen-Puffer von DEGAS Elite weist. Das fünfte Wort enthält die Anzahl der in diesem Array zusammengefaßten Zeiger. Zur Zeit enthält dieses Array 13 Zeiger, die folgendermaßen angeordnet sind:

array[0] = Pointer auf den Hauptmenü-Screen (nicht verändern!)
array[1] = Pointer auf Zeichen-Screen #1
array[2] = Pointer auf Zeichen-Screen #2
array[3] = Pointer auf BlockPuffer
array[4] = Pointer auf Blockmasken-Puffer
array[5] = Pointer auf Blockarbeits-Puffer
array[6] = Pointer UndoPuffer
array[7] = Pointer auf Zeichen-Screen # 3 (0 falls nicht vorhanden)
array[8] = Zeichen-Screen #4 (0 falls nicht vorhanden)
array[9] = Zeichen-Screen #5 (0 falls nicht vorhanden)
array[10] = Zeichen-Screen #6 (0 falls nicht vorhanden)
array[11] = Zeichen-Screen #7 (0 falls nicht vorhanden)
array[12] = Zeichen-Screen #8 (0 falls nicht vorhanden)

Künftige Versionen von DEGAS könnten mehr als acht Zeichen-Screens unterstützen, so daß man sicherheitshalber immer mit message[5] die Anzahl der Zeiger ermitteln sollte. Wie bereits angemerkt, ist kein Screen-Puffer vorhanden, wenn der Screen-Pointer den Wert 0 hat. Benutzen Sie nie solche Puffer. Zwei Puffer, array[5] und array[6], können dem Accessory als »Scratch«-Puffer dienen. Das array[5] ist ein temporärer Arbeitsbereich für das Zeichnen mit einem Block-Image und in array[6] speichert Elite das Bild temporär für die UNDO-Funktion. Diese Puffer werden ausgeschaltet, wenn man zum Menü-Screen zurückkehrt, und stehen deshalb für das Accessory zur Verfügung.

Beachten Sie, daß diese Puffer nicht zum Anzeigen entworfen wurden - nutzen Sie diese deshalb ausschließlich als Datenpuffer oder Arbeitsbereich, jedoch nie als Anzeige-Screen.

$DE00 eignet sich gut, um festzustellen, ob DEGAS Elite tatsächlich läuft, damit der GEM-Fehler bei appl_find() nicht zum Tragen kommt, denn sie antwortet sofort.

Da DEGAS Elite über bis zu acht Arbeitsbildschirme verfügt, muß Ihr Accessory wissen, mit welchem der Anwender im Moment arbeitet. Das Kommando $DE01 ermittelt die Screen-Nummer und hat folgendes Format:

message[0] = $DE01
message[1] = Accessory ap_id
message[2] = 0
message[3] = Ungenutzt
message[4] = Ungenutzt
message[5] = Ungenutzt
message[6] = Ungenutzt
message[7] = Ungenutzt

DEGAS Elite antwortet mit der Nachrichten-Nummer $DE81, die folgendes Format hat:

message[0] = $DE81
message[1] = DEGAS Elite ap_id
message[2] = 0
message[3] = Screen-Puffer- Index
message[4] = Ungenutzt
message[5] = Ungenutzt
message[6] = Ungenutzt
message[7] = Ungenutzt

Screen-Puffer-Index ist ein Index für das Screen-Puffer-Array. Wenn der Index gleich 1 ist, wird Bildschirm# 1, wenn er gleich 11 ist, wird der Zeichen-Screen 7 benutzt. Ermitteln Sie erst die Puffer-Adressen mit dem $DE00-Kommando, bevor Sie das $DE01-Kommando verwenden, denn nur dann sind Sie sicher, die Puffer-Adressen zur Hand zu haben.

Das nächste Kommando erlaubt Ihnen herauszufinden, wo das aktuelle »Blockimage« gespeichert und wie groß es ist. Das Format:

message[0] = $DE02
message[1] = Accessory ap-id
message[2] = 0
message[3] = Ungenutzt
message[4] = Ungenutzt
message[5] = Ungenutzt
message[6] = Ungenutzt
message[7] = Ungenutzt

Empfängt DEGAS Elite diese Nachricht, so stellt es die Antwort $DE82 zusammen und sendet sie folgendermaßen:

message[0] = $DE82
message[1] = DEGAS Elite ap_id
message[2] = 0
message[3] = Index des Puffers, in dem sich der Block befindet
message[4] = X-Koordinate der unteren linken Blockecke
message[5] = Y-Koordinate der unteren linken Blockecke
message[6] = Blockweite in Pixel
message[7] = Blockhöhe in Pixel

In der aktuellen Version von DEGAS Elite steht der Block in Puffer 3, und die untere linke Ecke hat immer die Koordinaten 0,0. In folgenden Versionen kann dies anders sein. Wenn Sie versuchen, den Block zu ändern, vergewissern Sie sich, daß er in der richtigen Lage in den korrekten Puffer zurückgeschrieben wird.

DEGAS Elite erlaubt es den Accessories, die gegenwärtige Blockgröße zu ändern. Dies ist zum Beispiel nötig, wenn das Accessory einen Block mit anderer Größe in den Puffer lädt. Das Format dieses Befehls:

message[0] = $DE03
message[1] = Accessory ap_id
message[2] = 0
message[3] = Neue Blockweite
message[4] = Neue Blockhöhe
message[5] = Ungenutzt
message[6] = Ungenutzt
message[7] = Ungenutzt

Auf diese Nachricht sendet DEGAS Elite keine Antwort. Dennoch ist es sehr wichtig, die Blockgröße so festzulegen, daß sie zur gegenwärtigen Auflösung paßt, also größer als 0 und kleiner oder gleich der Bildschirmbreite und -höhe ist.

Wenn Sie ein Accessory schreiben, das Bilder von Diskette in einen der Puffer lädt, möchten Sie vielleicht die Farbpalette auf die entsprechenden Werte ändern. Dies erledigen Sie mit dem Kommando $DE04, das eine Nachricht, die aus 48 Byte besteht, ist. Das Format:

message[0]  = $DE04
message[1]  = Accessory ap_id
message[2]  = 32
message[3]  = Bildpalette setzen Flag
message[4]  = Ungenutzt
message[5]  = Ungenutzt
message[6]  = Ungenutzt
message[7]  = Ungenutzt
message[8]  = Palettenfarbe 0
message[9]  = Palettenfarbe 1
message[10] = Palettenfarbe 2
message[11] = Palettenfarbe 3
message[12] = Palettenfarbe 4
message[13] = Palettenfarbe 5
message[14] = Palettenfarbe 6
message[15] = Palettenfarbe 7
message[16] = Palettenfarbe 8
message[17] = Palettenfarbe 9
message[18] = Palettenfarbe 10
message[19] = Palettenfarbe 11
message[20] = Palettenfarbe 12
message[21] = Palettenfarbe 13
message[22] = Palettenfarbe 14
message[23] = Palettenfarbe 15

Die ersten acht Worte bilden eine Standard-GEM-Nachricht. Beachten Sie, daß das Wort 2 gleich 32 ist. Dies bedeutet, daß der Hauptnachricht 32 Byte (16+32=48) mit Daten folgen. Wort 3 ist ein Flag, das DEGAS Elite mitteilt, ob es die Palette auf den gerade arbeitenden Zeichen-Screen anwenden soll oder nicht. Ist das Wort gleich 0, wird die Palette geladen, aber nicht auf den Screen bezogen. Ist es ungleich Null, wendet Elite die Palette nach dem Laden auf den aktuellen Zeichen-Screen an.

Wenn Sie ein Bild mit einer neuen Palette laden wollen, setzen Sie das Flag ungleich Null, damit das Bild die festgelegten Farben hat. Wollen Sie lediglich die Farben ändern, setzen Sie das Flag gleich Null. Dies erlaubt die »Remap«-Funktion einzusetzen, um das gegenwärtige Bild der neuen Palette anzupassen.

Falls Sie dieses Kommando an DEGAS Elite senden, vergewissern Sie sich, daß Elite wirklich 48 Byte empfängt, wobei es gleichgültig ist, ob dies mit einem großen Nachrichten-Puffer durch ein appl_write oder mit einem Standard-Nachrichten-Puffer und einem Paletten-Array geschieht.

Ein großes Feld:

appl_write (DEGAS_id, 48, message);

Der normale Nachrichten-Puffer und das Paletten-Array:

appl_write (DEGAS_id, 16, message);
appl_write (DEGAS_id, 32, palette);

Wie auch immer, DEGAS Elite empfängt die 48 Byte-Nachricht richtig. Unabhängig von der Auflösung ist immer das gesamte 16-Wort-Array zu senden.

Falls ein Programm wie DEGAS Elite eine Message Pipe nutzt, sollten Sie ausschließlich die dokumentierten Kommandos benutzen, um Informationen vom Programm zu erhalten. Tricks wie das Disassemblieren des Programms, um undokumentierte Variablen oder Puffer zu finden, sind sehr unklug, denn sie bringen stets Ärger, wenn eine neue Version des Programms erscheint.

Muß ein Accessory einen Dialog anzeigen, so ist es das beste, das Accessory öffnet ein eigenes Fenster über dem Kontrollfeld von DEGAS Elite. Macht es dies nicht, bezieht GEM Mausklicks über dem Kontrollfeld auf DEGAS Elite, so daß vielleicht der Bildschirm in Unordnung gerät. Vergewissern Sie sich jedoch, daß das Accessory nach getaner Arbeit das Fenster wieder schließt. Erhält DEGAS oder das Programm, mit dem Sie gerade arbeiten, die Kontrolle zurück, so erwartet es ein Full-Screen-Redraw, um seine Bildschirme, sein Kontrollfeld und so weiter neu zu zeichnen.

Das Programm DECOMM.ACC auf unserer Leserservice-Diskette ist ein Beispiel, wie man mit DEGAS kommuniziert. Es zeigt den Gebrauch aller Message Pipe-Kommandos und den Empfang der Antworten von DEGAS. Wird es aktiviert, wenn Elite gerade läuft, schickt es einen Statusreport über DEGAS an den Drucker.

Mit der Message Pipe und einem genau definierten und gut durchdachten Protokoll können Sie Ihre Programme so schreiben, daß sie leicht zu erweitern sind. Es bedarf lediglich einer kleineren Anstrengung während des Codierens des Hauptprogramms. Ich hoffe, daß auch Sie diesen Einsatz von Accessories genauso toll finden, wie ich es tat. Accessories werden dann nie mehr bloße RAM-Fresser sein.


Tom Hudson Ulrich Hofner
Aus: ST-Magazin 06 / 1988, Seite 72

Links

Copyright-Bestimmungen: siehe Über diese Seite