CPX: Dem variablen Kontrollfeld auf der Spur, Episode 3

Nachdem wir uns in den ersten beiden Episoden mit den kompletten Grundlagen der CPX-Programmierung auseinandergesetzt haben, gibt’s dieses Mal CPX für Fortgeschrittene (CPX for Runaways). Nach der Pflicht folgt hier also die Kür. Hinzu kommt natürlich noch das in der letzten Folge versprochene „CPX-Construction-Kit“.

To beam or not to beam ... oder anders ausgedrückt: Wer will schon zu Fuß gehen, wenn er auch einen komfortablen Transporter benutzen kann (außer Dr. McCoy natürlich)? Also auf das variable Kontrollfeld bezogen: Warum sollte man sich einen eigenen Event-Handler basteln, wenn man eine komfortable do_form()-Routine zur Verfügung hat? Nun wird sich manch einer vielleicht fragen, warum das überhaupt nötig sein sollte, denn das Kontrollfeld verwaltet doch unsere GEM-Dialoge ganz angenehm. Doch genau das ist der Punkt: der zur Verfügung gestellte Dialog-Handler kann nur Standard-GEM-Dialogboxen behandeln (wogegen natürlich auch nichts einzuwenden ist). Eine Grafik, wie sie z.B. Abbildung 1 zeigt, in der verschiedene Elemente anwählbar sind, kann also auf diese Weise genau wie unter GEM nicht als Dialog benutzt werden. Und die Standard-AES-Routinen wie evnt_multi(), die eine solche Abfrage erlauben würden, dürfen wir - wie früher bereits erläutert - nicht benutzen.

Glücklicherweise stellt uns das Kontrollfeld für genau diesen Fall eine definierte Schnittstelle zur Verfügung, mit der es möglich ist, Events selbst zu behandeln. Erinnern wir uns an die ersten beiden Teile dieser Serie: Nach der Initialisierung eines CPX-Modules wird dem Kontrollfeld die Adresse einer CPX_INFO-Struktur zurückgegeben, deren Definition wir in Abb. 2 sehen.

In den Beispielen wurde bisher in dieser Struktur nur der Eintrag cpx_call benutzt. Die übrigen Einträge dienen dazu, dem variablen Kontrollfeld verschiedene Funktionen im Zusammenhang mit der Event-Behandlung bekanntzugeben. Nicht benutzte Funktionen sind, wie schon bisher, auf NULL zu setzen. Benutzte Funktionen müssen mit den in Tabelle 1 beschriebenen Übergabeparametern definiert werden, um die Events korrekt handhaben zu können; der Übersichtlichkeit wegen sind die Funktionen in der Tabelle wieder als ebensolche und nicht als Zeiger auf Funktionen aufgelistet. Außerdem müssen sie auch alle wieder als cdecl deklariert werden.

Nun zu den Funktionen im einzelnen. Die Routine, auf die cpx_call zeigt (in den bisherigen Beispielmodulen üblicherweise main genannt), muß TRUE oder FALSE zurückgeben. FALSE bedeutet, daß das CPX-Modul fertig ist und aus dem Speicher wieder entfernt werden kann. Wird dagegen TRUE zurückgegeben, werden bei Eintritt von Events die entsprechenden Funktionen in CPX_INFO angesprungen, sofern diese vorhanden sind.

Woher weiß das Kontrollfeld jedoch, auf welche Ereignisse gewartet werden soll, bzw. woher kommen für das Ereignis wichtige Parameter (z.B. die Koordinaten für ein Maus-Event)? Die Antwort liefert uns die schon früher erwähnte Funktion multi(). In ev_flags wird für jedes Maus-Event, auf das gewartet werden soll, das entsprechende Flag (z.B. MU_M1) gesetzt. Wird stattdessen -1 übergeben, werden die alten Flags weiter benutzt. Die dazugehörigen Strukturen mit den Koordinaten werden ebenfalls übergeben. Außerdem ist es noch möglich, einen Timer anzustoßen. Ein Timer-Event tritt ansonsten alle 30 Sekunden auf, damit das Kontrollfeld nicht andere Programme und Accessories blockiert.

Die Funktion cpx_evhook() wird direkt nach dem kontrollfeldinternen evnt_multi() aufgerufen und noch vor den übrigen Event-Routinen. Sie bekommt sämtliche zur Behandlung der Ereignisse benötigten Informationen zur Verfügung gestellt. Wird der (die) aufgetretene(n) Event(s) innerhalb von cpx_evhook() abgehandelt, muß TRUE zurückgegeben werden, andernfalls FALSE.

Die Funktionen cpx_timer(), cpx_key(), cpx_button(), cpx_m1() und cpx_m2() werden bei Bedarf (in dieser Reihenfolge!) aufgerufen. Aufgrund der Namen dürfte klar sein, um welche Events es sich dabei jeweils handelt. Bei den beiden Maus-Events ist zu erwähnen, daß diese auf dem ganzen Bildschirm auftreten dürfen und nicht auf den Arbeitsbereich des Kontrollfeldfensters beschränkt sind. Alle diese Funktionen können übrigens das CPX-Modul beenden, indem sie *event auf TRUE setzen.

