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.5 Events  downtop

Ein Event (Ereignis) ist eine Nachricht vom Server an den Client, womit der Client über eine bestimmte Aktion informiert wird. Meistens handelt es sich dabei um Mausaktionen oder Tastatureingaben. Im Prinzip wird über Events das ganze Programm gesteuert. Die Events sind, um es genau zu nehmen, auch wieder nichts anderes als eine (komplexe) Datenstruktur, die vom Server an den Client geschickt wird. Folgende Dinge müssen immer gemacht werden, damit die Events auch funktionieren:

gp  Event-Maske setzen – Es macht keinen Sinn, wenn der Server auf jedes Event eines jeden Speicherobjektes reagiert – von der Überlastung des X-Servers dabei ganz zu schweigen. Welchen Sinn würde es denn ergeben, wenn der Server die Bewegung des Mauscursors, der sich über einem Button befindet, registriert und an den Client schickt? Daher verfügt X über eine Event-Maske, womit es möglich ist, von allen Events, die es gibt, nur eine gewisse Auswahl zu selektieren, die in der Anwendung auch wirklich benötigt wird.
gp  Event-Bearbeitungsschleife – Klar, dass bei regen Zeiten sehr viele Events auftreten, die nicht alle auf einmal abgearbeitet werden können. Daher verwendet man eine Event-Bearbeitungsschleife. In der Schleife holen Sie aus einem Event-Puffer ein Event nach dem anderen ab (sofern eines vorhanden ist) und werten dieses aus – mit einer anschließenden Reaktion, die Sie für das Event vorsehen.

Rheinwerk Computing

14.5.1 Event-Maske setzen  downtop

Der erste Schritt besteht also darin, eine Event-Maske zu setzen, die man für die Anwendung benötigt. Dazu wird der Befehl XSelectInput() wie folgt verwendet:

XSelectInput(display, win,
             ButtonPressMask | ButtonReleaseMask | KeyPressMask |
             KeyReleaseMask  | EnterWindowMask   | ExposureMask |
             LeaveWindowMask );
XMapWindow (display, win);

Wie Sie daran erkennen können, werden mehrere Masken des dritten Parameters durch ein bitweises ODER verknüpft. Die ersten beiden Parameter sind so weit nichts Neues mehr. Des Weiteren sollte dieser Befehl noch vor der Funktion XMapWindow() aufgerufen werden, da beim Erzeugen des Fensters schon das Expose-Event verschickt wird. Welche Event-Maske(n) Sie hierfür verwenden sollten, hängt von Ihrer Anwendung ab. Hierzu ein Überblick zu einigen Event-Masken, die auch im Buch angewendet werden.


Tabelle 14.1    Häufig verwendete Event-Masken und deren Events

Event-Maske Bedeutung Events
ButtonPressMask Maustaste im Fenster gedrückt ButtonPress
ButtonReleaseMask Maustaste im Fenster losgelassen ButtonRelease
KeyPressMask Eine Taste niedergedrückt KeyPress
KeyReleaseMask Eine Taste losgelassen KeyRelease
EnterWindowMask Mauscursor tritt ins Fenster ein EnterNotify
LeaveWindowMask Mauscursor verlässt das Fenster LeaveNotify
ExposureMask Anwendung muss etwas zeichnen Expose


Rheinwerk Computing

14.5.2 Event-Bearbeitungsschleife  downtop

Wenn Sie die Maske für die Events gesetzt haben, können Sie die Event-Bearbeitungsschleife erstellen, die von jetzt an bis zur Beendigung des Programms durchlaufen wird. Diese Schleife wird im Beispiel in der Hauptfunktion mit folgendem Konstrukt ausgeführt:

   while (!quit) {
      /* +++ Timer Anfang +++ */
      gettimeofday (&rt, NULL);
      td = time_diff ();
      pause = delay (FRAME_LEN - td + pause);
      st = rt;
      /* +++ Timer Ende */
      eventloop ();
   }

Der Timer stellt »nur« eine Entlastung für die CPU dar, was bereits im Listing zuvor beschrieben wurde, und hat somit nicht direkt etwas mit der Event-Bearbeitungsschleife zu tun. Die Funktion eventloop(), die in dieser Schleife die Events bearbeitet, sieht so aus:

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);
   }
}

Zuerst flushen Sie den Puffer der X-Aufrufe mit der Funktion XFlush(), der ja ansonsten nur entleert würde, wenn er voll ist. Anschließend überprüfen Sie mit der Funktion XPending(), ob überhaupt ein Event in der Event-Warteschlange vorhanden ist. Wenn nicht, wird der nächste Schleifenaufruf erst gar nicht ausgeführt, und die Anwendung kehrt zur main()-Funktion in die Event-Bearbeitungsschleife zurück. Befindet sich mindestens ein Event im Event-Puffer (num_events != 0), holen Sie dieses mit der Funktion XNextEvent() ab. Der erste Parameter ist wie immer der des Servers, der zweite ist eine Adresse vom Typ XEvent. XEvent ist wiederum eine Struktur, die das angekommene Event beinhaltet. Sie könnten dieses Event zwar jetzt schon abarbeiten, aber aus Übersichtlichkeitsgründen übergeben Sie XEvent xev an die Funktion process_event(), die das angekommene Event abarbeitet (dazu gleich mehr).

Sicherlich fällt Ihnen an der Funktion XNextEvent() auf, dass hier kein Parameter win für das Fenster verwendet wird. Aber in der Event-Struktur XEvent ist dazu natürlich eine Kennung enthalten, zu welchem Fenster welches Event gehört, für den Fall, wenn mehrere Fenster pro Anwendung geöffnet wurden.


Rheinwerk Computing

14.5.3 Abarbeitung der Events  downtop

Zur Abarbeitung der einzelnen Events wurde hier die Funktion process_event() mit dem auftretenden Event (XEvent) aufgerufen:

static void process_event (XEvent report) {
...
   switch (report.type) {
     /* Events abfragen mit case-Statements */
   }
}

Rheinwerk Computing

14.5.4 Tastatur-Event  downtop

Ein Tastatur-Event wie KeyPress oder KeyRelease erhalten Sie gewöhnlich, wenn der Window-Manager den Tastaturfokus an ein Fenster übergeben hat. Meistens ist dies der Fall, wenn sich der Mauszeiger darin befindet. Wenn ein KeyPress-Event oder ein KeyRelease-Event aufgetreten ist, können Sie mit einer Behandlungsroutine das Zeichen lesen. Welches Zeichen das denn nun war, kann z. B. mit der Funktion XLookupString() ermittelt werden:

XLookupString (&report.xkey, buf, bufsz, &key, NULL);

Die Funktion übersetzt ein Tastatur-Event, das Sie zuvor mit der Funktion XLookupKeysym() ermittelt haben, in einen String. Der erste Parameter ist das Event, im zweiten wird anschließend der String mit maximal bufsz (dritter Parameter) Bytes beschrieben. Der vierte Parameter enthält den Tastaturcode, den Sie mit XLookupKeysym() ermittelt haben. Der letzte Parameter wird hier nicht benötigt.

Solange es sich bei der Taste um ein ASCII-Zeichen wie Buchstaben- und Zahlentasten oder die Zeichen: =\ + (DEL) [ ] (ESC) / ' ` ; (˙_) . , (CR) (LF) (ć___) handelt, ist man mit XLookupString() gut bedient ((ş_) wird auch berücksichtigt). Wenn es sich allerdings um kein ASCII-Zeichen handelt, ist der String buf von XLookupString() leer. Dies ist z. B. beim Betätigen der Tasten (Ş), (Ctrl), (F1), (F2) usw. der Fall. In diesem Fall kann man den Tastaturcode, der von der Funktion XLookupKeysym() zurückgegeben wird, mit der Funktion XKeysymToString() in einen String konvertieren. Den Tastaturcode selbst können Sie sich mit der Funktion XKeysymToKeycode() ermitteln lassen. Hierzu der Vorgang beim Drücken bzw. Loslassen einer Taste:

...
KeySym key;
char buf[128] = { 0 };
int bufsz = 128;
...
switch (report.type) {
case KeyPress:
   key = XLookupKeysym (&report.xkey, 0);
   XLookupString (&report.xkey, buf, bufsz, &key, NULL);
   if (strlen (buf) != 0 && buf[0] != '\n' && buf[0] != '\r')
      printf ("Taste'%s' (Tasturcode: %d) wurde gedrückt\n",
         buf, XKeysymToKeycode (display, key));
   else
      printf ("Taste '%s' (Tasturcode: %d) wurde gedrückt\n",
         XKeysymToString (key), XKeysymToKeycode (display, key));
   break;
case KeyRelease:
   printf ("Taste wieder losgelassen\n");
   break;
...

Rheinwerk Computing

14.5.5 Mausbutton-Event  downtop

Wenn eine Maustaste innerhalb eines Fensters gedrückt bzw. losgelassen wurde, treffen die Events ButtonPress bzw. ButtonRelease ein. Wenn das Fenster bei gedrückter Maustaste verlassen und außerhalb losgelassen wird, wird dennoch das Event ButtonRelease gesendet. Wollen Sie wissen, welche Maustaste gedrückt bzw. losgelassen wurde, müssen Sie report.xbutton.button (wobei report vom Typ XEvent ist) auswerten. Darin befindet sich ein Integerwert mit folgenden Bedeutungen:


Tabelle 14.2    Integerwerte für das Drücken eines bestimmten Mausbuttons

Mausaktion Integerwert
linke Maustaste 1
rechte Maustaste 3
mittlere Maustaste 2
Scrollrad nach oben 4
Scrollrad nach unten 5

Wollen Sie hingegen überprüfen, an welcher Stelle die Maustaste gedrückt bzw. losgelassen wurde, müssen Sie für die X-Position report.xbutton.x und für die Y-Position report.xbutton.y auswerten. Dabei wird jeweils ein Wert relativ von der linken oberen Seite des Fensters zurückgeliefert.


Rheinwerk Computing

14.5.6 Expose-Event  downtop

Wenn das Fenster erzeugt, verkleinert, vergrößert, minimiert oder von einem anderen Fenster überdeckt wurde, wird das Expose-Event mitgeschickt. Dieses Event zeigt der Anwendung an, dass der Fensterinhalt neu gezeichnet werden muss. Beim Verschieben hingegen wird kein Expose-Event gesendet, da hierbei der Window-Manager dafür sorgt, dass ein Fenster wieder hergestellt wird.


Hinweis   Das Expose-Event ist weitaus anspruchsvoller, als in diesem Buch dargestellt wird, denn häufig muss nur ein rechteckiger Detailausschnitt des Fensters neu gezeichnet werden. In diesem Fall wird eine Folge von Expose-Events mit einer Variablen myevent.xexpose.count > 0 erzeugt.



Rheinwerk Computing

14.5.7 EnterWindowMask – LeaveWindowMask  downtop

Sobald der Cursor das Fenster betritt, wird das Event EnterNotify gesendet, und beim Verlassen des Cursors das Event LeaveNotify.


Rheinwerk Computing

14.5.8 Listing zu den Events  toptop

Alle Events, die Ihnen im Beispiel hier erläutert wurden, sollen jetzt in der Praxis getestet werden. Das jeweilige Event, das eintrifft, wird auf der Konsole ausgegeben, von wo die X-Anwendung gestartet wurde.

/* events.c */
#include <stdio.h>
#include <string.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
#define MAUS_LINKS  1
#define MAUS_RECHTS 3
#define MAUS_MITTE  2
#define SCROLL_UP   4
#define SCROLL_DOWN 5
/* 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; 
/* Funktion erzeugt ein Fenster */
static int create_window (void) {
  char *window_name = "Event-Behandlung";
  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, 10, 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 );
  XSelectInput (display, win,
      ButtonPressMask | ButtonReleaseMask | KeyPressMask    |
      KeyReleaseMask  | EnterWindowMask   | LeaveWindowMask |
      ExposureMask );
  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;
  char buf[128] = { 0 };
  int bufsz = 128;
  char position[50];
  switch (report.type) {
  case KeyPress:
    key = XLookupKeysym (&report.xkey, 0);
    XLookupString (&report.xkey, buf, bufsz, &key, NULL);
    if (strlen (buf) != 0 && buf[0] != '\n' && buf[0] != '\r')
      printf ("Taste'%s' (Tasturcode: %d) wurde gedrückt\n",
         buf, XKeysymToKeycode (display, key));
    else
      printf ("Taste '%s' (Tasturcode: %d) wurde gedrückt\n",
         XKeysymToString (key), XKeysymToKeycode (display, key));
    break;
  case KeyRelease:
    printf ("Taste wieder losgelassen\n");
    break;
  case ButtonPress:
    switch (report.xbutton.button) {
    case MAUS_LINKS:
      sprintf (position, "(X,Y)=(%d,%d).", report.xmotion.x,
         report.xmotion.y);
      printf ("Linke Maustaste wurde gedrückt an Pos.: %s\n",
         position);
      break;
    case MAUS_RECHTS:
      sprintf (position, "(X,Y)=(%d,%d).",
         report.xmotion.x, report.xmotion.y);
      printf ("Rechte Maustaste wurde gedrückt an Pos.: %s\n",
         position);
      break;
    case MAUS_MITTE:
      sprintf (position, "(X,Y)=(%d,%d).",
         report.xmotion.x, report.xmotion.y);
      printf ("Mittlere Maustaste gedrückt an Pos.: %s\n",
         position);
      break;
    case SCROLL_UP:
      sprintf (position, "(X,Y)=(%d,%d).",
         report.xmotion.x, report.xmotion.y);
      printf ("Mausrad nach oben gescrollt an Pos.: %s\n",
         position);
      break;
    case SCROLL_DOWN:
      sprintf (position, "(X,Y)=(%d,%d).",
         report.xmotion.x, report.xmotion.y);
      printf ("Mausrad nach unten gescrollt an Pos.: %s\n",
         position);
      break;
    }
    break;
  case ButtonRelease:
    switch (report.xbutton.button) {
    case MAUS_LINKS:
      sprintf (position, "(X,Y)=(%d,%d).",
         report.xmotion.x, report.xmotion.y);
      printf ("Linke Maustaste losgelassen an Pos.: %s\n",
         position);
      break;
    case MAUS_RECHTS:
      sprintf (position, "(X,Y)=(%d,%d).",
         report.xmotion.x, report.xmotion.y);
      printf ("Rechte Maustaste losgelassen an Pos.: %s\n",
         position);
      break;
    case MAUS_MITTE:
      sprintf (position, "(X,Y)=(%d,%d).",
         report.xmotion.x, report.xmotion.y);
      printf ("Mittlere Maustaste losgelassen an Pos.: %s\n",
         position);
      break;
    }
    break;
  case EnterNotify:
    XSetInputFocus (display, win, RevertToParent, CurrentTime);
    printf ("Cursor 'betritt' das Fenster\n");
    break;
  case LeaveNotify:
    XSetInputFocus (display, win, RevertToParent, CurrentTime);
    printf ("Cursor 'verlässt' das Fenster\n");
    break;
  case Expose:
    printf("ExposeEvent: Bild sollte neu gezeichnet werden\n");
    break;
  default:
    break;
  }
}
 
int main (int argc, char **argv) {
  int quit = 0;
  int pause = 0;
  int td;
  create_window ();
  gettimeofday (&st, NULL);
  while (!quit) {
    /* +++ Timer Anfang +++ */
    gettimeofday (&rt, NULL);
    td = time_diff ();
    pause = delay (FRAME_LEN - td + pause);
    st = rt;
    /* +++ Timer Ende */
    eventloop ();
  }
  close_window ();
  return EXIT_SUCCESS;
}

Das Programm bei der Ausführung:

$ gcc -o events events.c -L/usr/X11R6/lib -lX11
$ ./events
---[nach Aktionen auf dem Fenster die Ausgabe der Konsole]---
Mausrad nach oben gescrollt an Pos.: (X,Y)=(182,169).
Mausrad nach unten gescrollt an Pos.: (X,Y)=(182,169). 
Cursor 'verlässt' das Fenster
Cursor 'betritt' das Fenster
Cursor 'verlässt' das Fenster
ExposeEvent: Bild sollte neu gezeichnet werden
Cursor 'betritt' das Fenster
Rechte Maustaste wurde gedrückt an Pos.: (X,Y)=(368,276). 
Linke Maustaste wurde gedrückt an Pos.: (X,Y)=(368,276).
Linke Maustaste losgelassen an Pos.: (X,Y)=(368,276).
Mausrad nach unten gescrollt an Pos.: (X,Y)=(368,276). 
Cursor 'verlässt' das Fenster 
Taste'e' (Tasturcode: 26) wurde gedrückt
Taste wieder losgelassen
Taste 'F2' (Tasturcode: 68) wurde gedrückt
Taste wieder losgelassen
Taste 'F4' (Tasturcode: 70) wurde gedrückt
Taste wieder losgelassen
Taste 'Home' (Tasturcode: 97) wurde gedrückt
...
 << 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