Fenstergrößen mit XLIB und GLES

EGL und die XLIB bereiteten mir schon vor einigen Jahren Schwierigkeiten und ich bin offenbar nicht der einzige. Selbst die Raspberry-PI Edition von Minecraft kam beim Verändern der Fenstergröße durcheinander.

Nun treffe ich erneut auf diese Umgebung im Zuge der OpenGL Implementierung im GATE Projekt.
In diesem Sinne fasse ich mal meinen aktuellen Stand zusammen.


Viele Spiele machen es sich leicht. Sie funktionieren nur im Vollbildmodus und nicht selten stürzt eines ab, wenn sich die Auflösung ändert oder man z.B. einen zweiten Monitor an oder absteckt und die Fenster damit den Bildschirm wechseln.

Das läuft also nach dem Schema: Screen einmal initialisieren und der bleibt für immer so. Heute reagieren die meisten Apps zum Glück korrekt, aber Fehler passieren und früher war das noch viel schlimmer.

Deshalb möchte ich, dass in meinen Anwendungen Fenstergrößen immer vom Nutzer frei bestimmbar sein sollen.
Und im Fall der XLIB bedeutet das bei meinen OpenGL-Experimenten natürlich ein bisschen mehr Aufwand.

ConfigureNotify Event

Wenn man in seiner X11 Event-Mask per XSelectInput die Flags StructureNotifyMask und SubstructureNotifyMask gesetzt hat, dann liefert XNextEvent auch ConfigureNotify Ereignisse und dort drinnen steht, wo und wie groß der Benutzer sein Fenster haben möchte.

Das ist also ein guter Ort um Größenveränderungen zu erkennen und entsprechende Callbacks auszulösen.

Ich hatte anfangs den Fehler gemacht, das Ereignis ConfigureRequest abzufangen aber dieses Unterfangen bereitete mir nur Probleme und Crashes.

Doch die Dimensionen des Fensters beziehen sich vor allem auf die äußeren Größen und müssen nicht zwingend mit dem Anzeigebereich übereinstimmen.

Und deshalb nutze ich im Event-Callback XGetWindowAttributes() um die tatsächliche aktuelle Größe des Fenster-Clientbereiches auszulesen.

glViewport macht den Rest

Die vorhin abgeholten Fenstergrößen werden dann in der Render-Routine in den ersten Zeilen an glViewport() übergeben, also glViewport(0, 0, width, height).

Das Verhältnis von Höhe und Breite braucht zusätzlich noch beim Setzen des AspectRatio Wertes beim Definieren der Perspektive in der Projektionsmatrix.

Das erledigt oft auch die Hilfsfunktion gluPerspective(), doch leider fehlt die in EGL/GLES Implementierungen.
Dank des weltweiten Netzes konnte ich eine alternative gluPerspective Implementierung finden, dich ich hier nicht vorenthalten möchte:

 1void glPerspective(GLfloat fovy, GLfloat aspect, 
 2                   GLfloat znear, GLfloat zfar)
 3{
 4  GLfloat f = (GLfloat)(1.0f / tan((fovy * PI / 180.0f) / 2.0f));
 5  GLfloat m[16] = {
 6    f / aspect, 0, 0, 0,
 7    0, f, 0, 0,
 8    0, 0, (zfar + znear) / (znear - zfar), -1.0f,
 9    0, 0, (2.0f * zfar * znear) / (znear - zfar), 0
10  };
11  glMultMatrixf(m);
12}

Und eigentlich war es das auch schon, denn nun wird die nächste Szene korrekt an die neue Fenstergröße angepasst gezeichnet.

Fazit

Am Ende des Tages läuft also auch unter X11 eine EGL Anwendung, die sich genau so verhält wie eine OpenGL Anwendung unter Windows.
Denn unter Windows konnte ich den entsprechenden WM_SIZE Code quasi auswendig niederschreiben … doch XLIB Event Details habe ich leider nicht mehr so genau im Hinterkopf.

Schließlich liegen meine X11 Experimente auch schon 10 Jahre zurück und in der Praxis schreibt man nur selten Code auf dieser Ebene.

Und was die GL-Matrizen anbelangt, da muss auch unter Android rechnen, dass sie sich ändern können.
Denn wenn man das Smartphone kippt und keine Rotationssperre gesetzt ist, dann ändert sich auch dort der Aspect-Ratio des Anzeigebereichs.