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.8 Farben mittoptop


Hinweis   Verzeihen Sie mir hierbei gleich zu Beginn, dass ich das Thema Farben ein wenig zusammengeschrumpft habe. Das Ziel liegt hier vorwiegend in der Verwendung der Farben.


Die Farbe des Vorder- bzw. Hintergrundes eines Fensters ist Teil der Struktur des grafischen Kontextes GC. Verwendet haben Sie diesen bereits mit den Funktionen XSetForeground() und XSetBackground() wie folgt:

   XSetForeground(display, mygc, farbe1);
   XSetBackground(display, mygc, farbe2);

Bei farbe1 und farbe2 handelt es sich um einfache Integerzahlen. Was für Werte Sie nun für welche Farbe verwenden, ist wiederum nicht so einfach, da X ja unterschiedliche Server-Hardware unterstützt. Im Folgenden wird daher auf die einfachsten Grundlagen eingegangen.

Um eine Farbe auf dem Monitor darzustellen, wird ein Zahlentripel (Rot, Grün, Blau, kurz RGB) mit Intensitätswerten zwischen 0 und 255 verwendet (als Beispiel 8-Bit-Wert mit 256 Farben). Die Werte (R = 0, G = 0, B = 0) entsprechen der Farbe Schwarz, und die Werte (R = 255, G = 255, B = 255) stehen für eine weiße Farbe.

Realisiert wird diese Farbdarstellung bei X mit einer Colormap. Eine Colormap besteht somit aus 256 RGB-Tripeln, genannt Colorcells. XSetForeground() mit farbe1=11 z. B. schaltet dann um auf Farbe Nr. 11. Wie diese Farbe aussieht, legt der RGB-Wert fest, der in dieser Zelle eingetragen ist. Im einfachsten Fall, den Sie hier behandeln, wird von allen Anwendungen eine gemeinsame Colormap benutzt (Shared Colors) – sie wird auch als Default Colormap bezeichnet. Bei den Shared Colors wird folgender Weg eingeschlagen:

Wenn die Farbzellen von Nr. 0 bis n-1 definiert sind, gibt man dem X-Server eine gewünschte Farbe entweder als RGB-Wert oder in einer Stringform (dazu gleich mehr) an. Wenn eine Farbzelle bereits mit dieser Nummer verwendet wurde, dann wird einfach die Nummer dieser Zelle zurückgegeben. Existiert diese Farbe noch nicht, wird diese Zelle erst noch alloziiert – anschließend mit einem RGB-Wert versehen und zurückgegeben. Mehr als 256 Farben können dabei allerdings nicht pro Colormap definiert werden. Die Zelle kann auch wieder freigegeben und von derselben Anwendung neu belegt werden. Die Belegung der Colormap bleibt auch nach dem Ende der Anwendung erhalten und wird erst bei Beendigung des X-Servers gelöscht.

Jetzt folgen die Funktionen, die den eben beschriebenen Weg in der Praxis möglich machen. Zuerst wird gewöhnlich die Funktion DefaultColormap(display, screen) aufgerufen, womit die Struktur Colormap mit den Standardinformationen belegt wird, die zur weiteren Arbeit nötig sind.


Hinweis   Ob Ihre Hardware überhaupt Farbe anzeigen kann (so etwas soll es auch noch geben), können Sie sich mit der Funktion DefaultDeep(display, screen) ermitteln lassen. Solange der Rückgabewert >1 ist, können Sie Farbe darstellen.


Jetzt können Sie den RGB-Wert in einer Zelle der Colormap mit der Funktion XAllocColor() eintragen:

    Display *display; 
    Colormap cmap;
    XColor color;
    Status result;
    ...
    color.red = 16276; color.green=54271; color.blue=2000;
    result = XAllocColor(display, cmap, &color);
    if (result == 0)
       /* Fehler bei XAllocColor() */

XColor ist eine Struktur, bestehend (u. a.) aus den unsigned short-Werten red, green, blue sowie einem unsigned long-Wert pixel. XAllocColor() benutzt red, green, blue als Input und liefert pixel als Nummer einer neuen (oder auch alten, falls schon definiert) Farbzelle zurück.

Es wurde bereits erwähnt, dass die Farbwerte auch als Strings übergeben werden können. Dazu kann entweder die Funktion XAllocNamedColor() oder die Funktion XLookupColor() verwendet werden. Die Funktion XAllocNamedColor() funktioniert im Prinzip wie die Funktion XAllocColor(), nur dass der Name der Farbe hierbei direkt benutzt wird:

