Programmieren

DataType-Programmiertip

von Jürgen Klawitter

Im Gadget #29 hatte ich beschrieben, wie man mit Hilfe von DataTypes Bilder auf eigenem Screen anzeigen kann. Der beigefügte Assembler-Source zeigte nur die wichtigsten Routinen.

Inzwischen habe ich etwas Zeit gefunden, um diese Routinen zu einem lauffähigen Programm auszubauen. Es heißt MView und kann nicht nur Bilder, sondern auch Texte, Guides usw. anzeigen. MView, der dazugehörige Sourcecode und ein kurzes .dok befinden sich in dieser Ausgabe des AmigaGadget. Um MView auszuprobieren, kann man sich das Bild pic002 anzeigen lassen. OS V39+ ist allerdings Vorraussetzung.

Der Sourcecode ist ausreichend kommentiert, so daß ich nur auf einige Besonderheiten hinweisen möchte. Assembliert werden kann nur mit dem A68k, weil das Format für lokale Label - sie beginnen mit "\" - von anderen Assemblern nicht verstanden wird.

MView ist "pure", kann also resident gemacht werden. Wie erreicht man das?

  1. Man muß absolute Adressen und damit Reloc-Hunks vermeiden. Die Adres- sierung muß immer relativ sein, entweder pc-relativ oder relativ zu einem anderen Adressregister.
  2. Der Programmcode darf nicht während der Laufzeit verändert werden. Auf den Datenbereich im Programm darf nur lesend zugegriffen werden. Variablen werden daher in einem eigenen Speicherbereich abgelegt, der mit AllocMem() bzw. AllocVec() besorgt wird. Üblicherweise hält man die Adresse dieses Puffers in Register a4.

MView akzeptiert ein Muster als Argument, d.h. im angegebenen Dateinamen könen wildcards enthalten sein. Möglich wurde das durch Verwendung der Dos-Funktionen MatchFirst(), MatchNext() und MatchEnd(). Ich hatte diese Funktionen vorher nie benutzt und war deshalb überrascht, wie komfortabel man damit arbeiten kann. Sie ersparen einem die Anwendung einer ganzen Reihe von Dos-Funktionen, z.B. Lock(), Examine(), ExNext, MatchPattern(), NameFromLock() und noch verschiedenes mehr. Alle 3 Funktionen erwarten als ein Argument einen Zeiger auf den sog. AnchorPath. Der Aufbau dieser Struktur ist wie folgt:

    STRUCTURE   AnchorPath,0
    APTR        ap_Base
    APTR        ap_Last
    LONG        ap_BreakBits        ;Bits für Ctrl-C bis Ctrl-F
    LONG        ap_FoundBreak
    BYTE        ap_Flags
    BYTE        ap_Reserved
    WORD        ap_Strlen           ;Länge von ap_Buf
    STRUCT      ap_Info,260         ;FileInfoBlock
    ------------------------
    STRUCT      ap_Buf
   

Der Speicher für AnchorPath (longword aligned) muß vom Programm zur Verfügung gestellt werden, initialisiert wird er größtenteils von MatchFirst(). Wenn man den vollen Pfad einer Datei haben will, ist ein Puffer dafür direkt hinter ap_Info anzuhängen und seine Länge in ap_Strlen einzutragen. Initialisiert werden können auch ap_Flags und ap_BreakBits. Letztere entscheiden darüber, ob der User mit einer Ctrl-Tastenkombination abbrechen kann. Für Ctrl-C ist z.B. $1000, für alle möglichen Kombinationen (C,D,E,F) $F000 einzutragen. Das sollte man allerdings erst vor dem Aufruf von MatchNext() tun, da eine Initialisierung vor MatchFirst() nach meiner Erfahrung keine Wirkung hat.

Nach MatchFirst() wird MatchNext() so lange aufgerufen, bis ein Wert ungleich Null zurückgegeben wird. d0 enthält dann einen der DosErrorCodes, wie sie auch von IoErr() geliefert werden. Dieser Code kann mittels PrintFault() in eine lokalisierte Fehlermeldung an der Shell umgewandelt werden. Anschließend wird MatchEnd() aufrufen. Bei normalem Verlauf tritt am Ende ERROR_NO_MORE_ENTRIES auf oder ERROR_BREAK, falls der User eine von den BreakBits zugelassene Ctrl-Kombination gedrückt hat.

Noch einige Infomationen zu ap_Flags: Sie sind im Handbuch (dosasl.h) dokumentiert, allerdings ohne den Hinweise, daß APB_DOWILD und APB_DODOT nicht implementiert sind. Durch Setzen von APB_DODIR kann man in ein Unter- verzeichnis eintreten, wenn MatchNext() auf eines stößt. Im Normalfall werden Unterverzeichnisse übergangen. Beim Wechsel zurück ins übergeordnete Verzeichnis setzt MatchNext() APB_DIDDIR.

Der Abbruch mittels Ctrl-C ist für den User ein wenig mühselig, wenn das Programm wie im Falle von MView ein eigenes Fenster öffnet und damit die Shell inaktiviert. Hier sollte man andere Abbruchmöglichkeiten vorsehen. Normalerweise empfängt ein Programm Eingaben über den IDCMP-Port des von ihm geöffneten Fensters und kann bestimmte Tasten für den Abbruch vorsehen. MView benutzt dafür bei der Bildanzeige ESC und Q. Bei Verwendung der amigaguide.library ist das Programm hingegen von der Außenwelt abgeschnitten. Die IntuiMessages, die an das von der library geöffnete Fenster gehen, werden auch von ihr ausgewertet. Um dem User eine komfortable Möglichkeit zum Abbruch zu geben, öffnet MView nach jeder Anzeige einen EasyRequester - vorausgesetzt, ein Muster wurde angegeben - der über die jeweils nächste Datei informiert und die Gadgets ZEIGEN, WEITER und ABBRUCH aufweist. Da der Punkt WEITER auch nach der Anzeige eines Bildes Sinn macht, wird er auch hier verwendet.

