Um festzustellen, ob eine Applikation GEMScript unterstützt, schickt man ihr folgende Message.
GS_REQUEST msg[0] 0x1350 (4944) msg[1] ap_id msg[2] 0 msg[3] + Pointer auf GS_INFO-Struktur msg[4] msg[5] 0 msg[6] 0 msg[7] beliebige ID (außer -1)
Als ID darf jede beliebige Nummer, außer -1 gewählt werden. ID -1 ist die "ungültige ID". Das macht es für den Programmierer leichter, da er die ungültige ID -1 intern z.B. für nicht aktive GEMScript-Verbindungen benutzen kann und kein extra Flag braucht (falls er sowas überhaupt benötigt).
Antwort:
Wenn die Applikation GEMScript versteht, erhält man als Antwort GS_REPLY.
Man muß jede Applikation natürlich nur einmal vor dem ersten Kommando auf GEMScript testen.
GS_REPLY wird von einer GEMScript-fähigen Applikation als Antwort auf GS_REQUEST verschickt.
GS_REPLY msg[0] 0x1351 (4945) msg[1] ap_id msg[2] 0 msg[3] + Pointer auf GS_INFO-Struktur msg[4] msg[5] 0 msg[6] 0: OK, Applikation kann GS-Kommunikation durchführen 2: "Bitte andere ID" sonst: Fehler, GS-Kommunikation derzeit nicht möglich msg[7] ID aus GS_REQUEST
Wenn eine Applikation in msg[6] einen Fehler meldet, dürfen an sie keine weiteren GS-Nachrichten geschickt werden. Man kann aber zu einem späteren Zeitpunkt mit GS_REQUEST erneut die GEMScript-Fähigkeit erfragen.
Man beachte, daß eine Applikation mehrere GS_REQUEST-Nachrichten erhalten kann. Zum einen können verschiedene Applikationen eine Kommunikation beginnen, aber durch die ID kann eine Applikation sogar in mehreren "Sessions" gleichzeitig kommunizieren.
Ist eine Applikation nur auf eine laufende Kommunikation gleichzeitig ausgelegt (z.B. weil der aktuelle Kommunikationspartner in einer globalen Variablen vermerkt wird), so muß darauf geachtet werden, daß alle folgenden GS_REQUEST-Nachrichten korrekt abgelehnt werden, wenn bereits eine Kommunikation läuft. (Am besten mit dem Wert 1 in msg[6].)
Anwortet die Applikation in msg[6] mit 2, so bittet sie um eine andere Session ID. Man wählt also eine andere Session-ID und versucht erneut ein GS_REQUEST. Der Grund: Schicken sich zwei Applikationen gegenseitig zeitgleich ein GS_REQUEST mit gleicher Session ID (äußerst unwahrscheinlich, aber möglich), so wären nachfolgende Kommandos den Sessions nicht eindeutig zuzuordnen. Eine Applikation, die gerade ein GS_REQUEST abgeschickt hat und (während sie eigentlich auf GS_REPLY wartet) ebenfalls ein GS_REQUEST derselben Partner-Applikation mit gleicher Session-ID bekommt, muß dieses GS_REQUEST mit msg[6] = 2 ablehnen.
typedef struct { long len; /* Länge der Struktur in Bytes */ int version; /* Versionsnummer des Protokolles beim Sender (z.Z. 0x0120 = 1.2) */ int msgs; /* Bitmap der unterstützten Nachrichten und Fähigkeiten (GSM_xxx) */ long ext; /* benutzte Endung, etwa '.SIC' */ } GS_INFO; GSM_COMMAND = 0x0001 /* kann GS_COMMAND empfangen */ GSM_MACRO = 0x0002 /* kann GS_OPENMACRO, GS_WRITE und GS_CLOSEMACRO empfangen, GS_MACRO verschicken (Interpreter) */ GSM_WRITE = 0x0004 /* kann GS_OPENMACRO, GS_WRITE und GS_CLOSEMACRO verschicken, GS_MACRO empfangen (aufnahmefähige Applikation) */ GSM_HEXCODING = 0x0008 /* Hex-Codierung wird verstanden */
Anmerkungen zur GS_INFO-Struktur:
GS_QUIT sollte an den Kommunikationspartner geschickt werden, wenn eine fernsteuernde Applikation keine GS_COMMAND-Befehle mehr verschickt oder wenn eine ferngesteuerte Applikation solche Nachrichten nicht mehr auswerten kann/möchte (z.B. weil die Applikation terminiert).
GS_QUIT msg[0] 0x1354 (4948) msg[1] ap_id msg[2] 0 msg[3] 0 msg[4] 0 msg[5] 0 msg[6] 0 msg[7] ID aus GS_REQUEST
GS_COMMAND msg[0] 0x1352 (4946) msg[1] ap_id msg[2] 0 msg[3] + Pointer auf Kommandozeile, s.u. msg[4] msg[5] 0 msg[6] 0 msg[7] ID aus GS_REQUEST
Die Kommandozeile enthält das eigentliche Kommando, gefolgt von optionalen Parametern. Kommando und Parameter sind durch ASCII #0 getrennt, am Ende der Kommandozeile steht ASCII #0#0, in C-Notation also z.B.
"Kommando\0Parameter 1\0Parameter 2\0\0"
Die Kommandos müssen beim Empfänger ohne Beachtung der Groß-/Kleinschreibung ausgewertet werden. Dabei wäre es schön, wenn möglichst viele Standard-GS-Kommandos unterstützt würden.
Siehe auch: Parameter.
Die Zeichenkette muß per Mxalloc() im globalen Speicher alloziert sein.
Antwort:
Als Antwort erhält man die Nachricht GS_ACK, die zum Freigeben dieses Speichers benutzt werden kann.
Folgende Nachricht wird von einer GEMScript-fähigen Applikation als Antwort auf GS_COMMAND verschickt. Die fernsteuernde Applikation kann beim Empfang dieser Nachricht z.B. den Speicher vom msg[3/4] wieder freigeben.
GS_ACK msg[0] 0x1353 (4947) msg[1] ap_id msg[2] 0 msg[3] + exakt die Werte der empfangenen GS_COMMAND-Nachricht msg[4] msg[5] + Ergebnis bzw. Fehlermeldung als ASCIIZZ-Text (s.u.) oder NULL msg[6] msg[7] 0: (GSACK_OK) OK, Kommando wurde oder wird ausgeführt 1: (GSACK_UNKNOWN) Kommando unbekannt 2: (GSACK_ERROR) Fehler (Kommando nicht ausgeführt)
Wird in msg[5/6] eine Rückgabe geliefert, liegt diese im Format wie die GS_COMMAND-Kommandozeile vor, also die einzelnen Werte durch ASCII #0 getrennt mit ASCII #0#0 am Ende des Rückgabestrings.
Antwort:
Wenn die auswertende Applikation in msg[5/6] ein Ergebnis der Funktion oder eine Fehlerbeschreibung liefert (also einen Wert ungleich NULL), muß die fernsteuernde Applikation folgende Antwort zurückschicken. Die auswertende Applikation kann dann ihrerseits den Ergebnisspeicher freigeben.
GS_ACK msg[0] 0x1353 (4947) msg[1] ap_id msg[2] 0 msg[3] 0 msg[4] 0 msg[5] + exakt die Werte der empfangenen GS_ACK-Nachricht msg[6] msg[7] 0
Anmerkungen zum Rückgabestring:
Der Rückgabewert eines Kommandos sollte immer über msg[5]+msg[6] zurückgegeben werden, man sollte nicht msg[7] dafür "mißbrauchen". Dies gilt vor allem für Kommandos, die wahr oder falsch zurückliefern. Sofern das Kommando korrekt ausgeführt werden konnte, sollte man bei "wahr" einen beliebigen nicht-leeren Rückgabestring (z.B. "1") zurückliefern, bei "false" empfiehlt sich Leerstring oder Nullpointer.
Dies ist z.B. für den Interpreter "Scripter" von Holger sehr praktisch, denn dann können True/False-Kommandos bequem durch folgendes Script abgefragt werden:
if (kommando(...)) { /* wahr */ } else { /* falsch */ }
Würde das Ergebnis ist msg[7] zurückgeliefert, müßte man im Beispiel "Scripter" folgendes schreiben:
kommando(...) if (errno == 0) { /* wahr */ } else { /* falsch */ }
GS_OPENMACRO (App->Interpreter) msg[0] 0x1355 (4949) msg[1] ap_id msg[2] 0 msg[3] + Pointer auf Dateinamen, unter dem das Script gespeichert werden soll msg[4] msg[5] 0 msg[6] 0 msg[7] 0
Eine Applikation will vom Script-Interpreter aufgenommen werden. Als Antwort bekommt sie GS_MACRO.
Der Interpreter sollte über die Environment-Variable GEMSCRIPT gesucht und ggf. auch nachgestartet werden.
Die Datei-Endung für Scripte kann die Applikation vom Interpreter über die GS_INFO-Struktur bei GS_REPLY erfahren. Diese Endung wird evtl. für den Fileselector benötigt, den man in den meisten Fällen vor einer Aufnahme aufrufen wird.
Zusammenfassung des Ablaufs einer Aufnahme:
GS_MACRO (Interpreter->App) msg[0] 0x1356 (4950) msg[1] ap_id msg[2] 0 msg[3] + exakt die Werte der empfangenen GS_OPENMACRO-Nachricht oder NULL bei Aufnahme-Aufforderung msg[4] msg[5] ID (am einfachsten das Dateihandle) zur Identifizierung des Scripts msg[6] 0: Datei ist geöffnet, Aufzeichnung kann beginnen; sonst: Fehler msg[7] 0
Zu beachten: GS_MACRO kann auch ohne ein vorheriges GS_OPENMACRO auftreten. msg[3/4] ist dann NULL.
Dieser Fall soll quasi als Aufforderung zur Aufnahme betrachtet werden, wodurch z.B. eine applikationsübergreifende Aufnahme durch einen externen Aufnahme-Server möglich ist. Die Applikation wird also aufgefordert, ab jetzt alle Aktionen per GS_WRITE an den Aufnahme-Server (der das GS_MACRO gesendet hat) zu schicken.
Will oder kann die Applikation gerade nicht aufnehmen, so muß sie ein GS_CLOSEMACRO zurücksenden.
Man beachte, daß auch hier die ID -1 ("ungültige ID") nicht übergeben werden darf.
Zusammenfassung des Ablaufs einer erzwungenen Aufnahme:
GS_WRITE (App->Interpreter) msg[0] 0x1357 (4951) msg[1] ap_id msg[2] 0 msg[3] + Pointer auf Kommandozeile (wie bei GS_COMMAND) msg[4] msg[5] ID aus GS_MACRO msg[6] 0 msg[7] 0
Der Interpreter antwortet auf diese Nachricht wie bei GS_COMMAND mit GS_ACK, ohne allerdings in msg[5/6] ein Ergebnis zurückzuliefern.
Falls in diesem GS_ACK ein Fehler signalisiert wird, sollten keine weiteren GS_WRITE-Nachrichten verschickt werden.
GS_CLOSEMACRO (App->Interpreter / Interpreter->App) msg[0] 0x1358 (4952) msg[1] ap_id msg[2] 0 msg[3] 0 msg[4] 0 msg[5] ID aus GS_MACRO msg[6] 0 msg[7] 0
Beendet die Aufzeichnung eines Scripts.
Zu beachten: Die Aufnahme kann von beiden Seiten beendet werden. Sowohl von der aufnehmenden Applikation selbst, als auch vom Interpreter (Aufnahme-Server bei applikationsübergreifender Aufnahme).