Display *display; 
Colormap cmap;
XColor exakt, color;
Status result;
...
result=XAllocNamedColor(mydisplay, cmap, "blue", &exact, &color);

Die Funktion trägt das zum Farbnamen gehörige RGB-Tripel wie XAllocColor() in eine Farbzelle ein. exact liefert in den Variablen exact.red, exact.green, exact.blue die exakten RGB-Werte zurück, die dieser Farbe entsprechen – exact.pixel bleibt undefiniert. Die Struktur XColor enthält neben einem möglichst gut an exact angenäherten RGB-Wert die alloziierte Zellennummer in color.pixel.

Eine zweite Alternative stellt die Funktion XLookupColor() zur Verfügung, die anschließend im Beispiel demonstriert wird. Natürlich setzt die Verwendung von XLookupColor() ein anschließendes XAllocColor() voraus.

Die Umsetzung der Namen in RGB-Tripel erfolgt über eine kleine Datenbank mit 8-Bit-RGB-Werten. Die zugehörige Textdatei heißt rgb.txt und befindet sich gewöhnlich im Verzeichnis /usr/lib/X11:

$  cat /usr/lib/X11/rgb.txt | less
255 250 250             snow
248 248 255             ghost white
248 248 255             GhostWhite 
... 
139   0   0             DarkRed
144 238 144             light green
144 238 144             LightGreen

Benötigen Sie den RGB-Wert einer Zelle, so können Sie diesen mit der Funktion XQueryColor() ermitteln.

    Display *display; 
    Colormap cmap;
    XColor color;
    ...
    color.pixel = 117;
    XQueryColor(mydisplay, cmap, &color);

Die Parameter werden genau umgekehrt zu XAllocColor() benutzt: Die Zellennummer wird in color.pixel übergeben, zurück kommt das eingetragene RGB-Tripel in color.red, color.green und color.blue.

Jetzt können Sie mithilfe des Grafikkontexts GC die alloziierte Farbe mit Funktionen wie XSetForeground() oder XSetBackground() verwenden.

Um am Ende die Farbzelle einer Colormap wieder freizugeben, müssen Sie die Funktion XFreeColors() verwenden. Es wird lediglich der Speicherplatz freigegeben; der Inhalt bleibt weiterhin unverändert bis zum Ende des X-Servers erhalten.

    Display *display; 
    Colormap cmap;
    XColor color;
    unsigned long pixels[MAX];
    int npixel;
    ...
    XFreeColors(mydisplay, cmap, pixels, MAX, 0);

MAX definiert hier, wie viele Einträge in der Colormap wieder freigegeben werden sollen. Die Zellennummern müssen in dem Feld pixels ab Element 0 eingetragen werden. Ist number = 1, so reicht eine einfache Variable für pixels aus – im Unterprogrammaufruf muss allerdings dann &pixels stehen. Wenn Sie versuchen, eine Zelle freizugeben, die Sie nicht alloziiert haben, bricht der X-Server das Programm mit einer Fehlermeldung ab.

Hier nochmals im Schnellüberblick die einzelnen Schritte, um Farben zu verwenden:

gp  Farbe vorhanden – DefaultDeep() (optional)
gp  Struktur Colormap mit Standardinformationen füttern – DefaultColormap()
gp  Umsetzen eines Namens in ein RGB-Tripel – XLookupColor() (falls nötig)
gp  RGB-Wert in einer Zelle der Colormap eintragen - XAllocColor() oder XAllocNamedColor()
gp  Farbe mit Grafikkontext verwenden – z. B. XSetForeground() oder XSetBackground()
gp  Farbzelle einer Colormap wieder freigeben – XFreeColors()

Das folgende Listing zeigt Ihnen die hier vorgestellten Funktionen in einer Funktion namens setcolourvianame() verpackt im Einsatz. Als Vorlage dient wieder das Listing vom Beispiel zuvor, nur dass diesmal die Grafikprimitiven in Farbe gezeichnet wurden.

/* xfont_Color.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "my_delay.h"
#define WINDOW_WIDTH 600
#define WINDOW_HEIGHT 450
/* 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);
static void draw_graphics (void);
static void print_text (GC, int, int, char *, char *);
static void print_font_demo (void);
static void setcolourvianame (GC, char *);
/* Globale Variablen */
static unsigned int width, height;
static Display *display;
static int screen;
static int depth;
static Window win; 
/* Schriftarten */
static char *my_fonts[] = {
   "-adobe-times-bold-r-normal--18*",
   "-adobe-times-bold-r-normal--12*",
   "-b&h-lucida-medium-r-normal-*-*-180-*-*-*-*-*-*",
   "-*-times-*-*-*-*-*-240-*-*-*-*-*-*"
};
enum{ ADOBE_BOLD_18, ADOBE_BOLD_12, LUCIA_NORMAL, TIMES };
/* Funktion erzeugt ein Fenster */
static int create_window (void) {
  char *window_name = "Farbe kommt ins Spiel";
  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 | KeyPressMask | ExposureMask);
  XMapWindow (display, win);
  return 1;
}
static void close_window (void) {
  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 Expose:      /* Expose-Event --> Bild zeichnen */
    if (report.xexpose.count == 0) {
      draw_graphics ();
    }
    break;
  case KeyPress:
    key = XLookupKeysym (&report.xkey, 0);
    switch (key) {
    case (XK_KP_4):
          case (XK_Left):
            case (XK_KP_Left):
                printf ("Links\n");
      break;
    case (XK_KP_6):
          case (XK_Right):
            case (XK_KP_Right):
                printf ("Rechts\n");
      break;
    case (XK_KP_8):
          case (XK_Up):
            case (XK_KP_Up):
                printf ("Nach oben\n");
      break;
    case (XK_KP_2):
          case (XK_Down):
            case (XK_KP_Down):
                printf ("Nach unten\n");
      break;
    default:
      break;
    }
    break;
  case ButtonPressMask:
    /* Rechte Maustaste gedrückt? */
    if (report.xbutton.button == 3)
      print_font_demo ();
    else
      printf ("Es wurde gedrückt:  %d\n",
         report.xbutton.button);
    break;
  default:
    break;
  }
}
static void draw_graphics (void) {
  GC mygc;                      /* neu: Grafikkontext */
  unsigned long black, white;   /* Nummern der Farben */
  int i;
  XPoint punkte[50];            /* Punkteschar */
  XPoint poly[4];               /* 4 Punkte fuer Polygon */
  /* Eine Schräge mithilfe von Punkten zeichnen */
  for (i = 0; i < 50; i++) {
    punkte[i].x = 470 + i;
    punkte[i].y = 200 + i;
  }
  /* geschlossenes Dreieck */
  poly[0].x = 200;
  poly[0].y = 200;   
  poly[1].x = 300;
  poly[1].y = 300;
  poly[2].x = 400;
  poly[2].y = 200;
  poly[3].x = 200;
  poly[3].y = 200;
  XMapRaised (display, win);   
  /* Farbnummern besorgen */
  black = BlackPixel (display, screen);
  white = WhitePixel (display, screen);
  /* Graphical Context erzeugen, Farben eintragen */
  mygc = XCreateGC (display, win, 0, 0);
  XSetForeground (display, mygc, black);
  XSetBackground (display, mygc, white);
  /* sicherheitshalber alles löschen */
  XClearWindow (display, win);
  /* Ein Rechteck zeichnen */
  XDrawRectangle (display, win, mygc, 10, 50, 50, 50);
  print_text ( mygc, 10, 320, "1. XDrawRectangle()",
     my_fonts[ADOBE_BOLD_12]);
  /* Blaue Farbe setzen */
  setcolourvianame (mygc,  "blue");
  /* Einen rechteckigen Bereich mit Farbe füllen */
  XFillRectangle (display, win, mygc, 110, 50, 50, 50);
  print_text ( mygc, 110, 240, "2. XFillRectangle()",
     my_fonts[ADOBE_BOLD_12]);
  /* Rote Farbe setzen */
  setcolourvianame (mygc,  "red");
  /* Einen Kreis zeichnen */
  XDrawArc (display, win, mygc, 210, 50, 50, 50, 0, 360 * 64);
  print_text ( mygc, 210, 320, "3. XDrawArc()",
     my_fonts[ADOBE_BOLD_12]);
  /* Grüne Farbe */
  setcolourvianame (mygc,  "green");
  /* Einen Kreisbereich füllen */
  XFillArc (display, win, mygc, 310, 50, 50, 50, 0, 360 * 64);
  print_text ( mygc, 310, 240, "4. XFillArc()",
     my_fonts[ADOBE_BOLD_12]);
  /* Grau */
  setcolourvianame (mygc,  "grey");
  /* Eine Ellipse zeichnen */
  XDrawArc (display, win, mygc, 410, 50, 60, 40, 0, 360 * 64);
  print_text ( mygc, 410, 320, "5. XDrawArc()",
     my_fonts[ADOBE_BOLD_12]);
  /* violet */
  setcolourvianame (mygc,  "violet");
  /* Einen Ellipsenbereich füllen */
  XFillArc (display, win, mygc, 510, 50, 60, 40, 0, 360 * 64);
  print_text ( mygc, 510, 240, "6. XFillArc()",
     my_fonts[ADOBE_BOLD_12]);
  setcolourvianame (mygc,  "grey50");
  /* Einen Halbkreis zeichnen */
  XDrawArc (display, win, mygc, 10, 200, 60, 40, 90*64, 180*64);
  print_text ( mygc, 10, 480, "7. XDrawArc()",
     my_fonts[ADOBE_BOLD_12]);
  setcolourvianame (mygc,  "grey25");
  /* Einen Halbkreis füllen */
  XFillArc (display, win, mygc, 110, 200, 60, 40, 90*64,180*64);
  print_text ( mygc, 110, 380, "8. XFillArc()",
     my_fonts[ADOBE_BOLD_12]);
  setcolourvianame (mygc,  "yellow");
  /* ausgefuelltes Polygon bzw. Dreieck */
  XFillPolygon(display, win, mygc, poly, 4, 0, CoordModeOrigin);
  setcolourvianame (mygc,  "black");
  print_text ( mygc, 250, 520, "9. XFillPolygon()",
     my_fonts[ADOBE_BOLD_12]);
  setcolourvianame (mygc,  "aquamarine");
  /* Eine schräge Linie mit Punkten */
  XDrawPoints(display, win, mygc, punkte, 50, CoordModeOrigin);
  print_text ( mygc, 470, 380, "10. XDrawPoints()",
     my_fonts[ADOBE_BOLD_12]);
  setcolourvianame (mygc,  "black");
  print_text (mygc, 150, 590,
     "Mit rechter Maustaste zum Font-Demo",
     my_fonts[ADOBE_BOLD_18]);
  XFlush (display);
  XFreeGC (display, mygc);   
}
/*  Die Funktionen print_text() und print_font_demo() übernehmen 
 *  Sie bitte aus dem Listing xfont.c, oder Sie können auch das
 *  komplette Beispiel von der Buch-CD verwenden
 */
  
