Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 14 Xlib – X Window-Programmierung
  gp 14.1 Architektur von X
    gp 14.1.1 Pufferung
    gp 14.1.2 Ressourcen
  gp 14.2 X Toolkit
  gp 14.3 Der Window-Manager
  gp 14.4 Fenster mit X
    gp 14.4.1 Verbindung herstellen
    gp 14.4.2 Fenster definieren
    gp 14.4.3 Informationen für den Window-Manager
    gp 14.4.4 Fenster anzeigen
    gp 14.4.5 Fenster und X-Verbindung beenden
    gp 14.4.6 Das Grundgerüst als Quellcode
  gp 14.5 Events
    gp 14.5.1 Event-Maske setzen
    gp 14.5.2 Event-Bearbeitungsschleife
    gp 14.5.3 Abarbeitung der Events
    gp 14.5.4 Tastatur-Event
    gp 14.5.5 Mausbutton-Event
    gp 14.5.6 Expose-Event
    gp 14.5.7 EnterWindowMask – LeaveWindowMask
    gp 14.5.8 Listing zu den Events
  gp 14.6 Grafik mit X
    gp 14.6.1 Grafikprimitive – Zeichenfunktionen
  gp 14.7 Text mit X
    gp 14.7.1 Fonts benutzen
    gp 14.7.2 Übersicht zu den Fonts
    gp 14.7.3 Höhe und Breite ermitteln
    gp 14.7.4 Listing mit Textausgabe
  gp 14.8 Farben mit X
  gp 14.9 Beispiel – ein Button mit X
  gp 14.10 Wozu kann ich denn nun die Xlib in der Praxis verwenden?
  gp 14.11 X und X-Toolkits – Einführung
  gp 14.12 Toolkit-Programmierung
    gp 14.12.1 Widgets
  gp 14.13 Ein einfaches Fenster mit einem Button
    gp 14.13.1 Ressourcen
  gp 14.14 Zwei Buttons in Box- und Form-Widget mit Textausgabe (Label-Widget)
  gp 14.15 editres – Widget-Hierarchie
  gp 14.16 Texteingabe mit Dialog-Widget
  gp 14.17 X-Toolkits meets Xlib
  gp 14.18 Zusammenfassung


Rheinwerk Computing

14.13 Ein einfaches Fenster mit einem Button  downtop

Um die Xt-Funktionen zu verwenden, sind immer mindestens folgende zwei Headerdateien nötig:

/* Toolkit-Strukturen */
#include <X11/Intrinsic.h>
/* Toolkit-Konstanten, z. B. fuer Ressourcen */
#include <X11/StringDefs.h>

Des Weiteren sind bestimmte Header für die verwendete Widget-Klasse nötig. Im ersten Beispiel werden zwei Buttons verwendet, die in der folgenden Headerdatei definiert sind:

/* Command-Widget des Athena-Sets */
#include <X11/Xaw/Command. h.>

Im ersten Schritt, bevor Sie überhaupt ein Widget verwenden können, müssen Sie erst das Toolkit initialisieren. Initialisieren können Sie ein Toolkit mit der Funktion XtVaAppInitialize(), wodurch eine komplexe Datenstruktur mit dem Namen XtAppContext, die für weitere Aktionen später benötigt wird, initialisiert wird. Die Funktion XtVaAppInitialize() liefert eine Variable vom Typ Widget zurück und stellt gleichzeitig auch das ranghöchste Widget in der Widget-Hierarchie dar – das Root- bzw. Eltern-Widget aller noch im Programm folgenden Widgets. Es gibt noch andere Funktionen zum Initialisieren eines Toolkits, die sich nur geringfügig von der Anzahl der Parameter unterscheiden. So besteht z. B. zwischen XtAppInitialize() und XtVaAppInitialize() nur der Unterschied, dass die zweite Version (..Va..) eine variable Argumentenliste zulässt.


Hinweis   Es gibt bei vielen Xt-Funktionen eine zweite Version, die eine variable Argumentenliste verwendet.


Eine Anwendung der Funktion XtVaAppInitialize() könnte wie folgt aussehen:

