Nachrichtenempfang per Callback

Zu guter Letzt soll hier die Rahmenfunktion eines X-Programmes vorgestellt werden. Seit der Einführung der Version X11R4 ist diese erfrischend kurz:

main(int argc, char *argv[])
{
XtAppContext ApplikationsHandle;
Widget HauptWidget;

   /* Anmelden der Applikation */
   HauptWidget = XtAppInitialize(&ApplikationsHandle,
                           äXMeinProgrammä,
                           (XrmOptionDescRec*)NULL, 0,
                           &argc, argv,
                           (String*)NULL, (Arg*) NULL, 0);

   /* Hier muss der Aufbau des Widgetbaums erfolgen... */

   /* Aufruf der Ereigniswarteschlange */
   XtAppMainLoop(ApplikationsHandle);
}

Die Funktion XtAppInitialize ist eine Zusammenfassung mehrerer Aufrufe der Versionen vor X11R4. Sie ermittelt die zwei wichtigsten Handles für das Programm. Dies ist einerseits der Applikationskontext. Dieser entspricht dem, was wir bisher als Applikationshandle bezeichnet haben. Als zweites Handle wird schon hier die Widgetkennung für das Hauptfenster ermittelt. Da jedes Programm ein Hauptfenster benäotigt, wird dies untrennbar mit der Initialisierung der Applikation verbunden.

Die weiteren Parameter dienen der Ressourcenverarbeitung von X-Programmen, die an dieser Stelle nicht im Detail betrachtet werden. Man kann zur Kenntnis nehmen, daäs das System über diesen Funktionsaufruf die Kommandozeilenparameter (argc und argv) erhäalt und sich die für das System bestimmten Parameter entnehmen kann. Zu beachten ist hier, daäs die Variable argc per Referenz übergeben wird. Das bedeutet, daäs die Funktion XtAppInitialize aus der Liste der Optionen einige entfernen und auswerten kann, die nur das System betreffen. Auf diesem Weg ist es mäoglich, daäs der Applikation beim Aufruf von der Kommandozeile Ressourcen mitgegeben werden.

Schlieäslich springt das X-Window Programm in die Endlosschleife, die sich hinter der Funktion XtAppMainLoop verbirgt. Ein Abmelden der Applikation und deren Ressourcen kennt man beim X-Window System nicht. Das Programm wird dadurch beendet, daäs an geeigneter Stelle die Funktion exit aufgerufen wird.

Auch bei X werden die Ereignisse nicht von der Hauptfunktion untersucht. Stattdessen hat jedes Widget für jedes seine jeweilige Funktionalitäat betreffende Ereignis eine Standard-Callbackfunktion. Der Programmierer schreibt eine eigene Callbackfunktion und meldet diese beim betreffenden Widget für ein Ereignis an. Man käonnte in diesem Zusammenhang von vielen kleinen Fensterfunktionen sprechen. Sie werden bei Aufbau des Widgetbaums angemeldet, indem dem Widget die Adresse der Funktion mitgeteilt wird und das Ereignis, bei dessen Eintritt die Funktion aufgerufen werden soll.

Übersicht über ein X Programm

Eine Abmeldephase gibt es bei X nicht. Im Allgemeinen wird die Beendigung des Programms von einer Callbackfunktion durchgeführt. Wie bei PM gibt es eine Messageschleife, die sich hinter der Funktion XtAppMainLoop verbirgt. Da dies eine Endlosschleife ist, gibt es nach dem Aufruf dieser Funktion auch keinen Code in der Hauptfunktion mehr. Im Unterschied zu GEM und PM gibt es eine direkte Fallunterscheidung innerhalb der Anwendung nicht mehr. Statt dessen wird vom System direkt die entsprechende Callbackfunktion aufgerufen. Um dies im Programm zu zeigen, ist ein etwas ausführlicheres Listing notwendig. Das folgende Programm erzeugt ein Hauptfenster mit zwei Knäopfen. Mit einem Knopf wird das Programm beendet, mit dem anderen wird eine Variable hochgezäahlt. Der Inhalt dieser Variablen wird im Label-Widget jedesmal neu angezeigt.

#include 
#include 
#include 
#include 

#include 

/* Der Name der Applikationsklasse */
#define Applikation äXCountä

/* Variable, in der die Knopfdruecke gezaehlt werden */
unsigned int Zaehler =0;

Widget wMsg; /* Das Widget, in dem die Zahl angezeigt wird */

void QuitCB(Widget w, XtPointer client, XtPointer call)
/* Die Callbackfunktion zum Verlassen des Programmes */
{
    exit(0);
}

