Home

Index Zurück Weiter


6 Programmierung der CDiC-Bibliothek

Im Kapitel 5 wurde das CD-i-Authoring vorgestellt. Nun werden wir die im Abschnitt 5.6 erwähnte CDiC-Bibliothek präsentieren, deren Entwicklung über ein Jahr benötigte. An dieser Stelle kann keine genaue Erklärung der über 400 Funktionen in der etwa 15'000 Zeilen C-Code umfassenden Bibliothek geboten werden. Vielmehr soll sie anhand eines Überblicks und einiger konkreter Algorithmen und Datenstrukturen soweit erläutert werden, wie dies zum Verständnis der Implementationsbeispiele im Kapitel 7 notwendig ist. Im folgenden werden C-Kenntnisse [231] vorausgesetzt.

6.1 Programmierstil

Zuerst muss der richtige Programmierstil einer CD-i-Applikation gefunden werden. Folgende Möglichkeiten bieten sich an:

  1. Skript-Interpreter
  2. Ereignisorientierte Programmierung

6.1.1 Skript-Interpreter

Bei dieser Variante wird die CD-i-Applikation in Form eines Skriptes auf der CD-i-Scheibe gespeichert. Ein Interpreter liest dieses Skript und führt die einzelnen Befehle aus, indem er Aufrufe in eine CD-i-Bibliothek durchführt. Dieses Prinzip wird in der Abbildung 6-1 dargestellt.

Abbildung 6-1: Schema eines CD-i-Skript-Interpreters

Ein derartiger Interpreter, der die Bilder einer Diashow wiedergeben kann, wurde am IAM als Prototyp implementiert. Die Weiterentwicklung dieses Prototyps wurde abgebrochen, da sich zeigte, dass die notwendige Geschwindigkeit zur Einhaltung der Echtzeitanforderungen nicht erreicht wird.

6.1.2 Ereignisorientierte Programmierung

Viele GUI [232]-Systeme sind so aufgebaut, dass sie durch die Übermittlung von Ereignissen gesteuert werden. Diese Idee wird übernommen, um die in Abbildung 6-1 dargestellte CD-i-Bibliothek zu verbessern. Die Verwendung dieser Weiterentwicklung, wir nennen sie CDiC-Bibliothek, wird in der Abbildung 6-2 präsentiert.

Abbildung 6-2: Verwendung der CDiC-Bibliothek

Die in ANSI-C geschriebene CD-i-Applikation verwendet die in Abbildung 5-7 dargestellten C-Deklarationsdateien und die erzeugten statischen Daten. Die CDiC-Bibliothek vereinfacht die Programmierung der Applikation, indem sie verschiedene CD-i-spezifische Klassen [233] und solche zur GUI-Programmierung zur Verfügung stellt. Die Kommunikation zwischen der Applikation und der CDiC-Bibliothek wird sowohl durch Funktionsaufrufe als auch durch die Übermittlung von Ereignissen durchgeführt.

Die CDiC-Objekte [234] kommunizieren untereinander durch Funktionsaufrufe, durch Ereignisaustausch oder durch das Aussenden von Signalen. Als Schnittstelle zum CD-RTOS-Betriebssystem wird die in Abbildung 5-19 dargestellte 'CDi.l'-Bibliothek von Microware verwendet. Einige (in Abbildung 6-2 nicht aufgeführte) Teile der CDiC-Bibliothek sind direkt in Assembler geschrieben. Alle CD-RTOS-Signale werden von der CDiC-Bibliothek abgefangen, interpretiert und falls nötig als Ereignis an die Applikation übermittelt.

In der CDiC-Bibliothek wird die folgende Schreibweise verwendet:

In den nun folgenden Kapiteln werden einige der CDiC-Klassen vorgestellt.

6.2 Klassenübersicht

6.2.1 CdiDispatcher

Der CdiDispatcher ist als Verteiler der Ereignisse ein sehr zentraler Teil der CDiC-Bibliothek. Er verwaltet alle ein- und ausgehenden Ereignisse, indem er sie in einer Dispatcherliste einfügt. Später entfernt er sie wieder aus der Liste und bearbeitet die Ereignisse.

Einige seiner wichtigsten Funktionen sind:

Ein Element der Dispatcherliste ist dabei wie folgt definiert:

// define CdiEventFct as a pointer to an event function
typedef void (*CdiEventFct) (CdiInt event, void* parameter);
typedef struct {
  CdiEventFct eventFct;     // the event function
  CdiInt event;       // the event
  void* parameter;      // the function parameter
} CdiDispatcherElement;

Es besteht also aus einer Ereignisfunktion (eventFct), einem Ereignis (event) und einem beliebigen Parameter. Bei der Bearbeitung eines solchen Elementes führt der CdiDispatcher die Ereignisfunktion unter Angabe des Ereignisses und des Parameters aus.