int main (int argc, char **argv) {
  XtAppContext contex;   /* Wird bei Init des Toolkits belegt */
  Widget       w_root;   /* Variable fuer Root-Widget         */
  ...
  w_root = XtVaAppInitialize(
                &app_context,  /* Kontext der Applikation     */
                NULL   ,       /* Applikationsklasse          */
                NULL, 0,       /* Kommandozeilenoptionsliste  */
                &argc, argv,   /* Kommandozeilenargumente     */
                NULL,          /* fallback_ressources         */
                NULL);         /* Terminierte varargs Liste   */

In den folgenden Beispielen werden Sie diese Funktion auch nicht mit den anderen Parametern verwenden – daher werden die meisten Werte mit NULL bzw. 0 belegt. Bis auf die Bedeutung des zweiten Parameters wird darauf nicht näher eingegangen.

Wenn Sie das Toolkit initialisiert haben, können Sie ein Widget einer bestimmten Klasse erzeugen. Dazu verwenden Sie praktisch eine Instanz. Zum Erzeugen eines Widgets wird auch hierbei wieder auf eine Funktion mit der ..Va..-Version zurückgegriffen – XtVaAppInitialize(). Als Beispiel wird hierbei ein Button der Klasse commandWidgetClass erzeugt, die in der Headerdatei <X11/Xaw/Command. h.> definiert ist.

quit = XtVaCreateManagedWidget (
        "quit", commandWidgetClass,
        w_root,
        /* Eigenschaften des Buttons ...    */
        XtNheight, (XtArgVal) 20, /* Höhe   */
        XtNwidth, (XtArgVal) 50,  /* Breite */
        /* Hier könnten noch mehr Eigenschaften stehen ... */
        NULL );

Damit erzeugen Sie ein Widget mit dem selbst definierten Widget-Namen quit und der Widget-Klasse commandWidgetClass. Die Eltern-Klasse des Buttons ist hier w_root, die Sie zuvor mit der Funktion XtVaAppInitialize() erzeugt haben. Anschließend können Sie hier die Eigenschaften des Buttons wie Farbe, Größe, Position, Abstand usw. als variable Argumentenliste angeben, die mit NULL terminiert werden muss. Die Elemente der variablen Argumentenliste müssen immer paarweise auftreten. Der erste der beiden Parameter muss ein Bezeichner der Form sein, der festlegt, was das zweite Argument bedeutet. Bei der Verwendung der variablen Argumentenliste spricht man von Hardcoding.


Rheinwerk Computing

14.13.1 Ressourcen  toptop

Außer der Verwendung von Hardcoding können nämlich auch Ressourcendateien verwendet werden. Ressourcen sind wiederum nichts anderes als modifizierbare Eigenschaften von Widgets wie z. B. Farbe, Schriftart und -größe, Größe des Widgets usw. Der Hersteller eines Widget-Sets legt die Ressourcen fest. D. h., er definiert für jede Widget-Klasse, welche Eigenschaften Sie als Anwendungsprogrammierer überhaupt modifizieren können – so entsteht auch das typischen Look & Feel der verschiedenen Desktops wie im Vergleich von KDE zu GNOME. Da Sie hierbei einen hierarchischen Aufbau des Widget-Sets haben, erben alle Abkommen einer Klasse die Ressourcen der Vorfahren – daher sehen auch alle Ihre Programme erst dem Window-Manager ähnlich. Wenn Sie jetzt wissen wollen, welche Ressourcen für welche Widget-Klasse zur Verfügung stehen, können Sie das Tool listres verwenden. Anwenden können Sie listres mit der Angabe der Widget-Klasse als zweitem Parameter. Im Beispiel des Buttons (command) wäre dies:

$ listres command
WidgetClass      Instance  Class                 Type
-----------      --------  -----                 ----
command:  Core\Simple\Label\Command
Core         accelerators  Accelerators          AcceleratorTable
Core    ancestorSensitive  Sensitive             Boolean
Core           background  Background            Pixel
Core     backgroundPixmap  Pixmap                Pixmap
Label              bitmap  Pixmap                Bitmap
Core          borderColor  BorderColor           Pixel
Core         borderPixmap  Pixmap                Pixmap
...

Zu Beginn erkennen Sie gleich die Hierarchie: Core\Simple\Label\Command. Bei der Ressourcendatei handelt es sich schlicht um eine einfache ASCII-Textdatei, die während der Ausführung der X-Anwendung verändert werden kann. Die Veränderungen werden umgehend aktiv, ohne die Anwendung neu zu übersetzen. Den Namen der Ressourcendatei müssen Sie bei der Initialisierung mit der Funktion XtVaAppInitialize() als zweiten Parameter mit angeben, z. B.:

  w_root = XtVaAppInitialize(
        &app_context,         /* Kontext der Applikation      */
        "MEine_Ressource"     /* Applikationsklasse           */
        NULL, 0,              /* Kommandozeilenoptionsliste   */
        &argc, argv,          /* Kommandozeilenargumente      */
        NULL,                 /* fallback_ressources          */
        NULL);                /* Terminierte varargs Liste    */

Um die anwendungsspezifische Ressourcendatei zu verwenden, müssen Sie den Namen der Datei als zweiten Parameter angeben. Üblicherweise schreibt man hierbei die ersten beiden Buchstaben groß.

Wollen Sie z. B., dass alle Instanzen des Command-Widgets die Breite 100 haben, müssen Sie Folgendes in die Ressourcendatei schreiben:

 *Command*width: 100         !1. Buchstabe gross!

Links steht der Klassenname, dann kommt der Ressourcenname und schließlich der Wert. Wollen Sie hingegen nur, dass die Instanz mit dem Namen 'hallo' den Wert bekommt, dann müssen Sie dies wie folgt in die Ressourcendatei eintragen:

*hallo.width: 150

Links steht der Instanzenname, dann kommen wieder der Ressourcenname und der Wert.

Es gibt hierbei allerdings keinerlei Syntax-Checks! Wenn also etwas nicht wie gewünscht funktioniert, hat man hier meistens etwas falsch gemacht. Z. B. muss man auf * und : achten und darf am Zeilenende keine Blanks mehr haben. Kommentarzeilen werden in der Ressourcendatei mit einem ! eingeleitet. Ob vor dem Ressourcennamen ein * oder ein . steht, ist nur von sekundärer Bedeutung. Ich empfehle Ihnen, die Ressourcendateien im Verzeichnis /usr/lib/X11/app-defaults zu einigen X-Anwendungen anzusehen.

Wollen Sie die Ressourcen, die Ihnen das Tool listres zurückliefert, im Hardcoding-Stil verwenden, ist das auch kein Problem; den Schreibstil haben Sie ja bereits gesehen:

   quit = XtVaCreateManagedWidget ( 
         ...
         XtNheight, (XtArgVal) 20,
         XtNwidth,  (XtArgVal) 50,
         NULL);

Folgende Bedeutung hat dabei die Zeile XtNheight, (XtArgVal) 20:

gp  XtN: Abkürzung für XtressourceName
gp  height: Name der Ressource, die Sie auch von dem Tool listres angezeigt bekommen (Spalte: Instance)
gp  20: Wert der Ressource vom geeigneten Typ
gp  Der Cast-Operator XtArgVal wird empfohlen.

Hinweis   In den noch kommenden Beispielen werden nur der Hardcoding-Stil und wenige Ressourcen verwendet. Sie können aber gerne mithilfe der Ressourcendsateien in /usr/lib/X11/app-defaults an den eigenen Programmen experimentieren.


Zurück zur Funktionsbeschreibung XtVaCreateManagedWidget(): Als Rückgabewert der Funktion XtVaCreateManagedWidget() erhalten Sie auch hier einen Wert vom Typ Widget, der in der Variablen quit abgespeichert wurde.

Gewöhnlich werden Sie wollen, dass bei Betätigung eines Widgets etwas passiert – im Falle des Drückens eines Mausbuttons soll eine Aktion aufgerufen werden. Um eine bestimmte Funktion aufzurufen, können Sie XtAddCallback() verwenden.

XtAddCallback (quit, XtNcallback, (XtCallbackProc)quitCB, NULL);

Der erste Parameter ist der Name des Widgets, zu dem die Callback-Funktion gehören soll. Mit dem dritten Parameter geben Sie die Callback-Funktion an, die aufgerufen werden soll, wenn der Button mit der linken Maustaste betätigt wurde. In diesem Fall ist es die Funktion quitCB, die wie folgt aussehen könnte:

/* Callback-Funktion für den Button 'quit' */
static void 
quitCB(Widget button, XtPointer client_data, XtPointer call_data) {
   printf ("Button 'quit' gedrückt\n");
   exit (EXIT_SUCCESS);
}

Wollen Sie der Callback-Funktion Daten übergeben, können Sie dies mit dem letzten Parameter der Funktion XtAddCallback() machen.

Jede Funktion, die durch XtAddCallback() eingefügt wird, wird in der Regel immer mit drei Parametern von den Toolkit-Funktionen aufgerufen. Dies zu wissen ist besonders wichtig bei der Definition der Funktion:

/* Callback-Funktion für den Button 'quit' */
static void 
quitCB(Widget button,XtPointer client_data,XtPointer call_data);

Um jetzt die Widgets auf dem Bildschirm anzuzeigen, müssen Sie, wie schon bei der Xlib, mit XMapRaised() die Funktion XtRealizeWidget(w_root) aufrufen. Weil Sie das in der Hierarchie am höchsten platzierte Widget aufrufen, werden damit alle zugeordneten Unter-Widgets ebenso am Bildschirm mit ausgegeben. Allmählich erkennt man die Stärke der hierarchischen Anordnung der Widgets.

Jetzt fehlt Ihnen nur noch die Event-Bearbeitungsschleife, was Ihnen im Gegensatz zur Xlib mit nur einem Befehl abgenommen wird:

XtAppMainLoop(context);

Nach diesem Befehl kann allerdings nichts mehr X-Spezifisches folgen und sollte somit auch der letzte X-Befehl in der Anwendung sein.

Das Grundgerüst einer Anwendung mit dem Xt-Toolkit sei hier nochmals kurz zusammengefasst:

1. Xt-Toolkit initialisieren – XtVaAppInitialize()

2. Ein Widget erzeugen – XtVaCreateManagedWidget()

3. (Callback-)Funktion(en) zum Widget hinzufügen (ist nicht immer nötig)

4. Widget(s) am Bildschirm darstellen – XtRealizeWidget()

5. Event-Bearbeitungsschleife setzen – XtAppMainLoop()

Das folgende Listing zeigt Ihnen die hier vorgestellten Funktionen im Einsatz. Es wird ein Fenster mit zwei Buttons erzeugt. Ein Button gibt in der Konsole eine Textfolge mit einem übergebenen Parameter aus, und bei Betätigung des anderen Buttons 'quit' wird das Programm beendet. Es wurde außerdem die Klasse formWidgetClass, die in der Headerdatei <X11/Xaw/Form.h> definiert ist, verwendet, um überhaupt eine Arbeitsfläche für zwei Buttons bzw. Widgets im Allgemeinen zu erstellen. Als alte Bekannte (aus der Xlib-Abteilung) geben Sie diesem Widget eine Callback-Funktion, womit Sie einige Maus-Events im Fenster abfragen und auf der Konsole ausgeben.

/* toolkit1.c */
#include <stdio.h>
#include <stdlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command. h.>
#define MAUS_LINKS  1
#define MAUS_RECHTS 3
#define MAUS_MITTE  2
static void quitCB (Widget, XtPointer, XtPointer);
static void halloCB (Widget, XtPointer, XtPointer);
static void quit_proc (Widget, caddr_t, XEvent *);
/* Callback-Funktion für den Button 'quit' */
static void 
quitCB(Widget button,XtPointer client_data,XtPointer call_data) {
  printf ("Button 'quit' gedrückt\n");
  exit (EXIT_SUCCESS);
}
/* Callback-Funktion für den Button 'hallo' */
static void 
halloCB(Widget button,XtPointer client_data,XtPointer call_data){
  printf ("Button 'hallo' gedrückt mit der Nachricht '%s'\n",
     (char *)client_data);
}
/* Eventhandler für die Anwendung */
static void
quit_proc (Widget form, caddr_t client_data, XEvent * event) {
  switch (event->type) {
  case ButtonPress:
    switch (event->xbutton.button) {
    case MAUS_RECHTS:
      printf ("Rechte Maustaste\n");
      break;
    case MAUS_LINKS:
      printf ("Linke Maustaste\n");
      break;
    case MAUS_MITTE:
      printf ("Mittlere Maustaste\n");
      break;
    default:
      break;
    }
    break;
  default:
    break;
  }
}
int main (int argc, char **argv) {
  XtAppContext ac;
  Widget toplevel, form, quit, hallo;
  /* Fenster erzeugen*/
  toplevel = XtVaAppInitialize (
     &ac, NULL, NULL, 0, &argc, argv, NULL, NULL);
  /* Form-Widget erzeugen */
  form = XtVaCreateManagedWidget (
           "form", formWidgetClass,
           toplevel,
           XtNbackground, (XtArgVal) "red",
           NULL  );
  XtAddEventHandler ( form, ButtonPressMask, False, 
                      (XtEventHandler) quit_proc, NULL);
  /* quit-Button erzeugen */
  quit = XtVaCreateManagedWidget (
           "quit", commandWidgetClass,
           form,
           /* Eigenschaften des Buttons ... */
           XtNheight, (XtArgVal) 20,
           XtNwidth, (XtArgVal) 50, NULL);
  /* Callback-Funktion für quit-Button */
  XtAddCallback (
     quit, XtNcallback, (XtCallbackProc)quitCB, NULL);
  /* hallo-Button erzeugen */
  hallo = XtVaCreateManagedWidget (
            "hallo", commandWidgetClass,
            form,
            /* Eigenschaften des Buttons ... */
            XtNhorizDistance, (XtArgVal) 50,
            XtNfromHoriz, (XtArgVal) quit,
            XtNheight, (XtArgVal) 20,
            XtNwidth, (XtArgVal) 50, NULL);
  /* Callback-Funktion für hallo-Button */
  XtAddCallback ( hallo, XtNcallback,
                  (XtCallbackProc)halloCB, "Eine Nachricht");
  /* Widget toplevel mitsamt Unter-Widgets anzeigen */
  XtRealizeWidget (toplevel);
  /* Event-Bearbeitungsschleife einrichten */
  XtAppMainLoop (ac);
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o toolkit1 toolkit1.c -L/usr/X11R6/lib -lX11 -lXaw -lXt
$ ./toolkit1
Button 'hallo' gedrückt mit der Nachricht 'Eine Nachricht'
Button 'quit' gedrückt

Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 14.5    Ein einfaches Fenster mit zwei Buttons


 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern