Home Index Search Links About Us
[LinuxFocus Image]
[Navegation Bar]
  Neuigkeiten   Archiv   Firmen   Tips  

GLUT Programmierung: Fensterverwaltung

von Miguel Angel Sepúlveda

Aus dem Englischen ins Deutsche übersetzt von Franz-Josef Lücke

Einleitung

Fensterverwaltung

Verwendung von Unter-Fenstern

Tastatur

Text rendern

Zusammenfassung

Einleitung

Mit diesem Artikel setzen wir die Serie über GLUT fort, dem GL Utility Toolkit zur Entwicklung von OpenGL Anwendungen geschrieben von Mark Kilgard. Wie ich im vorherigen Artikel (Windows and Animations) bereits ausführte, ist GLUT ein sehr interessantes und hilfreiches Werkzeug für jeden OpenGL Entwickler, weil es die Entwicklung portabler Progamme ermöglicht. GLUT versteckt die Einzelheiten des Window Manager und der grafischen Oberfläche vor dem Entwickler.

Die GLUT Bibliothek enthält mehrere Programmierschnittstellen. In der heutigen Ausgabe beschreiben wir die Programmierschnittstelle Fenster Management. Wie der Name schon sagt, enthält sie Operationen zur Manipulation von Fenstern einer OpenGL Anwendung: Erzeugung, Entfernen, Verkleinern eines Fensters; Ausführung von Stapel-Operationen, Verstecken, Bewegen; und das Setzen von Titeln, Positionierung, etc..

Fenster Management Programmierschnittstelle

Im folgenden sind alle Funktionen aufgeführt, die das Fenster Management in GLUT in der Version 3.6 unterstützten:

int glutCreateWindow(char *name) Erzeugt ein neues "top-level window"
int glutCreateSubWindow(int win,
   int x, int y, int width, int height)
Erzeugt ein "sub-window"
void glutSetWindow(int winId) Macht das Fenster mit der ID winId zum aktuellen Fenster
int glutGetWindow(void) Holt die Identifikation des aktuellen Fensters.
void glutDestroyWindow(int winId) Löscht das Fenster mit der angegebenen Identifikation winId.
void glutPostRedisplay(void) Teilt dem GLUT Ereignissprozessor mit, daß das aktuelle Fenster erneut anzuzeigen.
void glutSwapBuffers(void) Tauscht die Puffer des aktuellen Fensters.
void glutPositionWindow(int x, int y) Wechselt die Position des Fensters.
void glutReshapeWindow(int width, int height) Wechselt die Größe und Breite des Fensters.
void glutFullScreen() Zeigt das aktuelle Fenster auf dem gesamten Bildschirm an.
void glutPopWindow(void)
void glutPushWindow(void)
Legt oder holt das aktuelle Fenster auf den Stapel.
void glutShowWindow(void)
void glutHideWindow(void)
void glutIconifyWindow(void)
Zeigt oder versteckt das aktuelle Fenster oder macht es zum Icon
void glutSetWindowTitle(char *name)
void glutSetIconTitle(char *name)
Setzt den Titel des Fensters oder des (Fenster) Icons

Verwendung von Sub-Fenstern

Die Verwendung der meisten Funktionen ist sehr einfach. Einige wurden bereits im ersten Artikel erläutert: glutPostRedisplay, glutCreateWindow, glutPositionWindow, glutSwapBuffers,..etc. Obgleich die neu eingeführten Funktionen einfach zu benutzen sind und die obige Beschreibung viel über ihre Funktion aussagt, wie z.B. glutSetIconTitle, glutFullScreen ist die Verwendung von Subfenstern nicht einfach. Wir werden als nächstes ein kurzes Beispiel besprechen und die Details der Durchführung diskutieren.