static void setcolourvianame (GC myGC, char *colourname) {
  /* genaue RGB-Komponenten der Farbe */
  XColor rgbcolour;     
  /* am nächsten liegende RGB-Farben der Hardware */
  XColor hwarecolour;
  int status;
  /* Colormap */
  Colormap  myColourmap;            
  int myDepth;
  myDepth = DefaultDepth (display, screen);
  myColourmap = DefaultColormap (display, screen);
  screen = DefaultScreen (display);
  if (myDepth > 1) {
    status = XLookupColor (
       display, myColourmap,colourname,&rgbcolour,&hwarecolour );
    if (status != 0) {
      /* Farben für die Colormap reservieren */
      status = XAllocColor ( display, myColourmap, &hwarecolour);
      if (status != 0) {
        /* Zeichenfarbe setzen */
        XSetForeground (display, myGC, hwarecolour.pixel );
      } 
      else {
        printf ("Kann Farbzelle für %s nicht ermitteln\n",
           colourname);
        printf ("Verwende stattdessen Schwarz\n");
        XSetForeground(display, myGC,BlackPixel(display,screen));
      }
    } 
    else {
      printf ("Kann Farbe %s nicht finden\n", colourname);
      printf ("Verwende stattdessen Schwarz\n");
      XSetForeground(display, myGC, BlackPixel(display, screen));
    }
  } 
  else {
    printf("Keine Farbe in in dieser Tiefe:%d ?!\n", myDepth);
    printf ("Verwende stattdessen Schwarz \n");
    XSetForeground (display, myGC, BlackPixel(display, screen));
  }
}
 
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;
}

Einen Screenshot kann ich mir hierbei ersparen, da die Grafiken im Buch in Schwarz-Weiß gedruckt sind (der Screenshot entspräche dem vom Listing zuvor nur mit farbiger Darstellung).

 << zurück
  
  Zum Katalog
Zum Katalog: Linux-UNIX-Programmierung
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Linux-Server






 Linux-Server


Zum Katalog: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Katalog: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Katalog: Shell-Programmierung






 Shell-
 Programmierung


Zum Katalog: Linux Handbuch






 Linux Handbuch


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
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.


[Rheinwerk Computing]

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