Die Funktion cpx_draw() ist für alle Module, die einen eigenen Event-Handler installieren, vorgeschrieben, um den Dialog erstens zu zeichnen und zweitens später gegebenenfalls neu zeichnen zu können. Zu beachten ist, daß cpx_draw() nicht automatisch aufgerufen wird, wenn das Kontrollfeld eine AC_OPEN-Meldung erhält. Das ist z.B. dann der Fall, wenn das Kontrollfeld in der Menüzeile angewählt wird, obwohl es schon geöffnet ist. Dieser Fall kann nur über cpx_evhook() abgefangen werden. Da das Kontrollfeldfenster bei einer AC_OPEN-Meldung automatisch in den Vordergrund gebracht wird und man deshalb nicht am Zeichnen des Fensterinhalts vorbei kommt, ist auch diese Funktion obligatorisch.

cpx_wmove() wird aufgerufen, wenn das Fenster verschoben wurde, so daß der Dialog an die neue Bildschirmposition angepaßt werden kann.

Bleibt schließlich noch cpx_close() zu erwähnen. Diese Funktion wird bei Schließen des Kontrollfeldfensters aufgerufen, d.h. bei einer AC_CLOSE-Message. Diese tritt bei Anwahl des Schließfeldes und bei Beenden der aktuellen Applikation auf.

Aus den Geheimakten

Der Vollständigkeit halber seien hier noch die Funktionen get_rootblock() und get_resarea() aus der CPX_PARAMS-Struktur erwähnt, die wir bisher bewußt unterschlagen haben; mit diesen Funktionen kann man nämlich sehr viel Unheil anrichten. (Stellen Sie sich mal vor, ein Photonen-Torpedo hätte in Ihren nagelneuen TT eingeschlagen...)

get_rootblock() liefert die Adresse des ersten CPX_BLOCKs in einer internen Liste des Kontrollfeldes. Zu jedem CPX-Modul gehört eine solche Struktur, in der beispielsweise der Dateiname und der CPX-Header enthalten sind. Die einzelnen Strukturen sind über die Zeigervariable next miteinander verkettet; im ersten freien Eintrag in der Liste enthält filename[0] den Wert NULL. Ist die Liste voll, enthält next den Wert NULL. Bei residenten Modulen ist sogar der Zugriff auf das Text-, Daten-und BSS-Segment (!!!) über den Eintrag segments möglich. Hiervon kann natürlich ebenfalls nur dringend abgeraten werden.

Abb. 1: Eine solche Grafik (z.B.) kann mit dem Standard-Dialog-Handler des Kontrollfeldes nicht bedient werden.
typedef struct 
{
    WORD cdecl (*cpx_call)(GRECT *work);
    VOID cdecl (*cpx draw)(GRECT *clip);
    VOID cdecl (*cpx wmove)(GRECT *work)
    VOID cdecl (*cpxtimer)(WORD *event)
    VOID cdecl (*cpxkey)(WORD khstate WORD key, WORD *event);
    VOID cdecl {*cpx_button)(MOUSE_RET *mrets, WORD nclicks, WORD *event) 
    VOID cdecl (*cpx_m1)(MOUSE_RET *mrets, WORD *event);
    VOID cdecl (*cpx_m2)(MOUSE_RET *mrets, WORD *event)
    WORD cdecl (*cpx_evhook)(WORD event WORD *msgbuff, MOUSE_RET *mrets, WORD *key, WORD *nclicks)
    VOID cdecl (*cpx_close)(WORT app_term);
} CPX_INFO

Abb. 2: Definition der Event-Schnittstelle des Kontrollfeldes

Der Eintrag valid in CPX_BLOCK gibt dabei an, ob segments einen gültigen Wert enthält, und ok ist immer TRUE für einen gültigen Header.

get_resarea() gibt ebenfalls eine Adresse zurück. Dabei handelt es sich um die Adresse des reservierten Bereichs im Header des aktiven CPX-Moduls. Es ist jedoch auch hier davon abzuraten, in diesem Bereich eigene Daten abzulegen, da es nicht auszuschließen ist, daß er in Zukunft anderweitig benutzt wird.

Die genaue Definition beider Funktionen ist als Nachtrag zu den CPX_PARAMS-Funktionen noch einmal in Tabelle 2 zu finden.

Oberflächengestaltung

An dieser Stelle noch ein paar Worte zum Design eines CPX-Moduls. Wer eigene Module für das variable Kontrollfeld entwickeln möchte, sollte sich beim Entwurf der Oberfläche an die nachfolgend aufgeführten Konventionen halten, um eine gewisse Kontinuität bei der Bedienung gewährleisten zu können.

Zunächst einmal sollte jede Dialogbox, die innerhalb des Fensters erscheinen soll, eine Größe von 32 x 11 Zeichen, d.h. 256 x 176 Pixel (waagerecht x senkrecht) haben; das ist genau die Größe des Kontrollfeldfensters. Kleinere Dialogboxen sind natürlich auch möglich, aber nicht sinnvoll. Des weiteren darf eine Dialogbox mit diesen Ausmaßen natürlich nicht vom Typ OUTLINED sein, um noch vernünftig in das Fenster zu passen.

Weiterhin sollte die Dialogbox einen farbigen Hintergrund besitzen, auf die verschiedene Buttons und Anzeigen in logischen Gruppen (mit weißem Hintergrund) verteilt sind. Als positive Beispiele können nahezu alle bereits von ATARI mitgelieferten Module angeführt werden, lediglich das Modul zur Druckereinstellung weicht von diesem Schema ab. Es besitzt einen durchweg weißen Hintergrund, was die Dialogbox sehr unübersichtlich wirken läßt.

Buttons, bei deren Anklicken ein Pull-Down-Menü herunterklappen soll, sollten einheitlich als SHADOWED deklariert sein, um auf den ersten Blick und ohne langes Probieren verstellbare Parameter lokalisieren zu können. Außerdem sollten an allen Stellen, an denen es mindestens zwei Alternativen gibt, Pull-Down-Menüs statt mehrerer Buttons verwendet werden, um den ohnehin schon geringen Platz nicht noch weiter zu verringern.

Am unteren Rand der Dialogbox sollten sich (sofern benötigt) abgetrennt vom Rest des Dialoges die drei Buttons „Sichern“, „Ok“ und „Abbruch“ (in dieser Reihenfolge) befinden, wobei der „Sichern“-Button von den anderen beiden Buttons durch einen senkrechten Strich abgesetzt und der „Ok“-Button als DEFAULT definiert ist.

Abb. 3: Das fertige „CPX-Construction-Kit“ nach dem Laden eines Headers

Data’s Positronengehirn...

... braucht man nicht, um das „CPX-Construction-Kit“ zu verstehen. Genauso einfach ist es, zu diesem nützlichen Programm zu kommen: Einfach (wieder einmal fehlerfrei) die Listings abtippen und anschließend mittels DEFAULT.PRJ zu einem lauffähigen Programm compilieren. Be nötigt wird dazu (außer natürlich Turbo C) noch das bereits im ersten Teil abgedruckte „XCONTROL.H“. Zu beachten ist außerdem, daß beim Compilieren unbedingt die Compiler-Option „-M“ (no string merging) aktiviert sein muß. Das fertige Programm sollte sich nach dem Starten dann wie in Abbildung 3 präsentieren.

Das Programm übernimmt die Aufgabe, die bisher die BUILD-Programme hatten. Es beinhaltet einen (sehr) einfachen Icon-Editor und ermöglicht es, alle wichtigen Elemente des CPX-Headers komfortabel einzustellen. Mit „LOAD“ können die Daten aus einem bestehenden CPX-Modul ausgelesen werden (z.B. um bei einer neuen Version nicht alles neu einstellen zu müssen). Sind alle Parameter zur Zufriedenheit eingestellt, betätigt man den „BUILD'-Button. Daraufhin wird der Name des bereits vorher (!) erstellten CPX-Programmes (noch ohne Header, Endung also „.PRG“) ausgewählt. Das Construction-Kit macht daraus ein lauffähiges CPX-Modul, das nur noch in den entsprechenden CPX-Ordner kopiert werden muß. („Faszinierend!“) Wer meint, daß der eingebaute Icon-Editor zu einfach ist, soll ihn selbst entsprechend erweitern; das dürfte nicht weiter schwierig sein. Unserer unwesentlichen Meinung nach wäre bei einem komfortablen Icon-Editor das Listing jedenfalls nur unnötig lang geworden. Mit anderen Worten: das Ergebnis hätte in keinem Verhältnis zum Aufwand gestanden. Zu beachten ist bei der Bedienung auch noch, daß als Versionsnummer unbedingt immer eine vierstellige Zahl angegeben werden muß.

Zum Programmtext wollen wir hier nichts weiter sagen, da es sich um ein völlig normales GEM-Programm handelt.

„Close hailing frequencies!“

An dieser Stelle bleibt uns jetzt nichts anderes mehr übrig, als allen Software-Autoren viel Erfolg bei der Entwicklung eigener CPX-Module zu wünschen und zu hoffen, daß dabei viele Programme entstehen, „die noch nie ein Mensch zuvor gesehen hat“.

Uwe Hax & Oliver Scholz

Fehlerteufel

Leider hat uns der berüchtigte Fehlerteufel beim Layout unserer CPX-Reihe einen üblen Fehler gespielt: Wenn im C-Listing dekrementiert wird (—), erscheint nur ein etwas breiterer Minusstrich, statt zwei normalen. In dieser Ausgabe wurde dieser Fehler bereits behoben, aber für die beiden vorherigen Teile geben wir hier noch einmal die zu korrigierenden Zeilen an:

Teil 1, Listing DISK.C:

627: return(&rs_object[--j])

Teil 2, Listing BOOT.C:

368: return(&rs_object[--j]);
615: for (i=(WORD)strlen(path1)-1; i>=0; i--)
663: source->num-—;
724: bd->begin--;
**WORD cpx_call(GRECT *work)**

Diese Funktion wird aufgerufen, wenn das CPX-Modul mit Doppelklick geöffnet wird; sie ist für jedes Modul obligatorisch (mit Ausnahme von Set-Only-Modulen).

Übergabeparameter:

work: Zeiger auf eine GRECT-Struktur, die die Koordinaten und Größe des Kontrollfeldfensters enthält

Rückgabe: FALSE bei Beendigung des CPX-Moduls, TRUE bei weiterer Behandlung über einen eigenen Event-Handler

*VOID cdecl cpx_draw(GRECT clip)

Für eine eigene Event-Behandlung ist diese Funktion obligatorisch. Sie handhabt das (Neu-)Zeichnen eines Dialoges.

Übergabeparameter:

clip: Zeiger auf eine GRECT-Struktur, die die Koordinaten und Größe des neu zu zeichnenden Bereichs enthält.

Rückgabe: keine

*VOID cpx_wmove(GRECT work)

Diese Funktion wird aufgerufen, wenn das Kontrollfeldfenster verschoben wurde, damit der Dialog an die neuen Koordinaten angepaßt werden kann.

Übergabeparameter:

work: Zeiger auf eine GRECT-Struktur, die die neuen Koordinaten und Größe des Kontrollfeldfensters enthält

Rückgabe: keine

*VOID cpx_timer(WORD event)

Wurde ein Timer-Event an das Kontrollfeld gemeldet, wird diese Funktion zur Abarbeitung des Events aufgerufen.

Übergabeparameter:

event: Zeiger auf eine Variable, die immer FALSE ist; wird sie auf TRUE gesetzt, wird das CPX-Modul beendet

Rückgabe: keine

*VOID cpx_key(WORD kbstate, WORD key, WORD event)

Bei einem Keyboard-Event wird diese Funktion abgearbeitet.

Übergabeparameter:

kbstate: Status der Sondertasten (Shift, Control, Alternate) wie von evnt_multi() geliefert

key: Scan- und ASCII-Code der gedrückten Taste

event: CPX-Ende wie bei cpx_timer()

Rückgabe: keine

**VOID cpx_button(MOUSE_RET mrets, WORD nclicks, WORD event)

Tritt ein Mausknopfereignis auf, wird diese Funktion vom Kontrollfeld aufgerufen.

Überaabeparameter:

mrets: Zeiger auf eine Struktur, die die Ergebnisse des Mausknopf-Events enthält. Diese Struktur ist folgendermaßen definiert:

typedef struct {
    WORD    mx,my;
    WORD    mbutton;
    WORD    kbstate;
} MOUSE_RET;

mx,my: die Mausposition bei Auftreten des Events
mbutton: Zustand der Maustaste
kbstate: Zustand der Tastatur-Sondertasten
nclicks: Anzahl der aufgetretenen Mausklicks
event: CPX-Ende wie bei cpx_timer()

Rückgabe: keine

**VOID cpx m1(MOUSE_RET mrets, WORD event)
**VOID cpx_m2(MOUSE_RET mrets, WORD event)

Hierbei handelt es sich um Funktionen für die Bearbeitung zweier Mausereignisse, wie sie von evnt_mouse() bekannt sind.

Übergabeparameter:

mrets: Mausergebnis wie bei cpx_button()
event: CPX Ende wie bei cpx_timer()

Rückgabe: keine

**WORD cpx_evhook(WORD event, WORD *msgbuff, MOUSE_RET *mrets, WORD key, WORD nclicks)

Diese Funktion wird unmittelbar nach der kontrollfeldinternen evnt_multi() aufgerufen und noch vor der Abarbeitung der übrigen Event-Routinen. Sie ist ebenfalls obligatorisch, da nur hier eine AC OPEN-Mitteilung bei bereits geöffnetem Kontrollfeldfenster erkannt werden kann.

Übergabeparameter:

event: die aufgetretenen Events (wie üblich)
msgbuff: Adresse des Message-Buffers
mrets: Mausergebnis wie bei cpx_button()
key: Scan- und ASCII-Code der gedrückten Taste
nclicks: Anzahl der aufgetretenen Mausklicks

Rückgabe: TRUE, falls cpx_evhook() den aufgetretenen Event bereits behandelt hat, FALSE sonst

VOID cpx close(WORD app_term)

Diese Funktion wird bei Schließen des Kontrollfeldfensters aufgerufen.

Übergabeparameter:

app_term: 1 bei Anklicken des Schließfeldes, 0 bei Schließen durch eine terminierende Applikation

Rückgabe: keine

Tabelle 1: Die Event-Schnittstelle des Kontrollfeldes

**CPX_BLOCK *get_rootblock(VOID)**

Die Funktion liefert einen Zeiger auf den ersten CPX-Block in der internen CPX-Liste des Kontrollfeldes. Die weiteren Blöcke können über das Struktur-Element next angesprochen werden.

Übergabeparameter: keine

Rückgabe: wie beschrieben

*BYTE get_resarea(VOID)

Diese Funktion liefert einen Zeiger auf den reservierten Bereich im CPX-Header des aktiven CPX-Moduls; dieser Bereich sollte jedoch nach Möglichkeit nicht für eigene Zwecke benutzt werden.

Übergabeparameter: keine

Rückgabe: wie beschrieben

Tabelle 2: Die restlichen zwei Funktionen der CPX-PARAMS-Struktur

/***********************************************/ 
/* Datei: CPXBUILD.H                           */
/*----------------------*/
/* Programm: CPXBUILD.PRG         Version 1.00 */
/* (C) 1991 by MAXON Computer                  */
/* Autoren: Uwe Hax & Oliver Scholz            */
/* vom RCS erstellte Include-Datei             */
/***********************************************/

#define BUILD 0         /* TREE */
#define CPXNAME 3       /* OBJECT in TREE  #0 */
#define COLLEFT 5       /* OBJECT in TREE  #0 */
#define COLRIGHT 7      /* OBJECT in TREE  #0 */
#define TEXTCOL 6       /* OBJECT in TREE  #0 */
#define CPXID 8         /* OBJECT in TREE  #0 */
#define ICONDATA 10     /* OBJECT in TREE  #0 */
#define ICONSMAL 12     /* OBJECT in TREE  #0 */
#define ICONBOX 11      /* OBJECT in TREE  #0 */
#define ICLEFT 14       /* OBJECT in TREE  #0 */
#define ICRIGHT 16      /* OBJECT in TREE  #0 */
#define ICCOL 15        /* OBJECT in TREE  #0 */
#define ICNNAME 17      /* OBJECT in TREE  #0 */
#define INVERT 18       /* OBJECT in TREE  #0 */
#define VERSION 9       /* OBJECT in TREE  #0 */
#define ICONWORK 19     /* OBJECT in TREE  #0 */
#define SETONLY 21      /* OBJECT in TREE  #0 */
#define RESFLAG 23      /* OBJECT in TREE  #0 */
#define BOOTFLAG 22     /* OBJECT in TREE  #0 */
#define LOAD 24         /* OBJECT in TREE  #0 */
#define OK 25           /* OBJECT in TREE  #0 */
#define ABBRUCH 26      /* OBJECT in TREE  #0 */