Unter einem Ereignis wird ein 4 Byte langer Integerwert verstanden, der in einen Typ-, einen Klassen- und einen Informationsteil aufgeteilt wird.

Der Typ eines Ereignisses definiert meist den logischen Urheber. Dies wird im folgenden Ausschnitt aus Datei CDiEvent.h veranschaulicht.

...
#define CDi_EVENT_TYPE_FILE 0xfa000000
#define CDi_EVENT_TYPE_XV_DEVICE 0xf8000000
...

In diesem Beispiel werden die zwei logischen Urheber FILE und XY_DEVICE definiert. Im Klassenteil wird meist der konkrete Urheber eines Ereignisses angegeben.

...
#define CDi_EVENT_CLASS_FILE (CDi_EVENT_TYPE_FILE + (1 << 16))
#define CDi_EVENT_CLASS_FILE_PCL (CDi_EVENT_TYPE_FILE + (2 << 16))
#define CDi_EVENT_CLASS_POINTER (CDi_EVENT_TYPE_XV_DEVICE + (1 << 16))
#define CDi_EVENT_CLASS_HOTSPOT (CDi_EVENT_TYPE_XV_DEVICE + (2 << 16))
...

Die Ereignisklassen FILE und FILE_PCL gehören beide zum Typ FILE, während POINTER und HOTSPOT dem Typ XV_DEVICE zugeordnet werden. Im folgenden Codeauszug werden einige Informationsteile der Ereignisklasse FILE wiedergegeben.

#define CDi_EVENT_FILE_PAUSE_ERROR (CDi_EVENT_CLASS_FILE + 4)
#define CDi_EVENT_FILE_PAUSE_OK (CDi_EVENT_CLASS_FILE + 5)
#define CDi_EVENT_FILE_PLAY_ABORT (CDi_EVENT_CLASS_FILE + 6)
#define CDi_EVENT_FILE_PLAY_ERROR (CDi_EVENT_CLASS_FILE + 7)
#define CDi_EVENT_FILE_PLAY_OK (CDi_EVENT_CLASS_FILE + 8)
#define CDi_EVENT_FILE_SEEK_ERROR (CDi_EVENT_CLASS_FILE + 9)
#define CDi_EVENT_FILE_SEEK_OK (CDi_EVENT_CLASS_FILE + 10)
#define CDi_EVENT_FILE_STOP_ERROR (CDi_EVENT_CLASS_FILE + 11)
#define CDi_EVENT_FILE_STOP_OK (CDi_EVENT_CLASS_FILE + 12)

Beispielsweise wird, nachdem die Wiedergabe einer RTF-Datei erfolgreich beendet wurde, das Ereignis CDi_EVENT_FILE_PLAY_OK an die Applikation übermittelt. Im Informationsteil der Ereignisklasse FILE_PCL wird die Kanalnummer der RTF-Datei gespeichert, in der ein PCL-Signal [236] empfangen wurde.

Die CdiDispatcher-Funktion cdiDispatcherLoop wird nach dem Initialisierungsteil der CD-i-Applikation aufgerufen. Sie wartet darauf, dass neue Ereignisse eintreffen, die bearbeitet werden sollen.

extern void cdiDispatcherLoop(void) { 
  CdiDispatcherElement element; 

  for (;;) {        // endless loop
    if (countCdiDispatcherList() > 0) { // if there is an
      element CDi_IRQ_LOCK;   // no IRQ allowed
      element = itemCdiDispatcherList(0); // get the first element
          
      removeCdiDispatcherList(0); // remove it from the list 
      CDi_IRQ_UNLOCK;     // now allow IRQ
      element.eventFct(element.event, element.parameter); // call it
    }
  }
}

Diese Funktion wird also nicht beendet, sondern kann erst durch den Abbruchbefehl exit() verlassen werden. Neue Ereignisse finden ihren Weg in die Dispatcherliste, indem entweder eine Ereignisfunktion selber neue Ereignisse mit cdiDispatch() einfügt oder ein Signal empfangen wird, welches von der Signalbehandlungsprozedur in ein Ereignis übersetzt wird.

6.2.2 CdiSignal

Wie schon im Abschnitt 4.4.8.1 erwähnt, sind CD-RTOS-Signale 2 Byte lange Integerwerte. Die Klasse CdiSignal verwaltet diese Signale, indem sie sich als Empfänger beim CD-RTOS-Betriebssystem registriert und beim Auftreten eines Signals die richtige Signalbehandlungsprozedur aufruft. Die Bearbeitung der Signale ist extrem zeitkritisch, da sich der Prozessor während dieser Zeit im sogenannten system state mode befindet und keine anderen Signale bearbeiten kann. Das Betriebssystem speichert die Signale in einer Warteschlange, die jedoch schnell überlaufen kann. Da die Signale meist zur Synchronisation verwendet werden, sollte die Speicherung der Signale in dieser Warteschlange möglichst vermieden werden. Daher werden die meisten Signale durch die Behandlungsprozeduren in entsprechende Ereignisse übersetzt und dem CdiDispatcher übergeben, der sie anschliessend im normalen user state mode des Prozessors bearbeitet.

