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.4 Fenster mitdowntop

Bevor hier mit einer Einführung begonnen werden kann, soll erst einmal ein Listing erstellt werden, das Sie im Laufe der einzelnen Kapitel erweitern. Das Beispiel stellt außerdem ein Grundgerüst dar, das man im Allgemeinen für Projekte mit der Xlib verwenden kann. Zwar geht es in diesem Abschnitt erst um das Erstellen eines neuen Fensters – aber es werden dennoch bereits die Ereignisschleife und eine Funktion zur Auswertung der Ereignisse integriert – was aber erst im nächsten Abschnitt beschrieben wird.


Hinweis   Bitte haben Sie Verständnis, dass ich bei den Listings nicht jede einzelne Funktion behandeln kann. Ebenso sieht es mit der exakten Syntaxbeschreibung der Funktionen aus. Anstatt Ihnen hier seitenweise Syntaxbeschreibungen zu servieren, habe ich mich in diesem Kapitel mehr für die Praxis entschieden. Benötigen Sie bei Bedarf eine genauere Beschreibung der einzelnen Funktionen mit deren Parametern, dann sollten Sie entweder die entsprechende Manual Page oder die Dokumentation der Xlib verwenden.


Ein Fenster zu erstellen, ist im Prinzip recht einfach (wenn man es weiß), und lässt sich mit folgenden Schritten realisieren:

1. Eine Verbindung zum X-Server herstellen
       
2. die Umgebung überprüfen
       
3. das Fenster erzeugen
       
4. das Fenster anzeigen
       
5. das Fenster schließen und wieder freigeben
       

Um überhaupt Programme mit der Xlib zu erstellen, müssen mindestens folgende Include-Dateien definiert werden:

#include <X11/Xlib.h>
#include <X11/Xutil.h>

Um den Linker anschließend noch mit der Bibliothek bekannt zu machen, müssen Sie ihm den Pfad zur Xlib zeigen. Dies erledigen Sie mit den folgenden Flags (der Programmname sei hallo_x.c):

$ gcc -o hallo_x hallo_x.c -L/usr/X11R6/lib -lX11
$ ./hallo_x

Rheinwerk Computing

14.4.1 Verbindung herstellen  downtop

Der allererste Befehl – nachdem Sie alle Variablen und Datenstrukturen definiert haben – bei einer X-Anwendung lautet immer XOpenDisplay(), womit Sie eine Verbindung zum X-Server (Display) herstellen. Angewendet wird die Funktion so:

   Display *display;
   Screen *screen;
   ...
   /* X-Sitzung öffnen */
   display = XOpenDisplay (NULL);
   /* Fehlerüberprüfung */
   if (display == NULL) {
      printf("Verbindung zum X-Server fehlgeschlagen?!?\n");
      return EXIT_FAILURE;
   }
   /* Standardbildschirm eines Displays */
   screen = XDefaultScreen (display);

Die Funktion liefert bei erfolgreicher Verbindung einen Zeiger auf die Anfangsadresse der Display-Struktur zurück. Konnte die Verbindung nicht eingerichtet werden, wird NULL zurückgegeben. Als Parameter wird dieser Funktion der Name eines Servers übergeben, womit der Rechner eine Verbindung herstellen soll. Wird hierfür NULL oder "" angegeben, bedeutet dies, dass der X-Server auf demselben lokalen Rechner verwendet werden soll, auf dem das Clientprogramm ausgeführt wird (was wohl bei den meisten Lesern im Moment auch zutrifft). In diesem Fall wird der Leerstring mit dem Inhalt der Umgebungsvariablen DISPLAY (siehe echo $DISPLAY) ersetzt.

In einem netzwerktransparenten Window-System können Sie sich mit Ihrer Anwendung über TCP, UNIX Domain Sockets oder DECnet mit dem X-Server verbinden. Die Syntax hierfür sieht dann folgendermaßen aus:

Hostname:Displaynummer.Screennummer

Der Hostname ist der Name des Netzknotens, der mit einem (Verbindung mit TCP) oder zwei Doppelpunkten (Verbindung über DECnet) beendet wird. UNIX-Domain-Socket-Verbindungen werden durch keinen oder den Namen UNIX gekennzeichnet – was ja eine lokale Verbindung darstellt. Welches Display im Knoten verwendet werden soll, wird mit der Displaynummer, die aufsteigend nummeriert wird, angegeben. Und da ein Display mehrere Bildschirme besitzen kann, werden diese mit Screennummer aufsteigend durchnummeriert. Die speziellen Parameter von XOpenDisplay() seien hier nur erwähnt – werden aber in den Beispielen des Buches nicht verwendet, da hierbei immer eine Verbindung mit dem lokalen Rechner angefordert wird.

Der Rückgabewert der Funktion XOpenDisplay() liefert Ihnen eine vom Server alloziierte Struktur mit Hunderten von Variablen zurück – die Ihnen (glücklicherweise) egal sein können. Die zurückgegebene Struktur benötigen Sie allerdings bei jedem Aufruf einer X-Funktion als ersten Parameter.

Unmittelbar danach sollten Sie auch gleich die Funktion XDefaultScreen() mit dem Parameter display aufrufen. Die Funktion liefert Ihnen eine Integervariable screen zurück, die Sie ebenfalls bei vielen Funktionen der Xlib benötigen. Meistens ist dieser Wert mit 0 belegt, sofern eine Workstation nicht mehr als einen Bildschirm besitzt.


Rheinwerk Computing

14.4.2 Fenster definieren  downtop

Im nächsten Schritt wird mit der Funktion XCreateSimpleWindow() ein einfaches Fenster definiert. Die Funktion hat zwar recht viele Parameter, ist aber einfach zu verwenden:

rootwin = RootWindow (display, screen);
win = XCreateSimpleWindow ( display, rootwin, 10, 10, 
                            width, height, 5,
                            BlackPixel (display, screen),
                            WhitePixel (display, screen) );

Als erster Parameter folgt, wie schon erwähnt, bei jeder X-Funktion ein Zeiger auf die Display-Struktur. Anschließend bestimmen Sie das Fenster, das Vorgänger des neuen Fensters wird. In diesem Beispiel ist dies das root-Fenster (das heißt der ganze Bildschirm), das zuvor mit der Funktion RootWindow() ermittelt wurde. Mit den Parametern drei und vier geben Sie die x- und y-Koordinaten des Fensters (links oben) an (0,0). Diese Angaben werden aber recht häufig von den Window-Managern ignoriert – da diese gerne selbst entscheiden, wo ein neues Fenster platziert wird. Parameter fünf und sechs stellen die Breite und Höhe des Fensters dar. Auch der Parameter sieben, der die Randdicke des Fensters darstellt, wird von den meisten Window-Managern ignoriert. Die letzen beiden Parameter beinhalten die Kennziffern für die Farbe des Randes (Parameter acht) und die des Fensterhintergrundes (letzter Parameter). Sie sollten hier keine Zahlen angeben, auch wenn dies funktioniert. Die saubere Schwarz-Weiß-Lösung ist die, dass man sich eine für das aktuelle Display gültige Kennziffer mit BlackPixel() bzw. WhitePixel() beschafft. Als Rückgabewert dieser Funktion erhalten Sie erst einmal eine Kennungs-ID des Fensters.


Rheinwerk Computing

14.4.3 Informationen für den Window-Manager  downtop

Wenn Sie dem Window-Manager einige Informationen zu einem Fenster zukommen lassen wollen, können Sie hierfür Funktionen verwenden, die sich auf die Struktur XSizeHints beziehen. Dies ist eine Struktur im X-Server, die jedes Fenster besitzt und die der Client, was den Window-Manager mit einschließt, jederzeit abfragen kann. Sie können hierzu Funktionen verwenden, die sich auf einzelne Variablen der Struktur XSizeHints beziehen, oder gleich mehrere Strukturvariablen auf einen Schlag setzen. Ein solcher Vorgang wird wie folgt erledigt:

char *window_name = "Einfaches Fenster - hallo_x";
char *icon_name = "window";
static XSizeHints size_hints;
...
XCreateSimpleWindow(...);
...
size_hints.flags = PSize | PMinSize | PMaxSize;
size_hints.min_width  =  width;
size_hints.max_width  =  width;
size_hints.min_height = height;
size_hints.max_height = height;
XSetStandardProperties ( display, win, window_name,
                         icon_name, None, 0, 0, &size_hints );