Von den beiden Möglichkeiten, einen EasyRequester aufzurufen, habe ich BuildEasyRequestArgs() verwendet, weil nur mit dieser Funktion Messages über die vom User gedrückten Tasten empfangen werden können. EasyRequestArgs() erlaubt zwar auch durch Angabe des IDCMP-Flags VANILLAKEY per Tastendruck abzubrechen, informiert aber nur darüber, daß eine Taste gedrückt wurde, nicht welche.

Noch ein Wort zur Gestaltung des EasyRequesters: Höhe und Breite des Requesters werden von EasyRequestArgs() selbst bestimmt. Man kann ihn aber etwas großzügiger gestalten, indem man vor den Bodytext ein Linefeed setzt. Dies führt zur Einfügung einer Leerzeile ober- und(!) unterhalb des Textes. Wenn man noch mehr Kontrolle über das Aussehen des Requesters haben will, sollte man BuildSysRequest() verwenden. Durch entsprechende Werte in den hier erforderlichen IntuiText-Strukturen lassen sich auch Zeilenabstände und Font festlegen. EasyRequester verwenden immer den ScreenFont, nicht den SystemDefaultFont. Da der ScreenFont auch proportional sein kann, eignet er sich schlecht zur Ausgabe von Texten, in denen eine bestimmte Anordnung eingehalten werden soll, z.B. Tabellen. BuildSysRequest() läßt allerdings maximal nur 2 Gadgets zu.

Die Routinen zur Anzeige von Bildern sind von mir bereits ausführlich erläutert worden, erklärungsbedürftig sind zwei nachträgliche Änderungen:

  1. Vor der Öffnung des Screens wird geprüft, ob sc_depth über 8 liegt. In diesem Fall wird abgebrochen, weil mit SA_Colors32 nur maximal 256 Farben übergeben werden können. Andreas Neumann hat mir berichtet, daß es mit Grafikkarte und picture.datatype V43 ohne diese Maßnahme zu Abstürzen kam, wenn MView 24-Bit-Bilder anzeigen sollte. Eine befriedigende Lösung für dieses Problem habe ich im Moment nicht.

  2. Nach GetDTAttrs werden Informationen über das Bild gegeben. Abmessungen und Zahl der Bitplanes des Screens lassen sich leicht aus dem Bitmap-Header auslesen. Etwas schwieriger ist es, den zur DisplayID gehörigen Namen zu bekommen. Man muß zunächst die ID der Gfx-Funktion FindDisplay- Info() übergeben und bekommt dann ein "handle", das man an GetDisplay- InfoData() weiterreicht:
     GetDisplayInfoData = -$2f4  (handle,buf,size,tagID,displayID) (a0,a1,d0-d2)
    
                                buf     = Zeiger auf Zielpuffer
                                size    = Puffergröße in Bytes (=56)
                                tagID   = DTAG_NAME
                                ID      = optional, wenn handle NULL
            

    Im Erfolgsfall schreibt die Funktion eine NameInfo-Struktur in den Puffer. der Name steht dann ab Offset 16. So weit, so gut. Beim Austesten stellte ich allerdings fest, daß bei einigen Bildern kein Name geliefert wurde. Es stellte sich heraus, daß diese Bilder im HAM- oder EHB-Modus angezeigt werden. Die Lösung war, vor dem Aufruf von FindDisplayInfo() die Bits für diese Modi in der DisplayID auszumaskieren. Nach Erhalt des Namens wird festgestellt, welches der beiden Bits gesetzt war und eine entsprechende Information an den Namen angehängt.

Ein Problem taucht noch auf, wenn die erhaltenen Daten mittels RawDoFmt() formatiert werden sollen. Der hier verwendete FormatString ist:

    picinfo.fmt     dc.b    '%s: %s (%dx%dx%d) %s',LF,'%s',0
   

Im DataStream ist für jedes "%s" die Adresse eines strings, für jedes "%d" ein Wert (Word) vorzusehen. Nun kann es sein, daß für das letzte "%s" keine Adresse angegeben werden kann, weil FindDisplayInfo() bzw. GetDisplayInfo- Data() versagten. In diesem Fall muß man einen Zeiger auf einen Leerstring im DataStream eintragen, d.h. die Adresse muß auf eine Speicherstelle zeigen, die mit einem Nullbyte beginnt. Dann substituiert RawDoFmt() das "%s" mit nichts. Auf das LF(=Linefeed) würde also im formatierten string sofort das abschließende Nullbyte folgen. Keinesfalls ist NULL in den DataStream einzutragen, weil dies einen illegalen Zugriff auf Adresse 0 zur Folge hätte.

Zum Abschluß möchte ich auf die Verwendung der amigaguide.library hinweisen. Die Anzeige von Texten (ASCII, Guides), ab OS 3+ auch von anderen Dateitypen, für die DataTypes vorliegen, ist denkbar einfach. Man muß lediglich OpenAmigaGuide() eine leere NewAmigaGuide-Struktur übergeben, in die nur der Pfad der Datei einzutragen ist.

So, ich hoffe, das war jetzt nicht völlig umsonst. Bei dieser Gelegenheit möchte ich Sven Drieling grüßen. Ich war hoch erfreut darüber, daß Du meinen BOOPSI-Programmiertip aufgegriffen und erweitert hast. Die Idee, die Textzeilen über Execlisten zu verwalten, fand ich sehr interessant.

Jürgen


Zurück