Ähnlich wie die Ereignisse wird die 2 Byte CD-RTOS-Signalnummer in eine Signalgruppe und eine Signalnummer aufgeteilt. Die Gruppe definiert wieder den Urheber des Signals, wie beispielsweise CdiFile, CdiAudio, CdiPointer oder CdiTimer.

Einige der wichtigsten Funktionen aus CdiSignal sind:

Die im folgenden Auszug der Datei CdiSigna.h und in der Abbildung 6-3 präsentierten Datenstrukturen ermöglichen eine sehr schnelle Bearbeitung der CD-RTOS-Signale.

// define CdiSignalExecutemode as CdiInt (the compiler does not handle enums)
typedef CdiInt CdiSignalExecuteMode;
typedef struct {
CdiEventFct eventFct; // the event function
void* parameter; // the function parameter
CdiSignalExecuteMode executeMode; // call it using execute mode
}CdiSignalElement;
typedef struct {
CdiInt size; // size of signalarray
CdiSignalElement* enabledSignals; // signalarray
CdiSignalElement defaultSignalElement; // default grp element
}CdiSignalDescripor;

Für jede Signalgruppe wird ein CdiSignalDescripor-Element erzeugt, welches sich aus einem Standardelement (defaultSignalElement) und einem Zeiger auf eine Liste (enabledSignals) zusammensetzt, in der die entsprechenden Ereignisfunktionen mit ihren Parametern gespeichert werden. Die executeMode-Variable definiert, ob die Ereignisfunktion mit cdiDispatchFirst() oder cdiDispatch() dem CdiDispatcher übergeben werden soll. Sehr zeitkritische Funktionen können auch direkt im system state mode ausgeführt werden. Da diese auf eine spezielle Art compiliert werden müssen [237], wird diese Option so wenig wie möglich verwendet.

Der zentrale Algorithmus zur Signalbehandlung wird mit Hilfe des folgenden Codeauszuges aus der Datei CdiSySta.c und der Abbildung 6-3 dargestellt.