void CountCB(Widget w, XtPointer client, XtPointer call)
/* Die Callbackfunktion fuer den Zaehlknopf */
{
Arg arg[1];
char ZaehlerText[40];
XmString LabelText;

    Zaehler++;
    /* Aus der Zahl einen String machen... */
    sprintf(ZaehlerText, ä %dä, Zaehler);
    /* ...und diesen ins Compound-Format umwandeln */
    LabelText = XmStringLtoRCreate(ZaehlerText,
                                  XmSTRING_DEFAULT_CHARSET);
    /* um diesen schliesslich im Label anzuzeigen */
    XtSetArg(arg[0], XmNlabelString, LabelText);
    XtSetValues(wMsg, arg, 1);
}

void initWidgetBaum(Widget top)
{
Widget wRahmen, wCount, wQuit;

    wRahmen = XtCreateManagedWidget(ärahmenä,
                    xmRowColumnWidgetClass, top, NULL, 0);
    wMsg = XtCreateManagedWidget(ämessageä,
                    xmLabelWidgetClass, wRahmen, NULL, 0);
    wCount = XtCreateManagedWidget(äcountä,
                    xmPushButtonWidgetClass, wRahmen, NULL, 0);
    XtAddCallback(wCount, XmNactivateCallback, CountCB, NULL);
    wQuit = XtCreateManagedWidget(äquitä,
                    xmPushButtonWidgetClass, wRahmen, NULL, 0);
    XtAddCallback(wQuit, XmNactivateCallback, QuitCB, NULL);

}

main(Cardinal argc, char **argv)
{
XtAppContext AppKontext;
Widget topShell;
Display *display;
Arg args[5];
Cardinal n;

    XtToolkitInitialize();
    AppKontext = XtCreateApplicationContext();
    display = XtOpenDisplay(
                   AppKontext, /* Applikationshandle */
                   NULL, /* ermoeglicht Aenderung des Display */
                   NULL,
                   Applikation, /* Applikationsname */
                   NULL, /* Optionen */
                   0,    /* Anzahl Optionen */
                   &argc, /* argc in/out */
                   argv  /* argv in/out */);

    if (display == NULL) {
       /* Fehler: kein Display zur Verfuegung */
    }

    topShell = XtAppCreateShell(NULL, Applikation,
                     applicationShellWidgetClass, display, NULL, 0);

    initWidgetBaum(topShell);
    XtRealizeWidget(topShell);
    XtAppMainLoop(AppKontext);
}

In der Hauptfunktion sind hier die einzelnen Funktionen verwendet, die statt der Funktion XtAppInitialize in äalteren Versionen verwendet wurden und die immer noch verwendbar sind. Anhand dieser Aufrufe ist die Initialisierungsphase deutlicher zu erläautern.

Zunäachst wird das X Toolkit mit dem Aufruf XtToolkitInitialize initialisiert. Als näachstes wird mit der Funktion XtCreateApplicationContext der Applikationskontext ermittelt, mit dessen Hilfe in der Hauptschleife die Ereignisse vom System erfragt wird. Die näachste Funktion XtOpenDisplay hat kein Gegenstück bei den anderen Oberfläachen. Mit dieser wird der X-Servver eräoffnet. Damit ist es mäoglich, daäs ein Programm auf einem Computer abläauft, wäahrend es auf einem anderen Computer, der über ein Netzwerk verbunden ist, die Benutzersteuerung ablaufen läaäst. Nachdem die Display-Variable angemeldet ist, kann mit der Funktion XtAppCreateShell der Rahmen für das Hauptfenster angemeldet werden. Die Anmeldung des Hauptfensters wird bei der Betrachtung der Fenster näaher betrachtet und soll hier nicht aufhalten.

In der Funktion initWidgetBaum wird das Hauptfenster aus mehreren Widgets zusammengesetzt. Ein Widget wird mit der Funktion XtCreateManagedWidget erzeugt. Für den Aspekt der Nachrichtenbearbeitung interessiert hier besonders die Funktion XtAddCallback. Diese Funktion meldet für beide Pushbutton-Widgets je eine Callback-Funktion an. Dieser Vorgang entspricht dem Anmelden einer Fensterfunktion unter MS-Windows oder PM. Allerdings sind die Callbackfunktionen nicht so umfangreich. Ihre Funktionalitäat beschräankt sich typischerweise auf ein Ereignis eines Widgets, hier auf das Ausläosen eines Druckknopfes.

Die Callbackfunktion besitzt drei Parameter. Der erste kennzeichnet das Widget, das das Ereignis ausgeläost hat. So ist es mäoglich, die gleiche Callbackfunktion für mehrere Widgets anzumelden. Der zweite Parameter ist ein Zeiger auf Clientdaten, die bei Anmeldung des Callbacks übergeben werden käonnen. In unserem Beispiel käonnte man einen Zeiger auf die Variable Zaehler übergeben, so daäs die Callbackfunktion mit mehreren Widgets und unterschiedlichen Zäahlern verwendet werden käonnte. Der dritte Parameter wird vom System übergeben. Er zeigt auf eine je nach Widgettyp unterschiedliche Datenstruktur, die näahere Informationen über das zugrundeliegende Ereignis enthält.


Inhaltsverzeichnis (C) Copyright 1993-1999 Arnold Willemer