AMIGA-Magazin · Ausgabe 4/00 · Programmierung: AmigaOS-GUI

Aktuelles Heft 4/00

Das zweite Gesicht

In unserem Amiga-OS-Kurs in den letzten Ausgaben des AMIGA Magazins wurde gezeigt, wie man Oberflächen für und mit dem neuen AmigaOS 3.5 programmieren kann. Nun wollen wir Ihnen zeigen, wie man auch kompatibel zu alten Betriebssystem-Versionen programmiert.

von Michael Christoph

Bei unserem Kurs zur AmigaOS 3.5 Programmierung [1] wurde stillschweigend davon ausgegangen, daß der Benutzer bereits über die aktuelle Version des Betriebssystems verfügt. Werden die Beispielprogramme unter älteren Versionen gestartet, so erscheint lediglich die Meldung, dass AmigaOS 3.5 benötigt wird. Diese Vorgehensweise ist aber nicht immer von Vorteil oder erwünscht.

Warum Programme für beide AmigaOS-Versionen erstellen?

Ohne Schnickschnack:
Miami mit einer GadTools-Oberfläche läuft auf jedem Amiga.
Für den privaten Programmierer mag es noch egal sein, dass nicht jeder Amiga-Benutzer sein Programm verwenden kann. Bei kommerziellen Firmen sieht das anders aus. Hier ist es wichtig, dass möglichst viele Kunden das Programm verwenden können. Existiert bereits ein Programm für die älteren OS-Versionen, kann einfach ein Update erstellt werden, welches auf dem aktuellen AmigaOS läuft. Kunden, die noch das ältere AmigaOS einsetzen, erhalten einfach die Vorgängerversion. Eine Weiterentwicklung der Funktionalität wird in der Praxis jedoch nur noch für die aktuelle Version vorgenommen.

Lösungsansatz 1: Das Programm unterstützt beide Oberflächen

In diesem Fall wäre sowohl die Oberfläche für das alte, als auch für das aktuelle AmigaOS vorhanden. Das Programm entscheidet zur Laufzeit, welche Oberfläche / Funktionen benutzt werden. Der Vorteil für den Benutzer ist, dass er keine unterschiedlichen Programmversionen hat. Beim Umstieg auf AmigaOS 3.5 präsentiert sich dann automatisch das »alte« Programm im »modernen« Outfit.

Nachteil dieser Methode: Der Programmcode fällt größer aus. Auch die Programmwartung ist aufwändiger, da Ergänzungen bei Bedarf für beide Oberflächen zu erstellen sind. Trotzdem dürften die Vorteile, vor allem aus Sicht des Kunden, deutlich überwiegen!

Lösungansatz 2: Es werden zwei Programme erstellt

MUI mit dabei:
Miami mit elementen des Multi-User-Interface bietet erweiterte Funktionalität
Die Alternative: Es werden zwei getrennte Programme erstellt. Je nach Kundenumgebung wird die eine oder die andere Version geliefert bzw. installiert. Aktualisiert der Kunde jedoch sein Betriebssystem, so muß entweder das Programm neu installiert oder sogar ein Programm-Update erworben werden. Wird dabei der Programmcode strikt voneinander getrennt, so sind viele Neuerungen/Verbesserungen jeweils doppelt vorzunehmen (einmal für die alte Version und einmal für die neue), was die Programmentwicklung erschwert.

Welche der beiden Lösungen verwendet wird, bleibt Ihnen überlassen. Wichtig ist jedoch in beiden Fällen, daß sauber zwischen Oberfläche und Funktionalität im Programmcode unterschieden wird. Daraus ergibt sich zumindest eine einfachere Wartung des Sourcecodes. Änderungen der Funktionalität müssen nur ein einziges Mal gemacht werden und stehen doch in beiden Programmversionen zur Verfügung. Bei Änderungen der Oberfläche kann es aber durchaus notwendig sein, beide GUI-Versionen zu aktualisieren (z.B. wenn neue Bedienelemente benötigt werden). Nicht umgehen lässt sich der Testaufwand für beide Programme/Oberflächen.

Natürlich bietet sich hier besonders C++ mit seiner objektorientierten Denkweise an. Jeweils gekappselt in eine eigene Klasse, steht nach »außen hin« das selbe Interface zur Verfügung, wenn von einer gemeinsamen Basisklasse abgeleitet wird. Natürlich ist diese Vorgehensweise auch unter ANSI-C möglich, wobei aber mehr Disziplin vom Programmierer verlangt wird.

Ein Beispiel aus der Praxis

Für Jedermann:
Unser Beispielprogramm läuft auch automatisch mit älteren Betriebssystem-Versionen.
Als Paradebeispiel für flexible und austauschbare Oberflächen soll uns das Programm »Miami« von Holger Kruse (http://www.nordicglobal.com) dienen. Hier darf der User sogar während der Laufzeit zwischen den verschiedenen Oberflächen (Intuition, MUI, ClassAct bzw. ReActor, BGUI) wechseln. Das ist natürlich der eleganteste Weg, der aber aus Programmierersicht eine Menge zusätzlicher Arbeit erfordert.

Damit Miami mit steigender Anzahl unterstützter Oberflächen nicht immer größer wird, wird auf das bewährte Library-Konzept zurückgegriffen. Die komplette Oberfläche inklusive deren Messagehandling ist in einer Library untergebracht. Es existiert ein gemeinsames Interface (Funktions-Einsprünge), über dass das Programm mit der Oberfläche »kommuniziert«. Unser Beispiel in diesem Artikel wird nicht so ausgefeilt sein. Es wird einfach per Compiler-Define festgelegt, welche Oberfläche »ausgespuckt« werden soll. Aus den im letzten Kapitel erwähnten Gründen greifen wir auf C++ als Programmiersprache zurück. Alle modernen Compiler (StormC, MaxonC, GnuC) unterstützen die Programmentwicklung in C++. Das Programm dient natürlich nur als Anschauungsgrundlage. Erweiterungsmöglichkeiten wären z.B. die Auswahl der Oberfläche durch den Benutzer. Das setzt natürlich voraus, dass alle GUIs im Programmcode bzw. extern vorhanden sind.

Weitere Aspekte bei der Entwicklung

Bisher wurde die Programmunterscheidung lediglich aus Blickpunkt der Oberfläche betrachtet. Nimmt man allerdings auch die anderen Unterschiede zwischen AmigaOS 3.1 und 3.5 in Augenschein, so ergeben sich weitere Probleme, die hier allerdings nur kurz angesprochen werden können.

Mit neuem Outfit:
Das Beispielprogramm unter AmigaOS 3.5 - beim Start wird das neue Betribssystem erkannt und genutzt.
Im einfachsten Fall sind die Unterschiede lediglich zusätzliche Tags, z.B. in der asl.library. Wird das Programm unter älteren Betriebssystemversionen eingesetzt, stehen diese Funktionen neuerer Bibliotheken nicht zur Verfügung. Eine wirkliche Unterscheidung ist allerdings nicht notwendig, da die neuen Tags in der früheren asl-Version einfach ignoriert werden. Zu beachten ist lediglich, dass auch die alte Version bei OpenLibrary() verlangt werden muss.

Betrachtet man hingegen die Unterschiede in der workbench- und icon. library, muss eine Unterscheidung im Programmcode stattfinden. In der Regel sind die neuen Funktionen nicht in »Allerwelts-Programmen« notwendig. Allerdings ist es natürlich schöner, wenn das Programm sein Konfigfile mit den neuen »GlowIcons« speichert und nicht mit den alten grauen Symbolen. Die Vorgeheisweise hierbei ist allerdings einfach: Statt wie bisher GetDiskObject() aufzurufen, wird die neue Funktion GetIconTagList() dazu verwendet. Damit lädt man das Icon. Das gilt auch bei PutIconTagList() ­ diese Funktion kommt an Stelle von PutDiskObject() zum Einsatz, um das Icon zu speichern.

Andere Funktionen (z.B. icon.library/DrawIconStateA()) zum Zeichnen eines Icons als Image, müssen unter älteren Versionen nachgebildet werden. Also Icon laden, die Imagedaten entnehmen und an intuition.library/DrawIcon() übergeben. In allen Fällen muß die minimale Libraryversion (meist 37 für AmigaOS 2.04 oder 39 für AmigaOS 3.0) geöffnet werden und erst zum Ausführungszeitpunkt die Unterscheidung stattfinden. Das Gerüst in Listing 1 zeigt die Vorgehensweise.

struct Library *IconBase; 
struct DiskObject *diskobj; 
if(IconBase = OpenLibrary("icon.library",37))
{ if(IconBase->lib_Version < 44) 
    /* alte AmigaOS-Version */ 
    diskobj = GetDiskObject("dateiname"); 
  else 
    /* neue AmigaOS-Version */ 
    diskobj = GetIconTagList("dateiname",NULL); 

  if(diskobj) 
  {  /* tu was ! */
  }
  CloseLibrary(IconBase); 
} 
else 
  printf("FEHLER: icon.library "  
         "V37 kann nicht geöffnet werden.\n");

Listing 1: Icons richtig einbinden

AmigaOS 3.5 bietet aber auch ganz neue Libraries, wie die aml.library (Amiga-Mail-Library mit E-Mail-Funktionalität) und die hdwrech.library (Festplatten-HighLevel-Bearbeitung). Wird deren Funktionalität benötigt, so muss in der Regel auch die AmigaOS-3.5-Umgebung vorhanden sein. Eine Nachbildung der einzelnen Funktionen unter älteren Betriebssystemen ist normalerweise unverhältnismäßig aufwändig. Steht keine ähnliche Bibliothek (z.B. aus dem Public-Domain-Bereich) für die älteren Betriebssysteme zur Verfügung, ist der vermutlich einzige vernünftige Weg, als Programmvorgabe AmigaOS 3.5 zu verlangen.

Die Installation eines gemischten Programms

Abschließend soll noch kurz ein Blick auf die Installation und die dabei notwendige Unterscheidung geworfen werden. Hierbei wird davon ausgegangen, dass der Standard-Installer (bzw. dessen kompatible Äquivalente) eingesetzt werden. Das Erstellen von Installerscripts wurde bereits im Installer-Kurs [2] ausführlich erklärt und hier nur Ausschnittsweise angesprochen.
(if (< (/ (getversion "exec.library" (resident)) 65536)) 37)
  (abort "Es wird mind. AmigaOS 2.04 benötigt !").

(if (< (/ (getversion "workbench.library") 65536)) 44)
  (abort "Es wird mind. AmigaOS 3.5 benötigt !").

Bzw. ein "=" Vergleich, wenn genau eine Version zutreffen muß.

Listing 2: Ermitteln der vorhandenen OS Version

Während mit der ersten vorgestellten Lösung nur ein einziges Programm vorhanden ist, muss auch nur dieses installiert werden (per copyfiles-Anweisung). Werden z.B. unterschiedliche Iconsets geliefert (alte Icons/Glow Icons), so kann automatisch die korrekte Version installiert werden oder eine einfache Benutzerauswahl vorgenommen werden (per askchoice-Anweisung). Wird nur eine OSspezifische Version geliefert, muß bei der Installation das Vorhandensein dieser OS-Version überprüft werden ­ in Listing 2 finden Sie zwei Beispiele. Werden hingegen alle Versionen geliefert, muß bei der Installation die korrekte Version ermittelt werden und nur diese darf installiert werden. Es ist eine einfache IF-ELSE-Unterscheidung vorzunehmen (siehe Listing 3).
(set #OS_Version (/ (getversion "workbench.library") 65536))
(if (< #OS_Version 44) (*installieren Gadtools Oberfläche*)
                       (*installieren Reaction Oberfläche*)
)

Listing 3: Ermitteln der zu installierenden Version

Bei mehreren mitgelieferten (externen) Oberflächenmodulen, sollte wieder der Benutzer auswählen können, welche er installieren will. Dazu nutzt der Programmierer die askchoice- oder die askoptions-Anweisung.

Das Beispielprogramm

Als anschauliches Beispiel dient diesmal die Oberfläche für das Perl-Script »txt2pdf« [3]. Damit können einfache Textdateien in das PDF-Format umgewandelt werden. Dieses Format wird vor allem für Druckvorlagen (z.B. von Handbüchern) verwendet. Die aktuelle Version ist unter der URL http://www.sanface.com zu finden.

Wie bereits oben angesprochen, liegen die Oberflächen als einzelne C++-Klassen vor. Anhand der Defines GUI_GADTOOLS und GUI_REACTION wird definiert, welche Oberfläche das erzeugte Programm verwenden soll; bzw. sind beide Defines gesetzt, kann der Benutzer die Oberfläche auswählen oder wird automatisch beim Programmstart ermittelt. Durch einfaches Hinzufügen weiterer Defines und C++-Klassen können auch andere Oberflächen, z.B. MUI oder BGUI, eingebunden werden.

Die Basis-Klasse
bool InitGUI();
Sie wird aufgerufen, nachdem die Libraries geöffnet und der Bildschirm ermittelt wurde. Das GUI-Modul kann nun zusätzliche Libraries öffnen oder andere Vorbereitungen treffen. Returnwert true, um fortzufahren.

void ExitGUI();
Aufruf ganz am Ende, wobei allocierte Resourcen freizugeben sind (z.B. GUI-spezifische Libraries schließen).

bool CreateGui();
Der Aufruf soll die komplette Oberfläche erzeugen. Es werden also z.B. die GadTools-Gadget allokiert und verkettet oder die ReactionObjekte generiert. Return-Wert true, um fortzufahren.

void DestroyGUI();
Wird am Ende, aber vor ExitGUI() aufgerufen, um die Oberflächenelemente freizugeben.

ULONG MessageLoop();
Diese Funktion ist das eigentliche Herzstück des Programms. Sie läuft so lange, bis der Benutzer das Programm beendet. Erst dann kehrt sie mit dem Return-Wert für den Shellaufruf zurück. Die Funktion kümmert sich darum, dass die Benutzeraktionen (z.B. Gadget- oder Menü-Auswahl) festgestellt und verarbeitet werden. Eine Alternative wäre, nur das Signalbit für die Oberfläche zu erfragen und im Hauptprogramm z.B. mit anderen Signalen verodert zu warten. Damit könnten dann auch weitere Aktionen (z.B. Timer, ARexx) berücksichtigt werden. Die eigentliche Nachrichtenbehandlung müsste dann wieder über eine eigene GUI-spezifische Funktion erfolgen.
Die »veraltete« Gadtools-Oberfläche wurde mit der GadToolsBox von Jan van den Baard erstellt. Der geringfügig nachbearbeitete Code wurde dann in die myGUI_Gadtools-Klasse »verstrickt«. Das Programm wird dadurch fontsensitiv, das Fenster aber nicht größenänderbar.

Auf die Erstellung der »modernen« Reaction-Oberfläche wurde bereits im ersten Teil des Inspektor-Gadget-Kurses [1] eingegangen und auf deren Programmierung im zweiten Teil [4]. Die zutreffenden Programmteile sind in der myGUI_Reaction-Klasse enthalten. Sie unterstützen neben der Größenänderung des Fensters auch die neue Iconify-Funktion. Bei der GadTools-Umgebung wurde auf die Nachbildung der Iconify-Funktion verzichtet. Zumindest für das eigentliche Iconify-Image gibt es bereits eine fertige BOOPSI-Klasse im Aminet (s. Kasten »Online-Ressorucen«). Das eigentliche Fenster schließen und AppIcon für die Workbench erzeugen, müsste dann noch ergänzt werden. Die einzelnen Dateien sind natürlich mit den aktuellen Include-Dateien des AmigaOs-3.5-DevPack zu kompilieren. Als Basisklasse dient myGUIClass, welche fünf virtuelle Funktionen enthält. Dadurch könnten z.B. Gemeinsamkeiten aller GUIs bereits in der Basisklasse behandelt werden. In unserem Beispiel ist die Basisklasse leer, und nur die Ableitungen enthalten die Funktionalität. Im Kasten »Die Basis-Klasse« finden Sie eine kurze Vorstellung der fünf Funktionen und ihre Funktionsweise.

Hinweis: Das Programm enthält teilweise doppelt ausgeführte Teile! Hier ist es in der Praxis empfehlenswert, eine zusätzliche Datei zu erzeugen und dort die Funktionen unterzubringen, die von allen Oberflächenmodulen benutzt werden können (z.B. die Menü-Behandlung). Das Programm und den Sourcecode finden Sie auf der Homepage des Autors. Sollten Fragen zum Artikel oder dem Programm auftreten, können Sie sich gerne per E-Mail (michael@meicky-soft.de) an den Autor wenden.

Online-Resourcen
Homepage des Autors http://www.meicky-soft.de
GadToolsBox Aminet dev/gui/gadtoolsbox20c.lha
titlebar.image
(Massimo Tantignone)
http://www.intercom.it/~amigaws
titlebar Aminet dev/gui/titlebar_ic.lha
txt2pdf/Sanface http://www.sanface.com
Literatur:

[1] Michael Christoph, Inspektor Gadget (Teil 1), AMIGA-Magazin 12/1999, Seite 24,
[2] Marcel Bennicke, Installer-Scripts erstellen, AMIGA-Magazin 3 bis 5/1997
[3] Jörn-Erik Burkert, Skript-Schätze, AMIGA-Magazin 11/1999, S. 24
[4] Michael Christoph, Inspektor Gadget (Teil 2), AMIGA-Magazin 1/2000, Seite 25,
[5] Includes und Autodocs für AmigaOS 3.5 sind zu finden auf der DeveloperCD 2.1,
erhältlich z.B. bei Haage & Partner

 


 Hauptseite © 2000 All Rights Reserved. Alle Rechte vorbehalten Franzis' Verlag GmbH
Veröffentlichung und Vervielfältigung nur mit schriftlicher Genehmigung des Verlags

Kommentare, Fragen, Korrekturen und Kritik bitte an Webmaster AMIGA schicken.
Zuletzt aktualisiert am 01. Juni 2000 und 03. Juni 2006, Michael Christoph.