In einem früheren Artikel [1] zeigte ich, wie man die Bildschirmzeichensätze des GDOS nutzt. Des weiteren bietet GDOS die komfortable Ausgabe auf verschiedene Medien wie Drucker und Datei an. Deshalb möchte ich heute eine Unit vorstellen, die die Verwaltung der Ausgabegeräte von GDOS übernimmt.
Zur Wiederholung möchte ich noch einmal kurz die Grundlagen des GDOS (Graphics Device Operating System) erklären: Das GDOS ist eigentlich der Teil des Betriebssystems, der die Verwaltung von verschiedenen Gerätetreibern (Bildschirm, Drucker und Metafile) übernimmt. Aus Platzmangel im ROM ist dieserTeil des Betriebssystems als eigenständiges Autoordnerprogramm realisiert. Es erlaubt dem Programmierer hardwareunabhängige Programmierung, da der Kontakt mit den Ausgabegeräten vollkommen vom GDOS übernommen wird. Der Vorteil der GDOS-Nutzung wird am Beispiel eines Druckertreibers schnell klar. Stellen Sie sich vor, alle Ihre Programme würden über GDOS ausgegeben, dann bräuchten Sie nur einen einzigen GDOS-Druckertreiber und könnten das Problem Druckeranpassung für alle Zeit vergessen. Leider sieht die Wirklichkeit ganz anders aus, da GDOS leider von den wenigsten Programmen unterstützt wird. Dies liegt zum Teil an der etwas komplizierten Installation sowie an der mangelnden Geschwindigkeit des Atari-GDOS, die aber mit Erscheinen des neuen GDOS ,Speedo‘ der Vergangenheit angehören sollte. Schon jetzt steht mit dem Programm NVDI ein sehr schnelles GDOS zur Verfügung.
Beim Start des GDOS aus dem Autoordner sucht das Programm zuerst nach der Datei ,ASSIGN.SYS' im Wurzelverzeichnis des Boot-Laufwerks. Diese Datei beinhaltet alle Informationen über die zu ladenden Gerätetreiber und ihre zugehörigen Zeichensätze (Information über den genauen Aufbau findet man in [2] und [3]). In dieser Datei wird jedem Gerätetreiber eine Nummer zugeordnet, über die man ihn später ansprechen kann. Dabei gelten die Zuordnungsregeln aus Tabelle 1. Sie werden sich nun fragen, warum zu jedem Typ mehrere Treiber zur Verfügung gestellt werden können? Nehmen wir z.B. den Drucker, so hat man die Möglichkeit, im ,ASSIGN.SYS' einen Laser- und einen Nadeldrucker zu installieren. Jetzt kann man in einem Programm, in dem es nicht auf die Optik ankommt, den Nadeldrucker und in der Textverarbeitung den Laserdrucker ansprechen, ohne zwischendurch ,ASSIGN.SYS' anpassen und einen Reset durchführen zu müssen. Dies funktioniert allerdings nur, wenn der Anwender mit Hilfe der Gerätenummer im Programm den Gerätetreiber auswählen kann. Dies sollte man in eigenen Programmen immer ermöglichen; im Demo wird aus Bequemlichkeit immer der Standardtreiber gewählt.
Nummer | Typ des Gerätes |
---|---|
01-10 | Bildschirm |
11-20 | Plotter |
21-30 | Drucker |
31-40 | Metafile |
41-50 | Kamera |
51-60 | Grafiktablett |
Durch die Gerätenummern können verschiedene Ausgabegeräte angesprochen werden.
Kommen wir jetzt zur Nutzung des GDOS in unseren eigenen Programmen: Beim Aufruf aller VDI-Befehle wird immer das sogenannte Workstation-Handle benötigt. Gibt man auf den Bildschirm aus, wird hier das Handle einer virtuellen Workstation übergeben, das man mittels der Prozedur v_openvwk erhält (siehe Listing Routine Init_GEM). Der große Vorteil der Nutzung von GDOS besteht nun darin, daß man eigentlich nur der Ausgaberoutine statt des Bildschirm-Handles das Handle eines anderen Ausgabegerätes übergeben muß, um die Ausgabe auf dieses Gerät umzulenken. Um an das Handle des Ausgabegerätes zu gelangen, dient die Funktion v_opnwk, die praktisch das Laden der Gerätetreiber übernimmt. Ich habe diese Routine durch eine kompaktere ersetzt, der man nur noch die Nummer des Gerätetreibers (device) und das gewünschte Koordinatensystem (koord, siehe [2] und [3]) übergeben muß. Sie hat folgendes Aussehen:
handle := Open_Work(koord,device);
handle, koord, device : INTEGER;
Sie übernimmt nicht nur das Öffnen der Workstation, sondern lädt auch gleich die zugehörigen Zeichensätze. Die Zeichensätze werden wie in [1] beschrieben geladen. Die Routine v_openwk stürzt leider hemmungslos ab, wenn kein GDOS geladen ist. Deshalb habe ich mit Hilfe des Inline-Assemblers die Funktion vq_gdos implementiert, die bei installiertem GDOS einen Wert ungleich Null zurückliefert. Ich verwalte jeweils nur ein anderes Ausgabegerät neben dem Bildschirm, da die Gerätetreiber und die Zeichensätze den Speicher doch belasten. Der Speicherplatz wird vom Betriebssystem zur Verfügung gestellt; man sollte deshalb der Link-Option {$M, stack,hmin,hmax,dosfree} einen großen Wert (-200 KByte) für dosfree übergeben. Nachdem man die Workstation geöffnet hat, übergibt man nur der Ausgaberoutine das neue Handle und hat den Text oder die Grafik auf Drucker oder das Metafile ausgegeben. Danach löschen wir mit der Routine Close_Work (handle: Integer) den Gerätetreiber und seine Zeichensätze aus dem Speicher. So schön einfach können GEM & GDOS sein.
Diese Frage haben Sie sicher auch schon mal gestellt. Das Metafile ist eine standardisierte Datei, in der die VDI-Aufrufe, die zur Ausgabe nötig sind, abgespeichert werden. Es handelt sich also um ein objektorientiertes Format, da die Zeichenvorschriften und keine BIT-Images (gesetzte und ungesetzte Pixel) abgespeichert werden. Man bietet dem Anwender mit der Ausgabe auf Metafile eine einfache Möglichkeit zur Nachbearbeitung mit anderen Programmen an. Dem Metafile kann man im Gegensatz zum Drucker einige zusätzliche Informationen übergeben, die ich hier nur kurz auflisten möchte:
Die Funktionen 1 - 3 werden von der Unit GEMVdi zur Verfügung gestellt. Die Funktionen vm_coords und vm_pagesize müssen wir direkt beim Betriebssystem anfordern. Wie man Routinen des AES bzw. VDI anspricht, habe ich schon in [4] und [5] beschrieben.
Das Demo-Programm besteht aus zwei Teilen: 1. der GDOS-Unit, die man nun in eigenen Programmen fleißig verwenden sollte und 2. der eigentlichen Demo. Das Programm nutzt ein RSC-File, das aber nur eine Menüleiste beinhaltet. Das Nachbauen des Menüs dürfte mit Bild 1 und dem Listing von ,GDOSDEMO.I‘ kein Problem darstellen. Auf der Monats-Disk liegt natürlich das RSC-File bei.
Literatur:
[1] ST Computer 6/91, S. 82, ,GDOS-Zeichensätze in MAXON-Pascal'
[2] Jankowski, Reschke, Rabisch, ,Atari ST Profibuch', Sybex-Verlag
[3] Geiß, D. und J., ,Vom Anfänger zum GEM-ProfT, Hüthig-Verlag
[4] ST Computer 2/92, S.83, ,Form_Keybd & Form_Button in MAXON-Pascal ‘
[5] ST Computer 7/8/92, S.1992, Quicktip
Mit der Unit GDOS kann man bequem die Ausgabe vom Bildschirm auf den Drucker oder in eine Metadatei umlenken.
Unit GDOS;
{ von Wolfgang Sattler 1992 }
{ (c) MAXON Computer 1993 }
Interface
USES GEMVdi,GEMdecl,GEMAes;
TYPE
{ Feld zum Verwalten von Zeichensätzen }
SCH_NAME_TYP = STRING[32];
SCHRIFTART = RECORD
{ Nummer die bei vst_font }
{ wird: }
index:INTEGER;
{ Zeichensatzname: }
name: SCH_NAME_TYP;
END;
SCHRIFTFELD = ARRAY[1..20] OF SCHRIFTART;
SchPtr = ^SCHRIFTFELD;
VAR
{ Schriftfelder }
bild_schrift,device_schrift
: SCHRIFTFELD;
{ Anzahl der Schriften }
bild_anz,device_anz : INTEGER;
{ GDOS installiert ? }
gdos_flag : BOOLEAN;
{ Felder zum Öffnen von VDI-Workstations }
workin : IntIn_Array;
workout : WorkOut_Array;
{ Handle, d.h. Kennung des virtuellen }
{ Bildschirmes: }
vdi_handle : INTEGER;
{ Kennung des AES-Prozesses: }
aes_handle : INTEGER;
PROCEDURE vm_coords(handle,llx,lly,urx,ury : INTEGER);
PROCEDURE vm_pagesize(handle,pagew,pageh : INTEGER);
PROCEDURE Init_GDOS(ohne:BOOLEAN);
FUNCTION Open_Work(koord,device:INTEGER):INTEGER;
PROCEDURE Close_Work(handle:INTEGER);
PROCEDURE Exit_Gdos;
Implementation
TYPE
( Parameterblock dient zur Konversation mit }
{ dem VDI: }
VDIParBlk = RECORD
control : ^control_Array;
intin : ^intin_Array;
ptsin : ^ptsin_Array;
intout : ^intout_Array;
ptsout : ^ptsout_Array;
END;
VAR vdipb : ^VDIParBlk;
point : Pointer;
GDOS_FEHLT :STRING;
junk : INTEGER;
{ Überprüfung auf installiertes GDOS: }
FUNCTION Vq_Gdos:INTEGER;ASSEMBLER;
ASM
move.w #-2,D0
trap #2
cmp.w #-2,d0
sne d0
ext.w d0
move.w d0,@result
END;
PROCEDURE vm_coords(handle,llx,lly,urx,ury : INTEGER);
BEGIN
vdipb:=addr(vdi_pb);
vdipbc.intin^[0]:=1;
vdipb^.intin^[1]:=llx;
vdipb^.intin^[2]:=lly;
vdipb^.intin^[3]:=urx;
vdipb^.intin^[4]:=ury;
vdipb^.control^[0]:=5;
vdipb^.control^[1]:=0;
vdipb^.control^[2]:=0;
vdipb^.control^[3]:=5;
vdipb^.control^[4]:=0;
vdipb^.control^[5]:=99;
vdipb^.control^[6]:=handle;
ASM
{ VDI-Parameterblock }
move.l vdipb,d1
{ Magic-Number für VDI }
move.w #115,d0
{ GEM-Aufruf }
trap #2
END;
END;
PROCEDURE vm_pagesize(handle,pagew,pageh : INTEGER);
BEGIN
vdipb:=addr(vdi_pb);
vdipb^.intin^[0]:=0;
vdipb^.intin^[1]:=pagew;
vdipb^.intin^[2]:=pageh;
vdipb^.control^[0]:=5;
vdipb^.control^[1]:=0;
vdipb^.control^[2]:=0;
vdipb^.control^[3]:=3;
vdipb^.control^[4]:=0;
vdipb^.control^[5]:=99;
vdipb^.control^[6]:=handle;
ASM
{ VDI-Parameterblock }
move.l vdipb,d1
{ Magic-Number für VDI }
move.w #115,d0
{ GEM-Aufruf }
trap #2
END;
END;
{ Ermitteln der Länge eines C-Strings: }
FUNCTION Strlen(name:STRING): INTEGER;
VAR la: INTEGER;
BEGIN
la:=1;
while name[la]<>chr(0) DO
Inc(la);
Strlen:=la-1;
END;
{ Laden von GDOS-Zeichensätzen: }
PROCEDURE Loadfonts(handle:INTEGER;schrift : SchPtr;VAR anzahl:INTEGER);
VAR la:INTEGER;
BEGIN
IF handle>0 THEN
BEGIN
{ Zeichensätze laden }
anzahl:=vst_load_fonts(handle,0);
anzahl:=anzahl+1;
{ Namen und Index werden erfragt }
{ und in das Feld eingetragen }
FOR la := 1 TO anzahl DO
WITH schrift^[la] DO
BEGIN
index:=vqt_name(handle,la,name);
{ Länge des C-Strings erfragen und in }
{ name[0] eintragen: }
name[0]:=chr(strlen(name));
END;
END;
END;
FUNCTION Open_Work(koord,device:INTEGER):INTEGER;
VAR la :INTEGER;
BEGIN
{ Bei installiertem GDOS öffnen der }
{ Workstation, sonst den Wert '0' als }
{ Fehler zurückgeben: }
IF gdos_flag THEN
BEGIN
{ GDOS ist installiert }
{ Parameter für den v_opnwk-Aufruf: }
workin[0] := device;
FOR la := 1 TO 9 DO
workin[la] := 1;
workin[10] := koord;
{ Öffnen der Workstation: }
v_opnwk(workin,device,workout);
{ Zeichensätze laden }
loadfonts(device,@device_schrift,device_anz);
END
ELSE
{ GDOS nicht installiert: }
{ Fehler: Open_Work:=0 }
device:=0;
Open_Work:=device;
END;
PROCEDURE Init_GDOS(ohne:BOOLEAN);
BEGIN
GDOS_FEHLT:='[3][ | GDOS ist nicht '+'installiert ! ][OK]'+#00 ;
IF gdos_flag=FALSE THEN
{ Kein GDOS installiert ! }
BEGIN
junk:=form_alert(1,GDOS_FEHLT[1]);
{ ohne = FALSE -> Programmabbruch }
IF NOT ohne THEN
BEGIN
Halt(0);
END;
END
END;
{ Löschen der Zeichensätze und }
{ des Gerätetreibers }
PROCEDURE Close_Work(handle:INTEGER);
BEGIN
IF handle <> 0 THEN
BEGIN
vst_unload_fonts(handle,0);
v_clswk(handle);
END;
END;
PROCEDURE Exit_Gdos;
BEGIN
{ Löschen der Bildschirmzeichensätze: }
vst_unload_fonts(vdi_handle,0);
rsrc_free;
v_clsvwk(vdi_handle);
appl_exit;
END;
PROCEDURE Init_GEM;
VAR la,dummy:INTEGER;
feld:ARRAY_10;
BEGIN
aes_handle := appl_init;
IF aes_handle >= 0 THEN
BEGIN
vdi_handle := graf_handle(dummy,dummy,dummy,dummy);
FOR la := 0 TO 9 DO workin[la] := 1;
workin[10] := 2;
v_opnvwk (workin, vdi_handle, workout);
END
ELSE
BEGIN
WRITELN(' Applikation konnte nicht '+ 'geöffnet werden ! ');
repeat until Keypressed;
halt(0);
END;
END;
BEGIN
{ Anmelden beim GEM: )
Init_GEM;
IF vq_gdos = 0 THEN
gdos_flag:=FALSE
ELSE
BEGIN
gdos_flag:=TRUE;
{ Bildschirmzeichensätze laden: }
loadfonts(vdi_handle,@bild.schrift, bild_anz);
END;
END.
PROGRAM GDOS_Demo (INPUT,OUTPUT);
{ von Wolfgang Sattler 1992 }
{(c) MAXON Computer 1993 }
USES GEMDecl,GEMVdi,GEMAes,GDOS;
{$I GDOSDEMO.I)
VAR men : Pointer;
RSC_FEHLT,INFO : STRING;
x0,y0,w0,
h0,junk,
win_handle : INTEGER;
handle : INTEGER;
fenst_titel : STRING;
ch:CHAR;
{ Laden des Resource-Files: }
PROCEDURE Init_Resource;
VAR ResourceName : STRING;
fehler : INTEGER;
BEGIN
If RunFromMemory THEN
ResourceName := 'GDOS\GDOSDEMO.RSC' + #0
ELSE
ResourceName := 'GDOSDEMO.RSC' + #0;
rsrc_load(ResourceName[1]);
fehler:=GemError;
IF fehler = 0 THEN
BEGIN
junk:=form_alert(1,RSC_FEHLT[1]);
halt(0);
END
ELSE
rsrc_gaddr(R_TREE,MENU, men);
END;
{ Die 'Ausgabe'-Routine wird von allen Geräten }
{ benutzt. Übergeben wird das Gerätehandle }
PROCEDURE Ausgabe(handle,x0,y0:INTEGER);
VAR p:ARRAY_4;
la,dummy : INTEGER;
BEGIN
{ Textfarbe setzen: }
vst_color(handle,BLACK);
{ Texthöhe mit vst_point setzen }
vst_point(handle,12,dummy,dummy,dummy,dummy);
FOR la:=1 TO bild_anz DO
BEGIN
{ Auswahl der Schrift mittels Schriftindex }
vst_font(handle, bild_schrift[la].index);
{ Ausgabe des Textes }
v_gtext(handle,x0+40,y0+la*40,'Das ist der GDOS-Zeichensatz: '+ bild_schrift[la].name);
END;
END ;
{ Zeichnen des Fensterhintergrundes }
PROCEDURE Do_Redraw(window,x0,y0,w0,h0:INTEGER);
VAR p:ARRAY_4;
BEGIN
{ Bearbeitung beginnen }
graf_mouse(M_OFF, NIL);
wind_update(BEG_MCTRL);
wind_update(BEG_UPDATE);
p[0] := x0;
p[1] := y0;
p[2] := x0 + w0 - 1;
p[3] := y0 + h0 - 1;
{ Zeichenbereich festlegen }
vs_clip(vdi_handle, 1, p);
{ Parameter festlegen und Fensterhintergrund }
{ malen }
vsf_color(vdi_handle, WHITE);
vsf_interior(vdi_handle, SOLID);
vr_recfl(vdi_handle, p);
{ Jetzt geht es zur Ausgabe des Textes }
Ausgabe(vdi_handle,x0,y0);
wind_update(END_UPDATE);
wind_update(END_MCTRL);
graf_mouse(M_ON,NIL);
END ;
{ Event_Verwaltung: Es werden nur die Menü- }
{ aktionen überwacht. }
PROCEDURE Event_Loop;
VAR msg : ARRAY[0..15] OF INTEGER ;
schluss : BOOLEAN;
BEGIN
schluss:=FALSE;
REPEAT
Evnt_mesag(msg);
{ Programmende: }
IF msg[4] = ENDE THEN
schluss:=TRUE;
{ Ausgabe auf Drucker: }
IF msg[4] = DRUC THEN
BEGIN
graf_mouse(BUSYBEE,NIL);
{ Laden des Gerätetreibers und der }
{ Zeichensätze: }
handle:=Open_Work(2,21);
{ Ausgabe-Routine aufrufen: }
Ausgabe(handle,10,40);
{ Um den Ausdruck zu starten muß man }
{ den internen Grafikpuffer vom }
{ GDOS ausgeben: }
v_updwk(handle);
{ Gerätetreiber und Zeichensätze }
{ löschen: }
Close_Work(handle);
graf_mouse(ARROW,NIL);
END;
{ Ausgabe auf Metafile: }
IF msg[4] = META THEN
BEGIN
graf_mouse(BUSYBEE,NIL);
{ Metafiletreiber hat Gerätenummer 31 }
handle:=Open_Work(2,31);
{ Dateiname auf 'GDOSDEMO.GEM' setzen }
vm_filename(handle,'GDOSDEMO.GEM');
Ausgabe(handle,10,40);
{ Ausdehnung der Ausgabe: }
v_meta_extents(handle,0,0,450,280);
{ Koordinatensystem und Ausmaße der }
{ Ausgabenseite festlegen: }
vm_coords(handle,0,200,480,0);
vm_pagesize(handle,1905,2540);
Close_Work(handle);
graf_mouse(ARROW,NIL);
END;
{ Infomeldung: }
IF msg[4] = ABOUT THEN
junk:=form_alert(1,INFO[1]);
{ Menütitel normal darstellen: }
menu_tnormal(men,msg[3],1);
UNTIL schluss;
END;
BEGIN {HAUPTPROGRAM}
RSC_FEHLT:='[3][ | GDOSDEMO.RSC nicht '+ 'gefunden ! ][Ende]'+#00 ;
INFO:='[0][ | GDOS-Demo by| Wolfgang'+' Sattler ! | (c) 1992 ][OK]'+#00;
{ Resourcen laden }
Init_Resource;
{ Überprüfen ob GDOS geladen ist und gegeben- }
{ falls Programm beenden }
Init_Gdos(FALSE);
menu_bar(men,1);
x0:=10;
y0:=100;
w0:=480;
h0:=200;
fenst_titel:=' Ausgabe ins Fenster ';
fenst_titel:=fenst_titel +#00 +#00 ;
win_handle:=wind_create(NAME,x0,y0,w0,h0);
wind_set(win_handle,WF_NAME,HiPtr(fenst_titel[1]),LoPtr(fenst_titel[1]),0,0);
graf_mouse(M_OFF, NIL);
wind_open(win_handle,x0,y0,w0,h0);
graf_mouse(M_ON, NIL);
wind_get(win_handle,WF_WORKXYWH,x0,y0,w0,h0);
Do_Redraw(win_handle,x0,y0,w0,h0);
graf_mouse(ARROW, NIL);
Event_Loop;
wind_close(win_handle);
wind_delete(win_handle);
menu_bar(men,0);
{ Bildschirmzeichensätze löschen und }
{ beim VDI und AES abmelden: }
Exit_Gdos;
END.
CONST
MENU = 0;
(* Menuebaum *)
ABOUT = 7;
(* Infomeldung im Menu GDOS_DEMO *)
ENDE = 16;
(* Programmende *)
DRUC = 18;
(* Ausgabe auf Drucker *)
META = 19;
(* Ausgabe in Metafile *)