Hier wird zuerst eine Strukturvariable namens size_hints vom Typ XSizeHints definiert. Nachdem ein neues Fenster ausgemacht wurde, können Sie die einzelnen Strukturkomponenten mit Werten versehen. Mit dem ersten Flag flags legen Sie z. B. fest, dass die Anwendung die Größe des Fensters (Psize) bestimmt, was die minimale (PminSize) und maximale (PmaxSize) Größe mit einschließt. Anschließend legen Sie diese minimale bzw. maximale Größe mit min_width, min_height, max_width und max_height fest. Die minimale Größe des Top-Level-Windows wird übrigens immer benötigt. In diesem Beispiel kann das Fenster weder vergrößert noch verkleinert werden (wohl aber minimiert), da gleiche Größen angegeben wurden. Anschließend legen Sie diese Properties mit der Funktion XSetStandardProperties() für das Fenster mit der ID win und der Verbindung display fest. Mit dem dritten Parameter können Sie den Titel des Fensters und mit dem vierten Parameter das Icon dazu angeben. Die nächsten drei Parameter werden ignoriert und mit 0 besetzt. Im letzten Parameter befinden sich die Variablen der Struktur XSizeHints. Mehr zu dieser Struktur und dessen unzähligen Funktionen entnehmen Sie bitte wieder der Dokumentation der Xlib oder der Manual Page.


Rheinwerk Computing

14.4.4 Fenster anzeigen  downtop

Um das Fenster anzuzeigen, das Sie mit der Funktion XCreateSimpleWindow() definiert haben, müssen Sie nur die Funktion XMapWindow() mit den Parametern zum Display und die ID des Fensters verwenden:

XMapWindow (display, win);

Hinweis   Da in den folgenden Beispielen immer auf bestimmte Events (Tastatur, Maus etc.) gewartet wird, wird das Fenster mit der Funktion XMapWindow() auch immer gleich angezeigt. Da, wie bereits erwähnt, die Funktionsaufrufe vom Client in einem Puffer gehalten werden, bis dieser voll ist, müssen Sie, wenn Sie keine Events in Ihrer Anwendung erwarten, die Funktion XFlush(display) verwenden, um den Puffer explizit von Hand zu leeren. Beim Eintreffen von Events wird dieser Puffer sonst immer automatisch geleert.



Rheinwerk Computing

14.4.5 Fenster und X-Verbindung beenden  downtop

Beides wird eigentlich bei Beendigung des Programms automatisch durchgeführt; doch wie es sich in C mit dem Schließen von Filedeskriptoren usw. gehört, sollten Sie auch hier das Fenster mit der Funktion XDestroyWindow() schließen und den Kontakt mit dem Server mit der Funktion XCloseDisplay() beenden:

XDestroyWindow(display, win);
XCloseDisplay(display);

Rheinwerk Computing

14.4.6 Das Grundgerüst als Quellcode  toptop

Jetzt finden Sie im folgenden Beispiel viele dieser Funktionen wieder. Beim Grundgerüst sollten Sie sich erst nur für die Funktion create_window() interessieren. Die Events werden erst im nächsten Kapitel beschrieben.