// the intercept function
extern CdiInt cdiSystemStateInterceptHandler(CdiInt signalNumber)
{
CdiSignalElement* signalElement; // the current signal element
CdiSignalDescripor* descriptor; // the current descriptor
CdiInt groupId; // current groupId
CdiInt signalId; // current signalId
groupId = CDi_SIGNAL_GET_GRP_ID(signalNumber); // get the current groupId
signalId = CDi_SIGNAL_GET_SIG_ID(signalNumber); // get the current signalId
// get the current signal descriptor and element
descriptor = cdiGlobalsGetSignalDescripor(groupId);
signalElement = &(descriptor->enabledSignals[signalId]);
if ((signalElement->eventFct) == CDi_NULL) { // if no function is given
cdiExecuteSignalElement(&(descriptor->defaultSignalElement), signalNumber); // execute default function
else {
cdiExecuteSignalElement(signalElement, signalNumber);
}
}
static void cdiExecuteSignalElement(
CdiSignalElement* signalElement,
CdiInt signalNumber)
{
switch (signalElement->executeMode) { // get execute mode
case CDi_SIGNAL_EXCECUTE_IN_SYSTEM_STATE: // use system state mode
signalElement->eventFct(
CDi_EVENT_CLASS_SIGNAL_SUPERVISOR | signalNumber,
signalElement->parameter); // add signal number
break;
case CDi_SIGNAL_EXCECUTE_FIRST: // use cdiDispatchFirst
cdiDispatchFirst(CDi_EVENT_CLASS_SIGNAL_IMMEDIADTE | signalNumber,
signalElement->eventFct,
signalElement->parameter);
break;
case CDi_SIGNAL_EXCECUTE_DISPATCHED: // use cdiDispatch
cdiDispatch(CDi_EVENT_CLASS_SIGNAL_DISPATCHED | signalNumber,
signalElement->eventFct,
signalElement->parameter);
break;
}
}

Abbildung 6-3: Struktur der Signalelemente

Im Beispiel der Abbildung 6-3 sind 15 Signalgruppen mit cdiSignalGroupCreate() generiert worden. Für jede dieser Gruppen wurden das Standardelement (Def0 bis Def15) und ein Zeiger auf eine Elementliste gesetzt. Die einzelnen Listeneinträge (El0,0, El0,1, ...) wurden unter Verwendung der Funktion cdiSignalEnable() korrekt initialisiert.

Sendet CD-RTOS das Signal '513', dann wird es sofort in seine Signalgruppe '2' und die Signalnummer '1' [238] zerlegt. Anhand dieser zwei Indizes kann das Element El2,1 direkt, d.h. ohne langwieriges Durchlaufen der Liste, referenziert und die Ereignisfunktion entsprechend ihres Ausführungsmodus bearbeitet werden.

Als nächstes soll nun das Signal '260' empfangen werden. Der Algorithmus erkennt, dass das Signalelement El1,4 nicht initialisiert ist. Nun wird das Standardelement Def1 mit der Signalnummer '4' im Informationsteil des Ereignisses dem CdiDispatcher übergeben. Auf diese Art werden beispielsweise die 32 möglichen PCL-Signale [239] übermittelt, ohne dass alle 32 Elemente generiert werden müssen.

6.2.3 CdiFile

Eine weitere wichtige Klasse ist CdiFile, die als Schnittstelle zwischen der CD-i-Applikation und den RTF-Dateien dient. Die folgende Abbildung 6-6 zeigt das Zusammenspiel von CdiFile mit den anderen Klassen der CDiC-Bibliothek und der CD-i-Applikation.

Abbildung 6-4: Einbettung der Klasse CdiFile in CDiC

Die Klasse CdiFile stellt der Applikation die folgenden Funktionen zur Verfügung:

CD-RTOS vergibt für alle geöffneten Dateien und Geräte sogenannte Pfade. Die Klasse CdiPath verwaltet diese CD-RTOS-Pfade mit den folgenden Funktionen:

6.2.4 CdiSoundmap

Die Wiedergabe von Tönen aus dem RAM des CD-i-Gerätes wird durch die Klasse CdiSoundmap gesteuert.

Abbildung 6-5: Einbettung der Klasse CdiSoundmap in CDiC

Im folgenden werden einige der wichtigsten Funktionen aufgelistet, die der Applikation durch CdiSoundmap zur Verfügung gestellt werden:

Die von CdiSoundmap verwendete Klasse CdiAudio bildet die Schnittstelle zum Audioprozessor und wird von der Applikation nicht direkt verwendet. Einige der Funktionen aus CdiAudio sind:

6.2.5 CdiScreen

Die Videoausgabe wird durch die Klasse CdiScreen kontrolliert. Sie schirmt die Applikation von den FCT- und den LCT-Tabellen komplett ab. Der Bildschirm wird aus mehreren Bildstreifen [240] in den beiden Bildebenen [241] aufgebaut. Die CD-i-Applikation muss mit cdiSubScreenCreate() derartige Streifen erzeugen, die daraufhin mit cdiScreenAppend() der Klasse CdiScreen übergeben werden. Anschliessend kann ein Bild mit cdiScreenShow() dargestellt werden. Um bei der Definition der Bildstreifen die dynamische Speicherreservation zu vermeiden [242], kann die Applikation zuvor CDiC-Speicherblöcke mit der Klasse CdiMemory erzeugen. Diese werden in der Initialisierungsphase der Applikation angefordert und erst bei Beendigung wieder freigegeben.

Abbildung 6-6: Einbettung der Klasse CdiScreen in CDiC

Einige der Funktionen aus der Klasse CdiScreen sind:

Abbildung 6-7: Darstellung der Bildstreifen auf dem Bildschirm

Da CD-RTOS viele der benötigten Bildmanipulationen, wie beispielsweise das Verschieben von Bildteilen, nicht unterstützt, werden verschiedene Bildfunktionen implementiert. Im folgenden werden die wichtigsten Funktionen der Klasse CdiPicture aufgelistet:

Mit Hilfe der Klassen CdiFct und CdiLct können die DCP-Befehle in die FCT- und LCT-Tabellen unter Verwendung eines Puffers [244] geschrieben werden. Die Verwaltung gestaltet sich etwas heikel, da nicht alle DCP-Befehle in beiden Bildebenen zur Verfügung stehen [245]. Für einige Argumente der DCP-Befehle müssen Informationen aus beiden Bildebenen angegeben werden, so etwa für den Befehl Codingmethod, der nur in einer LCT-Tabelle der Ebene 'A' geschrieben werden kann. Dieser Befehl definiert aber die ab einer bestimmten Zeile verwendeten Bildformate [246] beider Bildebenen. Die Weitergabe der richtigen Befehlsparameter ist Aufgabe der Klasse CdiScreen, da sie alle benötigten Informationen ihrer Bildschirmliste entnehmen kann.

Die wichtigste Funktion in der Klasse CdiScreen ist cdiScreenShow(), deren Algorithmus hier präsentiert wird.

// show subScreen stack of both planes
// set both lcts
cdiScreenShow( CdiScreen* screen)
{
reset lcts and fcts
// scan all lines
for (scanLine = 0; scanLine < lastScreenLine; scanLine++) {
if (new subScreen at scanLine position in plane A or B is used) {
set lcts at scanLine position // add dcp commands to buffer
}
}
if CLUT is used install CLUT in fcts
write lcts and fcts
}

Zuerst werden die FCT- und LCT-Puffer beider Bildebenen vollständig initialisiert, damit sie neu aufgebaut werden können. Mit einer Scanline wird daraufhin in einer Schlaufe jede Bildschirmzeile durchsucht, ob in der Bildschirmliste ein in dieser Zeile beginnender Bildstreifen gespeichert ist. Falls ja, dann werden in die LCT-Tabellen beider Bildebenen die richtigen DCP-Befehle geschrieben. Nachdem alle Bildschirmzeilen durchsucht wurden, wird bei Bedarf die Farbtabelle in die richtige FCT-Tabelle installiert und anschliessend werden die DCP-Puffer an CD-RTOS übermittelt.

6.2.6 CdiHotspot

Eine weitere Klasse ist CdiHotspot. Sie ermöglicht es, auf dem Bildschirm verschiedene 'heisse' Bereiche zu definieren. Befindet sich der Cursor über einem solchen Bereich, dann kann er eine spezielle Form annehmen. Diese CdiHotspots übernehmen ausserdem die Umsetzung der Mausaktionen des Benutzers in CDiC-Ereignisse. Die folgende Abbildung 6-8 zeigt eine Anordnung dreizehn solcher Bereiche, wie sie im CIMEDIA-Lehrprogramm für die Flugansicht aufgebaut wird [247].

Abbildung 6-8: Anordnung einiger CdiHotspots auf dem Bildschirm

Im oben gezeigten Beispiel wird mit H1 ein CdiHotspot definiert, der den ganzen Bildschirm ausfüllt. H2 und H3 werden zur Definition zweier Schaltflächen in der rechten Hälfte des Bildschirmes verwendet. Die Bereiche H4 bis H13 verdeutlichen, dass sich die CdiHotspots überlagern dürfen. Für jeden solchen Bereich kann eine eigene Cursorform bestimmt werden. So wird der Mauszeiger in den Bereichen H1 bis H4 die Form eines Zeigers und in H5 bis H13 die einer Lupe annehmen.

Die Priorität der CdiHotspots wird ähnlich wie bei den Bildstreifen durch die Reihenfolge ihrer Erzeugung bestimmt. Der zuletzt erzeugte CdiHotspot hat gegenüber seinen Vorgängern immer den Vorrang, d.h. er ist im Vordergrund. Würde der Bereich H5 vor H4 generiert, dann hätte er gegenüber H4 die tiefere Priorität und würde, da vollständig überdeckt, nicht berücksichtigt werden.

Die in Abbildung 6-9 dargestellte CdiHotspot-Liste ermöglicht die schnelle Implementierung des im nächsten Absatz erklärten Suchalgorithmus.

Abbildung 6-9: Aufbau der CdiHotspot-Liste

Der Algorithmus, der aufgrund der x/y-Koordinaten des Eingabegerätes den aktuellen CdiHotspot finden muss, durchläuft diese Liste, bis er einen Bereich gefunden hat, von dem aus er weder in derselben Ebene nach rechts noch eine Ebene tiefer einen Bereich finden kann, der die Koordinaten einschliesst. Dieser CdiHotspot-Suchalgorithmus ist in CdiPointer implementiert. Er verwendet zusätzlich zu der oben erwähnten CdiHotspot-Liste auch noch einen CdiHotspot-Baum.

CdiHotspot* cdiPointerGetCdiHotspot(x,y)
{
for (all enabled hotspotLists in hotspotTree list) {
hotspot = last enabled and x,y enclosing list element on highest level
if (hotspot <> NULL) return(hotspot)
}
return(NULL)
}

Der in Abbildung 6-10 präsentierte CdiHotspot-Baum (hotspotTree im obigen Algorithmus) fasst mehrere CdiHotspot-Listen zusammen, so dass sehr schnell zwischen verschiedenen aktiven Listen gewechselt werden kann. Die kann beispielsweise zur Implementierung verschiedener Menü- und Symbolleisten [248] verwendet werden.

Abbildung 6-10: Aufbau des CdiHotspot-Baumes

Sowohl für jede Liste als auch für jedes Listenelement kann ein disable Attribut gesetzt werden. Dieses Attribut bewirkt, dass der oben beschriebene Suchalgorithmus die entsprechende Liste (respektive das Listenelement) bei der Suche nicht berücksichtigt. So können kurzzeitig einzelne CdiHotspots oder ganze Listen ausgeblendet werden, ohne sie aus dem CdiHotspot-Baum entfernen zu müssen.

Wenn der Benutzer das X/Y-Eingabegerät bewegt, dann wird ein CD-RTOS-Signal ausgesendet. Die Klasse CdiPointer, die das Eingabegerät verwaltet, registriert sich für dieses Signal bei CdiSignal, so dass die folgende Funktion im system state mode aufgerufen wird.

cdiSystemStatePointerHandler(CdiInt event, void* parameter)
{
get x,y coordinates of pointer
set coordinates of cursor
remove all cdiPointerHandler functions from dispatcher
dispatch cdiPointerHandler
}

Diese Signalroutine wird bei jeder Bewegung des Mauszeigers aufgerufen. Daher dürfen nur die unbedingt notwendigen Operationen durchgeführt werden. Zuerst werden die aktuellen x/y-Koordinaten des Eingabegerätes abgefragt und daraufhin wird der graphische Cursor auf dem Bildschirm an diese Koordinaten positioniert. Alle zur weiteren Behandlung der Mausaktionen erforderlichen Befehle werden via CdiDispatcher in einer Ereignisfunktion (cdiPointerHandler) ausgeführt.

cdiPointerHandler(CdiInt event, void* parameter)
{
hotspot = cdiPointerGetCdiHotspot(x,y) // get current hotspot
cdiCursorShow(hotspot->cdiCursor) // show cursor
if state of hotspot has changed // handle hotspot state
cdiHotspotHandler(hotspot)
}

Die Ereignisfunktion cdiPointerHandler() sucht den aktuellen CdiHotspot, setzt die durch ihn definierte Cursorform und führt eine Funktion (cdiHotspotHandler) aus, die zur Bearbeitung des aktuellen Status des CdiHotspots dient. Das Drücken des linken Aktionsknopfes auf dem X/Y-Eingabegerät ändert den Status in BUTTON_0_DOWN, das Loslassen bewirkt den Übergang in den Status BUTTON_0_UP.

Zusätzlich existieren noch die folgenden Stati:

Der aktuelle CdiHotspot-Status wird von der Funktion cdiHotspotHandler bearbeitet. Zuerst wird eine Ereignisfunktion (hotspot->handler()) dem CdiDispatcher übergeben, die dafür sorgt, dass sich der CdiHotspot korrekt auf dem Bildschirm darstellt. Diese Zeichnungsfunktionen werden in den zur GUI-Programmierung eingesetzten CdiControl-Klassen definiert. Danach wird eine vom Status abhängige Funktion (currentStateHandler() ) in die CdiDispatcher-Liste eingetragen, die dafür sorgt, dass der aktuelle Statusübergang an die CD-i-Applikation übermittelt wird. Meist will die Applikation nur auf den Übergang in den Status BUTTON_0_UP (Loslassen der Maustaste) reagieren.

cdiHotspotHandler(CdiHotspot* hotspot)
{
dispatch hotspot->handler // paint and set value
currentState = getCurrentState(hotspot)
currentStateHandler = getCurrentStateHandler(hotspot)
dispatch currentStateHandler // send event to application
if (currentState = ButtonUp) { // button 0 or 1 is up
disable hotspot // disable now
showCursor Busy // show hourglass
play soundmap // play soundmap
setTimer to enable hotspot // enable later
}
}

Sogenannte Doppelklicke, die das zweimalige Drücken und wieder Loslassen der Knöpfe erfordern, werden nicht unterstützt. Sie erschweren dem typischen Benutzer die Bedienung eines Programms. Folglich wird in der Funktion cdiHotspotHandler() nach dem Loslassen eines Knopfes sofort das disable Attribut des entsprechenden CdiHotspots gesetzt, damit er bei erneuter Mausaktion (bewegen oder drücken) vom Suchalgorithmus (cdiPointerGetCdiHotspot) nicht mehr gefunden wird. Zudem wird eine Cursorform angezeigt, die einer Sanduhr [249] ähnlich sieht und dem Benutzer signalisieren soll, dass seine durch das Loslassen des Knopfes gewählte Aktion gerade ausgeführt wird. Damit der Benutzer auch eine akustische Rückmeldung erhält, wird eine pro CdiHotspot frei definierbare Soundmap wiedergegeben. Parallel dazu [250] wird ein Zeitgeber [251] aus der CdiTimer-Klasse gestartet, so dass nach einer gewissen Zeitspanne [252] das disable Attribut wieder gelöscht wird.

Eine Schaltfläche auf dem Monitor kann drei Zustände annehmen:

  1. Inaktiv
    Auf der Schaltfläche kann kein Klick ausgeübt werden, da ihre Funktion keinen Sinn ergibt. So sollte etwa eine Film-'Stop'-Schaltfläche inaktiv sein, falls kein Film wiedergegeben wird.
  2. Gedrückt
    Eine aktive Schaltfläche befindet sich in diesem Zustand, falls der Benutzer den Cursor über sie positioniert hat und daraufhin den Aktionsknopf auf dem Eingabegerät drückt.
  3. Normal
    In allen anderen Fällen befindet sich die Schaltfläche im Normalzustand.

Meist soll der Benutzer über den Zustand einer Schaltfläche informiert werden, indem sie je nach aktuellem Status auf eine etwas andere Art dargestellt wird. Zu diesem Zweck kann jedem CdiHotspot eine Liste mit Bildern (CdiPicture-Liste) übergeben werden, aus denen die Zeichnungsfunktionen das richtige Bild zur Darstellung der Schaltfläche entnehmen.

Im folgenden werden einige der wichtigsten Funktionen der Klasse CdiHotspot aufgezählt:

Im Zusammenhang mit der Erläuterung der CdiHotspot-Klasse wurden auch die CdiPointer, die CdiCursor und die CdiTimer-Klassen erwähnt. CdiPointer verwaltet das X/Y-Eingabegerät und wandelt dessen Koordinaten so um, dass sie zur Darstellung des Cursors auf dem Bildschirm verwendet werden können. Bei dieser Umwandlung muss die momentane Auflösung des Bildschirmes berücksichtigt werden.

Aus der CdiPointer-Klasse ist vor allem die folgende Funktion erwähnenswert:

Eng mit CdiPointer verbunden ist die Klasse CdiCursor, die den graphischen Cursor steuert. Sie stellt vor allem folgende Funktionen zur Verfügung:

Die CdiTimer-Klasse verwalten die CD-RTOS-Alarme und bietet folgende Funktionen an:

6.2.7 CdiMovie

Konvertierte Quicktime-Filme [254] können mit Hilfe der MetaCDL-Befehle [255] in einer RTF-Datei gespeichert werden. Diese wird mit den Methoden der Klasse CdiMovie wiedergegeben.

Das nächste Codebeispiel verdeutlicht die konkrete Anwendung der wichtigsten Funktionen aus CdiMovie.

exampleMovie(CdiInt event, void* parameter) // this is an example
{ // event function
CdiMovie* movie; // our movie object
CdiScreen* screen; // the screen object
CdiSubScreen* subScreen; // the subScreen with
// our movie
CdiFile* file; // movie RTF-file
CdiPicture* buffer[CDL_NUM_OF_CHAN_MOVIE_GUIDE]; // first level buffer
screen = cdiScreenCreate(CDi_RESOLUTION_LOW); // make a screen
// maka a subScreen
subScreen = cdiSubScreenCreate(CDi_PLANE_B, // use plane B
CDi_PICTURE_TYPE_DYUV, // type is dyuv
(76 << 1), (62 << 1), // width, height
CDi_PICTURE_ALIGN_FORM2, // round to form 2 sector
cdiMemoryPlane1); // use memory
file = cdiFileOpenRealTime("MovieGuide.rtf"); // now open the file
movie = cdiMovieCreate(screen, subScreen, file, // make movie object
CDL_CHAN_AUDIO_MOVIE_GUIDES_acm, // audio channel
153 << 1, 0, // x,y position
(80 << 1), (60 << 1)); // width, height
for (i=0; i<CDL_NUM_OF_CHAN_MOVIE_GUIDE ; i++) { // all movie channels
// create buffers
buffer[i] = cdiPictureCreate(CDi_PLANE_B, CDi_PICTURE_TYPE_DYUV,
(80 << 1), (60 << 1), CDi_PICTURE_ALIGN_FORM2);
cdiMovieSetVideoChannel(movie, // add channels
CDL_START_CHAN_MOVIE_GUIDE + i, // channel no.
buffer[i]); // set buffer
}
cdiScreenAppend(screen, subScreen); // add subScreen to screen
cdiScreenShow(screen); // show the screen
cdiMoviePlay(event, movie); // play movie
}

Zuerst wird mit cdiScreenCreate() ein CdiScreen-Objekt erzeugt. Gleichzeitig wird mit CDi_RESOLUTION_LOW angegeben, dass die normale PAL-Auflösung für den Bildschirm verwendet werden soll.

Gleich anschliessend wird in der Ebene 'B' ein Bildstreifen (CdiSubScreen) generiert, der zur Darstellung von DYUV-Bildern eingesetzt wird. Er beginnt in der Zeile 76 des Monitors und weist eine Höhe von 62 Zeilen auf. Diese Angaben werden mit zwei multipliziert (76<<1), da alle Bildschirmkoordinaten immer in der hohen Auflösung angegeben werden. Da immer nur ganze Sektoren einer RTF-Datei gelesen werden können, wird mit dem Parameter CDi_PICTURE_ALIGN_FORM2 der Bildschirmspeicher auf das nächst grössere Vielfache der Form-2-Sektorgrösse aufgerundet [256].

Nach dem Öffnen der RTF-Datei wird mit cdiMovieCreate() ein CdiMovie-Objekt initialisiert. Der Parameter CDL_CHAN_AUDIO_MOVIE_GUIDES_acm stammt aus einer C-Deklarationsdatei, die mit MetaCDL automatisch erzeugt wurde, was auch an seinem Beginn mit CDL_ erkennbar ist. Es soll also der Audiokanal verwendet werden, in dem die Datei guideS.acm gespeichert wurde.

Mit den letzten Argumenten wird die genaue Position [257] der Filmdarstellung festgelegt. Da die x-Koordinate nicht mit '0' angegeben wird, muss jedes einzelne Frame an die richtige Position in den Bildstreifen kopiert werden.

Als nächstes wird für jeden verwendeten Kanal ein Bildpuffer erzeugt und mit cdiMovieSetVideoChannel() dem CdiMovie-Objekt zugewiesen. Nun muss noch der Bildstreifen mit cdiScreenAppend() der Bildschirmliste hinzugefügt und mit cdiScreenShow() der Bildschirm angezeigt werden. Schliesslich wird mit cdiMoviePlay() die Wiedergabe des Films gestartet.

Werden Filme wiedergegeben, die den ganzen Bildstreifen ausfüllen (x=0 und Breite=384), dann kann auf das Kopieren der Frames verzichtet werden. In solchen Fällen modifiziert der Darstellungsalgorithmus [258] direkt eine LCT-Tabelle, in der die Startwerte des Bildschirmspeichers neu gesetzt werden. Da dafür nur ein Bruchteil der Zeit benötigt wird, die für das Kopieren aufgewendet werden muss, können solche Filme mit sehr kleinen Leerräumen in den RTF-Dateien gespeichert werden.

6.2.8 CdiUiObject

Die Klasse CdiUiObject verwaltet die Elemente der Benutzerschnittstelle [259]. Es werden Befehls-, Umschalt-, und Animationsschaltflächen unterstützt.

Alle Elemente der Benutzerschnittstelle werden in einer Liste eingetragen, die anschliessend durch Angabe eines einzigen Befehls mit den notwendigen Video- und Audiodaten einer RTF-Datei [261] initialisiert wird.

Die Verwendung der folgenden Funktionen aus CdiUiObject wird im Abschnitt 7.2.4 an verschiedenen Beispielen verdeutlicht werden.


Fussnoten

[231] Eine gute Einführung in ANSI C kann aus [KerRit88] entnommen werden.

[232] Graphical User Interface.

[233] Wir sprechen von Klassen, da die meisten der C-Strukturen in der CDiC-Bibliothek zuerst mit C++ entwickelt wurden, dann aber aus den im Kapitel 5.6.1 erwähnten Gründen nach ANSI C portiert werden mussten.

[234] Die Instanzen der Klassen bezeichnen wir als Objekte.

[235] Vergleiche hierzu Abschnitt 6.2.2.

[236] Vergleiche hierzu Abschnitt 4.4.9.

[237] Dem Compiler muss für diese Funktionen mitgeteilt werden, dass er keinen Stackchecking-Code erzeugen darf. Darum werden alle system state mode-Funktionen in der Datei CDiSySta.c zusammengefasst und mit dem Schalter -s compiliert.

[238] 513 = 2*28+1.

[239] Von Vorteil wird die Kanalnummer des PCL-Elementes mit dem Signal übertragen. Da insgesamt 32 Kanäle in einer RTF-Datei möglich sind, können also 32 verschiedene Signale übermittelt werden.

[240] Vergleiche hierzu Abschnitt 4.3.1.6.

[241] Vergleiche hierzu Abschnitt 4.3.1.5.

[242] Vergleiche hierzu Abschnitt 4.4.7.

[243] Image Contribution Factor.

[244] Vergleiche hierzu 4.3.3.

[245] Vergleiche hierzu Anhang D.

[246] CLUT4, CLUT7, CLUT8, RL3, RL7, DYUV oder RGB555.

[247] Tatsächlich werden noch drei weitere CdiHotspots definiert. Vergleiche hierzu Abbildung 7-1.

[248] In Symbolleisten werden mehrere Schaltflächen zusammengefasst.

[249] Die Cursorform 'busy' ist ein fester Bestandteil der CDiC-Bibliothek.

[250] Die Wiedergabe der Soundmap wird asynchron ausgeführt, d.h. als eigenständiger Prozess.

[251] Die CDiC-Zeitgeber (Klasse CdiTimer) verwenden OS-9 Alarme. Vergleiche hierzu Abschnitt 4.4.8.2.

[252] In CIMEDIA beträgt die Zeitspanne meist 1,5 Sekunden.

[253] Ein Tick ist die Zeiteinheit des CD-RTOS. 100 Ticks ergeben eine Sekunde.

[254] Vergleiche hierzu Abschnitt 5.3.

[255] Vergleiche hierzu Abschnitt 5.4.2.4.

[256] In diesem Beispiel werden daher nicht 62*384=23'808 Byte, sondern 11*2324=25'564 Byte reserviert (2324 ist die Form-2-Sektorgrösse).

[257] Im Codebeispiel: x=153, y=0, Breite=80, Höhe=60.

[258] Der Darstellungsalgorithmus ist in der CdiMovie-Klasse implementiert.

[259] Wird als Userinterface bezeichnet. Daher der Name CdiUiObject.

[260] In CIMEDIA werden beispielsweise verschiedene rotierende Schalter verwendet.

[261] Diese RTF-Datei wird mit dem im Abschnitt 5.4.2.2 beschriebenen MetaCDL-Skript erzeugt.


Home

Index Zurück Weiter