/***********************************************/ 
/* Datei: CPXBUILD.RSH                         */
/* ------------------------ */
/* Programm: CPXBUILD.PRG         Version 1.00 */
/* (C) 1991 by MAXON Computer                  */
/* Autoren: Uwe Hax & Oliver Scholz            */
/* vom RCS erstellte (und modifizierte)        */
/* Include-Datei                               */
/***********************************************/ 

WORD image[48];

BITBLK bitblk[] =
{
    image, 6, 24, 0, 0, 1
};

TEDINFO tedinfo[] =
{
    /* Tedinfo 0 */
    "(c) 1991 by Oliver Scholz & Uwe Hax",  "", "",
    5,6, 0, 0x1180, 0x0, -1, 36,1,
    /* Tedinfo 1 */
    "________________"
    "CPX-Name: ________________"
    "XXXXXXXXXXXXXXXX",3, 6, 0, 0x1180, 0x0, -1, 
    17,27,
    /* Tedinfo 2 */
    "____", "ID: ____", "XXXX", 3, 6, 2, 0x1180,
    0x0,-1, 5,9,
    /* Tedinfo 3 */
    "____________", "Name: ____________",
    "XXXXXXXXXXXX", 3, 6, 0, 0x1180, 0x0, -1,13,
    19,
    /* Tedinfo 4 */
    "____", "Version:  __.__", "9999", 3, 6, 2,
    0x1180, 0x0, -1, 5,15
};

OBJECT object[] =
{
    -1, 1, 26, G_BOX, NONE, OUTLINED, 0x1101L, 12, 
    1538, 39,20,
    2,  -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"CPX-Building-Tool", 11,1, 17,1,
    3,  -1, -1, G_TEXT, NONE, NORMAL,
    (LONG)&tedinfo[0], 774,770, 538,1536,
    4,  -1, -1, G_FTEXT, EDITABLE, NORMAL,
    (LONG)&tedinfo[1], 6,260, 27,1,
    5,  -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"Textfarbe:", 6,773, 10,1,
    6,  -1, -1, G_BOXCHAR, TOUCHEXIT, NORMAL, 
    0x4FF1100L, 20,773, 2,1,
    7,  -1, -1, G_BOXCHAR, NONE, NORMAL,
    0x31FF1100L, 278,773, 3,1,
    8,  -1, -1, G_BOXCHAR, TOUCHEXIT, NORMAL, 
    0x3FF1100L, 537,773, 2,1,
    9,  -1, -1, G_FBOXTEXT, EDITABLE, NORMAL,
    (LONG)&tedinfo[2], 24,7, 1806,1,
    10, -1, -1, G_FBOXTEXT, EDITABLE, NORMAL,
    (LONG)&tedinfo[4], 1815,1288, 1806,257,
    20, 11, 19, G_EOX, NONE, OUTLINED, 0xFF1101L, 
    1,263, 1813,1209,
    13, 12, 12, G_BOX, NONE, NORMAL, 0x1100L, 14,1, 
    262,515,
    11, -1, -1, G_IMAGE, NONE, NORMAL,(LONG)bitblk,
    0,0, 6,2,
    14, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"Farbe:", 15,1540, 6,1,
    15, -1, -1, G_BOXCHAR, TOUCHEXIT, NORMAL, 
    0x4FF1100L, 14, 6, 2,1,
    16, -1, -1, G_BOXCHAR, NONE, NORMAL,
    0x31FF1100L, 16,6, 3,1,
    17, -1, -1, G_BOXCHAR, TOUCHEXIT, NORMAL, 
    0x3FF1100L, 19,6, 2,1,
    18, -1, -1, G_FTEXT, EDITABLE, NORMAL,
    (LONG)&tedinfo[3], 2,1032, 18,1,
    19, -1, -1, G_BUTTON, 0x41, NORMAL,
    (LONG)"Invers", 1793,263, 9,1,
    10, -1, -1, G_BOX, TOUCHEXIT, OUTLINED, 
    0xFF1100L, 1536,512, 12,518,
    21, -1, -1, G_STRING, NONE, NORMAL,
    (LONG)"Flags:", 24,11, 6,1,
    22, -1, -1, G_BUTTON, SELECTABLE, NORMAL,
    (LONG)"Set-Only", 25,1548, 9,1,
    23, -1, -1, G_BUTTON, SELECTABLE, NORMAL,
    (LONG)"Bootinit", 1816,782, 9,1,
    24, -1, -1, G_BUTTON, SELECTABLE, NORMAL,
    (LONG)"Resident", 1560,16, 9, 1,
    25, -1, -1, G_BUTTON, 0x5, NORMAL,
    (LONG)"Load", 1,18, 8,1,
    26, -1, -1, G_BUTTON, 0x7, NORMAL,
    (LONG)"Build", 19,18, 8,1,
    0, -1, -1, G_BUTTON, 0x25, NORMAL,
    (LONG)"Abbruch", 29,18, 8,1
};

struct foobar
{
    WORD dummy;
    WORD *image;
} imdope[]={
            0, image
           };

#define NUM_OBS 27
/***********************************************/
/* Datei: CPXBUILD.C                           */
/* ---—------------------ */
/* Programm: CPXBUILD.PRG         Version 1.00 */
/* (C) 1991 by MAXON Canputer                  */
/* Autoren: Oliver Scholz & Uwe Hax            */
/* verwendeter Compiler: Turbo-C 2.0           */
/* !Compileroption -M (string merging) setzen! */ 
/***********************************************/

/* die üblichen Header-Dateien ---------*/

#include <aes.h>
#include <vdi.h>
#include <tos.h>
#include <portab.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "cpxbuild.rsh“
#include "cpxbuild.h"
#include "xcontrol.h"

/* Prototypen für Turbo-C ----------*/

VOID open_vwork(VOID);
VOID init_header(CPX_HEADER *header);
VOID load_header(CPX_HEADER *header);
VOID into_dialog(CPX_HEADER *header, OBJECT *dialog);
WORD get_path(char *pfad);
VOID get_colors(CPX_HEADER *header, WORD *tcolor, WORD *icolor);
VOID copy_icon(CPX_HEADER *header, OBJECT *dialog);
VOID draw_icon(CPX_HEADER *header);
VOID build_cpx(CPX_HEADER *header);
VOID plot(WORD x, WORD y, WORD color); 
char hex(WORD i);

/* ein paar Konstanten und Variablen ------*/

#define TRUE    1
#define FALSE   0

WORD gl_apid;
WORD work_in[12];
WORD work_out[57];
WORD vdi_handle;
WORD ob_x,ob_y,dot_w,dot_h;

/* Hier geht's zur Sache ------------*/