Hier ist der Quellkode eines kleinen OpenGL-GLUT Demonstrationsprogramms (../../common/March1998/example1.c, ../../common/March1998/Makefile). Es soll das folgende Demonstrieren: (a) Wie wird ein Subfenster angelegt? (b) Wie wird die Tastatur in einer OpenGL Applikation abgefragt? (c) Wie wird eine Textausgabe in OpenGL vorgenommen?

(Zum verfolgen der weiteren Ausführungen sollte der Quellkode von ../../common/March1998/example1.c zur Hand sein.)

[Bild des Programms example1]

Zuerst wird die main() Funktion beschrieben. Sie beginnt wie jede GLUT Anwendung mit der Initialisierung: Verarbeiten von Optionen der Kommandozeile. Auswahl des Anzeigemodus. Setzen der Fensterposition. Setzen der Fenstergröße. Weil die Applikation mehrere Fenster verwalten soll, muß die Identifikationsnummer der Fenster gespeichert werden, die von der Funktion glutCreateWindow zurückgegeben wird. Die Variable winIdMain ist das zugehörige Handle des Fensters. Anschließend werden die Rückruf (callback) Funktionen für das Hauptfenster definiert, sie behandelen alle dem Hauptfenster zugeordneten Ereignisse: eine Anzeigefunktion (mainDisplay) zum Zeichnen der Szene, die Funktion (mainReshape), die alle Transformationen des Fensterrahmens behandelt, eine (keyboard) Funktion, welche die Tastendrücke auswertet, sowie eine (idle) Funktion. Falls keine Ereignisse stattfinden, dann verbleibt das Programm in dieser Funktion (lesen Sie auch den Artikel Windows and Animations für eine detailierte Beschreibung der Funktion idle).

Bei der Arbeit mit der Programmbibliothek GLUT muß folgendes beachtet werden: Es gibt nur eine idle Rückruf Funktion. Die arbeitet global für alle Fenster der Applikation. Dieser Punkt muß beim Entwurf der idle() Funktion beachtet werden. Sie muß alle Fenster und Subfenster einer Applikation aktualisieren.

Anschließend wird das Subfenster erzeugt (winIDSub). Zum Erzeugen eines Subfensters müssen die folgenden Parameter übergeben werden: die Identifikationsnummer des Hauptfensters winIDMain, die Position des Subfensters bezogen auf das Koordinatensytem des Hauptfensters - die x und y Koordinaten werden in der Einheit Pixel angegeben- und die Breite und Höhe des Subfensters. Die Funktion liefert ein Handle für das erzeugte Fenster zurück. Nun werden die notwendige Rückruf (callback) Funktionen für das Fenster winIDSub implementiert. Unser Beispiel verfügt über zwei Rückruf (callback) Funktionen: eine zum Anzeigen (subDisplay) und eine zum Verändern der Abmessungen (subReshape).

Beim Öffnen eines Subfensters wird es von der GLUT-Bibliothek mit einem OpenGL Kontext ausgestattet, was ein wichtiger Leistungsnachteil bei der Benutzung von Subfenstern ist. Für jedes Subfenster muß der zugehörige Speicher der Grafikkarte aktualisiert werden. Da jedes Fenster einen eigenen OpenGL Kontext besitzt, verfügt jedes über ein eigenes Koordinatensystem. Im Beispielprogramm ../../common/March1998/example1.c werden die Kontexte von den Funktionen mainDisplay() und subDisplay() erzeugt. Im folgenden werden die beiden Funktionen erläutert. Sie sind einfach und ihr Aufbau ist auch einfach verständlich. Ergänzende Informationen enthält der Artikel ("Simple Polygon Rendering").

Die Funktion mainDisplay() zeichnet ein Dreick mit roten, grünen und blauen Spitzen. Dessen Farbfüllung berechnet OpenGL durch eine Interpolation zwischen den Farben. Vor dem Rendern des Dreiecks müssen wir die Funktion glRotate hinzufügen, die eine Rotation des Dreiecks um die z-Achse durchführt. Die z-Achse ist senkrecht auf dem Bildschirm angeordnet. Im Programm wird der Rotationswinkel (spin) in der Funktion idle kontinuierlich erhöht, weshalb beim Betrachter die Illusion eines rotierenden Dreiecks entsteht.