/* hallo_x.c */
#include <stdio.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 300
/* Funktionsprototypen */
static int create_window (void);
static void process_event (XEvent report);
static void eventloop (void);
static void close_window (void);
/* Globale Variablen */
static unsigned int width, height;
static Display *display;
static int screen;
static int depth;
static Window win;
/* Funktion erzeugt ein Fenster */
static int create_window (void) {
  char *window_name = "Einfaches Fenster - hallo_x";
  char *icon_name = "window";
  static XSizeHints size_hints;
  Window rootwin;
  width = WINDOW_WIDTH;
  height = WINDOW_HEIGHT;
  /* X-Sitzung öffnen */
  display = XOpenDisplay (NULL);
  /* Fehlerüberprüfung */
  if (display == NULL) {
    printf ("Verbindung zum X-Server fehlgeschlagen?!?\n");
    exit(EXIT_FAILURE);
  }
  /* Standardbildschirm eines Displays */
  screen = XDefaultScreen (display);
  /* Standardtiefe des Screens - Anzahl der Ebenen */
  depth = XDefaultDepth (display, screen);
  rootwin = RootWindow (display, screen);
  win = XCreateSimpleWindow ( display, rootwin, 100, 10,
                              width, height, 5,
                              BlackPixel (display, screen),
                              WhitePixel (display, screen) );
  size_hints.flags = PSize | PMinSize | PMaxSize;
  size_hints.min_width =  width;
  size_hints.max_width =  width;
  size_hints.min_height = height;
  size_hints.max_height = height;
  XSetStandardProperties ( display, win, window_name, icon_name,
                           None, 0, 0, &size_hints );
  /* Erwünschte Events, auf die das Fenster reagieren soll ... */
  XSelectInput (display, win, ButtonPressMask | KeyPressMask);
  /* Fenster anzeigen */
  XMapWindow (display, win);
  return 1;
}
static void close_window (void) {
  XDestroyWindow(display, win);
  XCloseDisplay (display);
}
static void eventloop (void) {
  XEvent xev;
  int num_events;
  XFlush (display);
  num_events = XPending (display);
  while ((num_events != 0)) {
     num_events--;
     XNextEvent (display, &xev);
     process_event (xev);
  }
}
static void process_event (XEvent report) {
  KeySym key;
  switch (report.type) {
  case KeyPress:
    key = XLookupKeysym (&report.xkey, 0);
    if(key) {
      printf("Tastatur-Event\n");
    }
    break;
  case ButtonPressMask:
    printf ("Maus-Event\n");
    break;
  default:
    break;
  }
}
int main (int argc, char **argv) {
  int quit = 0;
  create_window ();
  while (!quit) {
    eventloop ();
  }
  close_window ();
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o hallo_x hallo_x.c -L/usr/X11R6/lib -lX11
$ ./hallo_x

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

Abbildung 14.1    Ein einfaches Fenster mit der Xlib


Bei einem Blick auf die Systemressourcen werden Sie jetzt bei diesem einfachen Fenster mit Schrecken Folgendes feststellen. Starten Sie hierzu in der Kommandozeile das Tool top:

PID USER PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  Command 
2581 tot 25  0   1112 1108  900 R 99.0  0.4   0:32.02 hallo_x
2582 tot 15  0    884  884  696 R  0.7  0.3   0:00.21 top
1959 tot 15  0  14296  13m  12m S  0.3  5.6   0:01.94 kdeinit
1 root   15  0    244  244  208 S  0.0  0.1   0:04.43 init

Das einfache Fenster beansprucht je nach Rechenleistung fast die komplette CPU für sich!? Das Problem liegt darin, dass das Programm andauernd in einer Endlosschleife nach eintreffenden Events überprüft, was nicht unbedingt nützlich ist, wie man erkennen kann. Sie benötigen hierzu einen Timer, der nach jedem Schleifenaufruf das Programm ein wenig anhält. Dazu müssen Sie lediglich eine Funktion wie z. B. gettimeofday() und einen eigene delay()-Funktion zusammenbasteln – um damit eine entsprechende Verzögerung einzubauen.


Hinweis   Um auf den folgenden Seiten ein wenig Platz zu sparen, wird für diese Verzögerung extra eine (Header-)Datei (my_delay.h) ausgelagert, die Sie bei allen folgenden Listings benötigen.


/* my_delay.h */
#include <sys/time.h>
#define FRAME_LEN 50000
static struct timeval st, rt;
static int delay(int i) {
  struct timeval timeout;
  if (i>0) {
    timeout.tv_usec = i % (unsigned long) 1000000;
    timeout.tv_sec = i / (unsigned long) 1000000;
    select(0, NULL, NULL, NULL, &timeout);
  }
  return (i>0 ? i : 0);
}
static int time_diff(void) {
  int diff;
  gettimeofday(&rt, NULL);
  diff = (1000000*(rt.tv_sec-st.tv_sec))+(rt.tv_usec-st.tv_usec);
  st = rt;
  return diff;
}

Hierzu nochmals das Beispiel von eben (hallo_x.c), nur mit den neu erstellten Funktionen time_diff() und delay(). Es müssen nur die in Fettschrift hervorgehobenen Stellen hinzugefügt werden.

/* hallo_x2.c */
#include <stdio.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "my_delay.h"
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 300
/* Funktionsprototypen */
static int create_window (void);
static void process_event (XEvent report);
static void eventloop (void);
static void close_window (void);
extern int delay(int i);
extern int time_diff(void);
/* Globale Variablen */
static unsigned int width, height;
static Display *display;
static int screen;
static int depth;
static Window win;
/* Die Funktionen create_window(), process_event(), eventloop(),
 * close_window() bitte vom Listing hallo_x.c übernehmen oder das
 * komplette Listing auf der Buch-CD verwenden
 */
int main(int argc, char **argv) {
  int quit = 0;
  int pause = 0;
  int td;
  create_window();
  gettimeofday(&st, NULL);
  while(!quit) {
    gettimeofday(&rt, NULL);
    td = time_diff();
    pause = delay(FRAME_LEN - td + pause);
    st = rt;
    eventloop();
  }
  close_window();
  return EXIT_SUCCESS;
}

Die Veränderung des Listings sollte sich jetzt auf die Auslastung des Systems bezogen gegenüber der vorherigen Version positiv bemerkbar machen.

 << 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