WORD main(VOID)
{
    OBJECT *dialog;
    WORD x, y, w, h;
    WORD exitobj;
    WORD tcolor, icolor, i;
    CPX_HEADER header; char *s,*t;
    WORD dummy,mx,my;
    LONG line;

    /* GEM Applikation initialisieren */ 
    gl_apid=appl_init();
    vdi_handle=graf_handle(&dummy,&dummy,&dummy,&dummy);
    open_vwork();
    init_header(&header);

    /* Resource relozieren */ 
    dialog=object; 
    for (i=0; i<NUM_OBS; i++) 
        rsrc_obfix(dialog, i);

    /* Dialog vorbereiten */ 
    graf_mouse(ARROW,NULL); 
    form_center(dialog,&x,&y,&w,&h); 
    form_dial(FMD_START,x,y,w,h,0,0,0,0);

    /* Hilfsgrößen für plot() */
    objc_offset(dialog, ICONWORK,&obx,&ob_y);
    dot_w=dialog[ICONWORK].ob_width/32;
    dot_h=dialog[ICONWORK].ob_height/24;
    ob_x+=(dialog[ICONWORK].ob_width-dot_w*32)/2;
    ob_y+=(dialog[ICONWORK].ob_height-dot_h*24)/2

    /* Header in Dialog eintragen und darstellen */ 
    into_dialog(&header,dialog); 
    objc_draw(dialog,0,MAX_DEPTH,x,y,w,h);

    /* Hauptschleife: Dialog bearbeiten */ 
    do
    {
        /* Doppelklick maskieren */
        exitobj=form_do(dialog,CPXNAME) & 0x7FFF;

        switch (exitobj)
        {
            /* Informationen aus CPX entnehmen */ 
            case LOAD:
                dialog[exitobj].ob_state &= ~SELECTED; 
                load_header(&header); 
                into_dialog(&header,dialog); 
                objc_draw(dialog,0,MAX_DEPTH,x,y,w,h); 
                draw_icon(&header);
                break;

            /* Iconfarbe Pfeil links */ 
            case ICLEFT:
                dialog[exitobj].obstate &= ~SELECTED; 
                objc_draw(dialog,exitobj,MAX_DEPTH,x,y,w,h);
                get_colors(&header, &tcolor, &icolor); 
                if (icolor>0)
                {
                    icolor--;
                    dialog[ICCOL].ob_spec.obspec.character= hex(icolor);
                    header.icon_info &= 0x0FFF;
                    header.icon_info |= (icolor << 12); 
                    objc_draw(dialog,ICCOL,MAX_DEPTH,x,y,w,h);
                }
                break;

            /* Iconfarbe Pfeil rechts */ 
            case ICRIGHT:
                dialog[exitobj].obstate &= ~SELECTED; 
                objc_draw(dialog,exitobj,MAX_DEPTH,x,y,w,h);
                get_colors(&header, &tcolor, &icolor); 
                if (icolor<15)
                {
                    icolor++;
                    dialog[ICCOL].ob_spec.obspec.character=hex(icolor); 
                    header.icon_info &= 0x0FFF; 
                    header.icon_info |= (icolor << 12);
                    objc_draw(dialog,ICCOL,MAXDEPTH,x,y,w,h);
                }
                break;

            /* Textfarbe Pfeil links */ 
            case COLLEFT:
                dialog[exitobj].ob_state &= ~SELECTED; 
                objc_draw(dialog,exitobj,MAX_DEPTH,x,y,w,h);
                get_colors(&header, &tcolor, &icolor); 
                if (tcolor>0)
                {
                    tcolor--;
                    dialog[TEXTCOL].ob_spec.obspec.character=hex(tcolor); 
                    header.obj_state &= 0xF0FF; 
                    header.obj_state |= (tcolor << 8);
                    Objc_draw(dialog,TEXTCOL,MAX_DEPTH,x,y,w,h);
                }
                break;

            /* Textfarbe Pfeil rechts */ 
            case COLRIGHT:
                dialog[exitobj].ob_state &= ~SELECTED; 
                objc_draw(dialog,exitobj,MAX_DEPTH,x,y,w,h);
                get_colors(&header,&tcolor,&icolor); 
                if (tcolor<15)
                {
                    tcolor++;
                    dialog[TEXTCOL].ob_spec.obspec.character=hex(tcolor); 
                    header.obj_state &= 0xF0FF; 
                    header.obj_state |= (tcolor << 8); 
                    objc_draw(dialog,TEXTCOL,MAX_DEPTH,x,y,w,h);
                }
                break;

            /* Icon invertieren */ 
            case INVERT:
                dialog[exitobj].ob_state &= ~SELECTED; 
                objc_draw(dialog,exitobj,MAX_DEPTH,x,y,w,h); 
                for (i=0;i<24;i++)
                    header.icon_data[i] ^= 0xFFFFFFFFL; 
                copy_icon(&header,dialog)
                objc_draw(dialog,ICONBOX,MAX_DEPTH,x,y,w,h); 
                draw_icon(&header); 
                break;

            /* Iconbereich angewählt (TOUCHEXIT) */ 
            case ICONWORK:
                vq_mouse(vdi_handle,&dummy,&mx,&my);
                mx-=ob_x;
                my-=ob_y;
                mx/-dot_w;
                my/=dot_h;

                if (mx>=0 && mx<-31 && my>=0 && my<—23)
                {
                    header.icon_data[my] ^= (1L<<(31-mx)); 
                    line=header.icon_data[my];
                    graf_mouse(M_OFF,NULL); 
                    plot(mx,my,(line & (1L<<(3^-mx))) ? 1 : 0); 
                    graf_mouse(M_ON,NULL);

                    copy_icon(&header,dialog); 
                    objc_draw(dialog,ICONBOX,MAX_DEFTH,x,y,w,h);
                }
                break;
        }

        /* Button normal darstellen */ 
        dialog[exitobj].ob_state &= ~SELECTED
    }
    while ((exitobj!=OK) && (exitobj!=ABBRUCH));

    form_dial(FMD_FINISH,x,y,w,h,0,0,0,0);

    if (exitobj==OK)
    {
        /* Werte aus dem Dialog lesen */ 
        header.flags.reserved=header.flags.boot_init=header.flags.set_only=FALSE; 
        if (dialog[SETONLY].ob_state & SELECTED) header.flags.set_only=TRUE; 
        if (dialog[BOOTFLAG].ob_state & SELECTED) header.flags.boot_init=TRUE; 
        if (dialog[RESFLAG].ob_state & SELECTED) header.flags.reserved=TRUE;

        s=dialog[VERSION].ob_spec.tedinfo->te_ptext; 
        t=(char *)&header.cpx_version; 
        for(i=0; i<2; i++)
            t[i] =(((*s++)-'0')<<4) | ((*s++)-'O');

        strncpy(header.cpx_id,dialog[CPXID].ob_spec.tedinfo->te_ptext,4); 
        strcpy(header.icon_name,dialog[ICNNAME].ob_spec.tedinfo->te_ptext); 
        strcpy(header.cpx_name,dialog[CPXNAME].ob_spec.tedinfo->te_ptext);

        /* CPX Modul 'linken' */ 
        build_cpx(&header)
    }

    /* bei GEM abmelden */ 
    v_clsvwk(vdi_handle); 
    appl_exit(); 
    return(0);
}

/* Header mit sinnvollen Daten initialisieren -*/

VOID init_header(CPX_HEADER *header)
{
    WORD i;
    char init[]="@\0";

    header->magic=100; 
    header->flags.boot_init=TRUE; 
    strcpy(header->cpx_id,init); 
    header->cpx_version=0; 
    strcpy(header->icon_name,init); 
    for (i=0; i<24; i++)
        header->icon_data[i]=0L; 
    header->icon_info=0x1000; 
    strcpy(header->cpx_name,init); 
    header->obj_state=0x1180;
}

/* Aktuellen Pfad und Laufwerk holen -------*/

WORD get_path(char *pfad)
{
    pfad[0]='A'+Dgetdrv();
    pfad[1]=':'; 
    pfad[2]='\0';

    return(Dgetpath(pfad+3,0));
}

/* Header aus CPX Modul lesen ---------*/

VOID load_header(CPX_HEADER *header)
{
    char pfad[128],filename[16],*pathend; 
    WORD button,handle;

    get_path(pfad); 
    strcat(pfad,"*.CP?"); 
    filename[0]='\0';

    fsel_input(pfad,filename,&button); 
    if (button)
    {
        if ((pathend=strrchr(pfad,(int)'\\'))!=NULL)
        {
            strcpy(pathend+1,filename); 
            if ((handle=Fopen(pfad,0))>0)
            {
                Fread(handle,512L,header);
                Fclose(handle);
            }
        }
    }
}

/* Daten aus Header in Dialog eintragen -----*/

VOID into_dialog(CPX_HEADER *header OBJECT *dialog)
{
    WORD tcol,icol,i; 
    char ver[4],*s;

    strcpy(dialog[CPXNAME].ob_spec.tedinfo->te_ptext,header->cpx_name);
    strcpy(dialog[ICNNAME].ob_spec.tedinfo->te_ptext,header->icon_name); 
    strncpy(dialog[CPXID].ob_spec.tedinfo->te_ptext.header->cpx_id,4);

    get_colors(header,&tcol,&icol); 
    dialog[TEXTCOL].ob_spec.obspec.character=hex(tcol);
    dialog[ICCOL].ob_spec.obspec.character=hex(icol);

    dialog[ICONSMAL].ob_spec.bitblk->bi_wb=4; 
    dialog[ICONSMAL].ob_spec.bitblk->bi_hl=24;

    copy_icon(header,dialog);

    if (header->flags.set_only)
        dialog[SETONLY].ob_state |= SELECTED; 
    else
        dialog[SETONLY].ob_state &= ~SELECTED;

    if (header->flags.boot_init)
        dialog[BOOTFLAG].ob_state |= SELECTED; 
    else
        dialog[BOOTFLAG].ob_state &= ~SELECTED;

    if (header->flags.reserved)
        dialog[RESFLAG].ob_state |= SELECTED; 
    else
        dialog[RESFLAG].ob_state &= ~SELECTED;

    s=(char *) &(header->cpx_version); 
    for(i=0; i<2; i++)
    {
        ver[2*i]=((s[i]>>4) & 0xF)+'0'; 
        ver[2*i+1]=(s[i] & 0xF)+'0';
    }

    strncpy(dialog[VERSION].ob_spec.tedinfo->te_ptext,ver,4);
}

/* Text- und Iconfarbe aus Header auslesen —*/

VOID get_colors(CPX_HEADER *header, WORD *tcolor, WORD *icolor)
{
    *icolor=(header->icon_info >>12) & 0xF,
    *tcolor=(header->obj_state >> 8) & 0xF;
}

/* Zahl 0..15 in Hexzahl umwandeln --------*/

char hex(WORD i)
{
    if ((i>=0) && (i<10)) 
        return('0'+(char)i); 
    if ((i>=10) && (i<16))
        return('A'+(char)(i-10)); 
    return('0');
}

/* Icon aus Header in Dialog kopieren -----*/

VOID copy_icon(CPX_HEADER *header, OBJECT *dialog)
{
    WORD i;

    for (i=0; i<24; i++)
    {
        dialog[ICONSMAL].ob_spec.bitblk->bi_pdata[2*i]=(WORD)((header->icon_data[i])>>16); 
        dialog[ICONSMAL].ob_spec.bitblk->bi_pdata[2*i+1]=(WORD)((header->icon_data[i]) & 0xFFFFL);
    }
}

/* Icon in GROSS malen -------------*/

VOID draw_icon(CPX_HEADER *header)
{
    WORD ix,iy;
    LONG line;
    graf_mouse(M_OFF,NULL); 
    for (iy=0; iy<24; iy++)
    {
        line=header->icon_data[iy]; 
        for (ix=0; ix<32; ix++)
        plot(ix,iy,(line&(1L<<(31-ix))) ? 1 : 0 );
    }
    graf_mouse(M_ON,NULL);
}

/* Ein 'grosses Pixel' malen ----------*/

VOID plot(WORD x, WORD y, WORD color)
{
    WORD pxyarray[4];

    vsf_color(vdi_handle,color);

    pxyarray[0]=ob_x+x*dot_w; 
    pxyarray[1]=ob_y+y*dot_h; 
    pxyarray[2]=pxyarray[0]+dot_w-1; 
    pxyarray[3]=pxyarray[1]+dot_h-1;

    v_bar(vdi_handle,pxyarray);
}

/* CPX Modul bauen: Header vor Prg schreiben —*/

VOID build_cpx(CPX_HEADER *header)
{
    char pfad[128],filename[16],*pathend; 
    char wpfad[128];
    WORD button,whandle,handle;
    LONG length;

    get_path(pfad); 
    strcat(pfad,"*.PRG"); 
    filename[0]='\0';

    fsel_input(pfad,filename,&button); 
    if (button)
    {
        if ((pathend=strrchr(pfad,(int)'\\'))!=NULL)
        {
            strcpy(pathend+1,filename); 
            strcpy(wpfad,pfad);
            if ((pathend=strrchr(wpfad,(int)'.'))!=NULL)
            {
                strcpy(pathend,".CPX"); 
                if ((handle=Fopen(pfad,0))>0 && (whandle=Fcreate(wpfad,0))>0)
                {
                    Fwrite(whandle,512L,header);

                    do
                    {
                        length=Fread(handle,512L,header); 
                        Fwrite(whandle,length,header);
                    }
                    while (length==512L);

                    Fclose(handle);
                    Fclose(whandle);
                }
            }
        }
    }
}

/* kein Kommentar ---------------*/

VOID open_vwork(VOID)
{
    WORD i;

    for (i=1; i<10; i++) 
        work_in[i]=1; 
    work_in[10]=2;
    v_opnvwk(work_in,&vdi_handle,work_out);
}


Aus: ST-Computer 05 / 1991, Seite 101

Links

Copyright-Bestimmungen: siehe Über diese Seite