Das Subfenster wird durch die Variable winIdSub beschrieben. Die Programmierung des Subfensters ist sehr gradlinig. Das Subfenster wird mit einer grauen Hintergrundfarbe gefüllt, ein grüner Rahmen gezeichnet und zu guter Letzt der Text gerendert. Später wird das Rendern von Text mit GLUT erklärt. Im Augenblick reicht es zu wissen, daß die Funktion glRasterPos2f(x, y) die Position des Textes setzt. Dabei muß beachtet werden, daß das Subfenster ein eigenes Koordinatensystem besitzt. Es wurde in der subReshape() definiert.

In dem Beispiel dient das Subfenster als Anzeige für die Animationsdaten. Bei dieser einfachen Anwendung hätte man den Text auch direkt in das Hauptfenster ausgeben können, was eine schnellere Programmausführung bewirkt hätte. Unter bestimmten Umständen ist die Nutzung eines Subfensters zur Textausgabe erforderlich, z.B. falls die Animation dreidimensional ist und der Text nicht deformiert dargestellt werden soll. Bei einer solchen Anwendung ist die Verwendung eines Subfensters sinnvoll, da es eine vollständige Trennung zwischen der dreidimensionalen Animation und der Textausgabe ermöglicht.

Es gibt einen entscheidenden Unterschied zwischen der Funktion zum Ändern der Abmessungen eines Hauptfensters und der eines Subfensters. Falls ein Ereignis mit der Meldung "Abmessungen" ändern eintritt, dann wird die Zeichenfunktion des Hauptfensters aktiviert, die heißt im Beispiel mainReshape(). Deshalb muß die reshape Funktion des Hauptfensters die reshape Funktion des Subfensters subReshape aufrufen. Dieses ist auch sinnvoll, da die Position und Größe des Subfensters von der Position und Größe des Hauptfensters abhängt. Im Quellkode der Funktion mainReshape() wird zuerst die Projektionsmatrix des Hauptfensters gesetzt und anschließend die reshape Funktion des Subfensters aufgerufen, wobei die Breite und Größe relativ zum Hauptfenster übergeben werden.

Es wurde schon vorher darauf hingewiesen, daß die idle() Funktion alle Fenster der OpenGL Appliktion aktualisieren muß. In dem Beispiel aktualisiert die idle() Funktion zuerst die Statusvariablen für die Animation (time und spin) und ruft die Routinen zum Zeichnen des Hauptfensters und des Subfensters auf.

Die Tastatur

Ich habe zwei Hotkeys zum Programm hinzugeführt. Durch das Drücken der Taste "i" kann die Textanzeige ein und ausgeschaltet werden. Durch das Betätigen der Taste "q" wird die Applikation beendet. Probieren Sie es aus :-).

Jedesmal wenn eine Taste gedrückt wird, wird dieses Ereignis von der Ereignisroutine der GLUT-Bibliothek registriert. Dieses Ereignis wird von der Tastatur Rückruf (callback) Funktion behandelt. Im Prinzip besitzt jedes Fenster eine eigene Rückruf (callback) Funktion. Wenn die Position der Maus innerhalb eines angegebenen Fensters liegt oder Sub-Fernsters liegt oder ein Tastatur-Ereignis auftritt, dann wird die diesem Fenster zugeordnete Tastatur-Rückruf Funktion aufgerufen. Die Rückruf (callback) Funktion besitzt die folgenden Parameter: unsigned char Taste und die aktuelle (x-, y-) Position der Maus. Im Beispiel ../../common/March1998/example2.c werden die Parameter x,y nicht verwendet. Ich bin aber sicher, Sie können sich Anwendungen für dieses hübsche Feature ausdenken.

Nur das Hauptfenster besitzt eine eigene Tastatur Rückruf (callback) Funktion. Befindet sich der Cusor innerhalb des Subfensters und es werden die Tasten "i" oder "q" gedrückt, so wird nichts passieren. Falls bei der Initialisierung eines Fensters keine Rückruf (callback) Funktion angegeben wird, dann werden die Tastendrücke ignoriert. Dieses muß bei der Erstellung einer Applikation mit mehreren Fenstern beachtet werden.

Es soll noch folgendes erwähnt werden. Nach dem Aufruf der Funktion glutKeyBoardFunc() mit dem Parameter NULL werden alle Tastendrücke ignoriert.

Text rendern

Rendern von Text mit OpenGL und GLUT ist nervig! Dies zu sagen tut mir leid, aber es ist die Wahrheit. Ich weiß nicht, warum die OpenGL Bibliothek das Rendern von Text vernachlässigt. Die alte GL Bibliothek von SGI verfügte über einige wenige effiziente Funktion zum Rendern von Text im Grafikmodus. Es gab eine zusätzliche Hilfsbibliothek zum Ändern der Schriftfonts. Dagegen besitzt OpenGL nur wenige und einfache Anweisungen zum Rendern von Bitmaps. Aus diesem Grund wird zur Darstellung von Text für jedes Zeichen eine Bitmap benötigt. Zu beachten sind dabei die Auflösung, die Text-Skalierung ... Sie sagen es!

Die GLUT Bibliothek bietet eine Lösung für dieses Dilemma. Sie enthält die Funktion glutBitmapCharacter, die einen Buchstaben rendert und an der Position ausgibt, die von der Funktion glRasterPos angegeben wird. Zur Bibliothek habe ich einige Funktionen hinzugefügt, z.B. drawString() und drawStringBig(), die das Arbeiten beim Rendern von Zeichenketten erleichtern.

Zusammenfassung

Dieser Artikel ist eine sehr einfache Einführung in der Anwendung von GLUT Subfenstern. An dieser Stelle weise ich darauf hin, daß es eine gute Idee ist, mit Subfenstern zu experimentieren und sie zu testen, denn sie sind nicht auf allen Plattformen vollständig verfügbar. Anwender, die eine Grafikkarte mit "3Dfx-Chipsatz" verwenden, werden feststellen, daß die Subfenster aufgrund von Hardwarebeschränkungen noch nicht richtig funktionieren. Auf verschiedenen Plattformen habe ich bei der Verwendung eine große Geschwindigkeitseinbuße festgestellt. Auf meinem Linuxsystem, einer Alpha mit einer 2Mb Matrox Millenium Grafikkarte, erzielt eine Applikation mit einem Subfenster nur die halbe Geschwindigkeit wie dieselbe Applikation ohne Subfenster. Der X-Server der Alpha unterstützt unglücklicherweise keine Möglichkeiten der Hardwarebeschleunigung der Grafikkarte. Andererseits konnte ich bei dem gleichen Test auf einem System mit Windows95, einer 2Mb ATI RageII Grafikkarte und dem OpenGL Treiber von SGI keinen Geschwindigkeitsverlust feststellen.

Die Entwicklung von Linux schreitet sehr schnell voran, so daß in der nahen Zukunft vielleicht viele der Geschwindigkeitsprobleme und Inkompatibilitäten verschwinden werden. Sie existieren zur Zeit, deshalb sollte man bei der Verwendung von mehreren Fenstern vorsichtig sein.

Fortgeschrittene Anwender können die Probleme bei der Verwendung von Subfenstern durch das gezielte Manipulieren des Matrix Stacks umgehen. Bitte seien Sie nachsichtig mit mir, wenn ich diese Möglichkeit erst in einem späteren Artikel erläutern werde ;)..


Weitere Informationen:
© 1998 Miguel Angel Sepulveda
Diese Webseite wird unterhalten von Miguel A. Sepulveda und Franz-Josef Lücke.