Para construir uma aplicação gráfica mínima no Linux usando X11, EGL e OpenGL ES, é necessário criar uma janela nativa, configurar uma superfície EGL e inicializar um contexto de renderização. O fluxo pode ser dividido em duas etapas principais: inicialização do X11 e setup do EGL.
- Inicialização do X11
A primeiar tarefa é abrir uma conexão com o servidor X, obter a janela raiz e criar a janela da aplicação. O conjunto de eventos deve ser escolhido de acordo com a interação desejada.
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#define LARGURA 800
#define ALTURA 600
Display *dpy = XOpenDisplay(NULL);
if (!dpy) {
fprintf(stderr, "Não foi possível abrir o display X11\n");
return -1;
}
Window root = DefaultRootWindow(dpy);
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;
Window win = XCreateWindow(
dpy, root,
0, 0, LARGURA, ALTURA, 0,
CopyFromParent, InputOutput, CopyFromParent,
CWEventMask, &swa);
Após criar a janela, é importante registrar o protocolo de fechamento WM_DELETE_WINDOW, mapear a janela na tela e definir um título.
Atom wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
XSetWMProtocols(dpy, win, &wm_delete, 1);
XMapWindow(dpy, win);
XStoreName(dpy, win, "Exemplo X11 + EGL + GLES");
- Configuração do EGL
O EGL atua como ponte entre o sistema de janelas X11 e a API de renderização OpenGL ES. Primeiro obtém-se uma referência ao display EGL a partir do display X11, depois inicializa-se a biblioteca e escolhe-se uma configuração compatível.
#include <EGL/egl.h>
EGLDisplay egl_dpy = eglGetDisplay(dpy);
if (egl_dpy == EGL_NO_DISPLAY) {
fprintf(stderr, "Display EGL inválido\n");
return -1;
}
EGLint major, minor;
if (!eglInitialize(egl_dpy, &major, &minor)) {
fprintf(stderr, "Falha ao inicializar EGL\n");
return -1;
}
EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_STENCIL_SIZE, 8,
EGL_NONE
};
EGLConfig cfg;
EGLint num_configs;
if (!eglChooseConfig(egl_dpy, attribs, &cfg, 1, &num_configs) || num_configs < 1) {
fprintf(stderr, "Nenhuma configuração EGL disponível\n");
return -1;
}
Com a configuração escolhida, cria-se a superfície de janela, o contexto e ativa-se o contexto para a thread atual.
EGLSurface surf = eglCreateWindowSurface(egl_dpy, cfg, win, NULL);
if (surf == EGL_NO_SURFACE) {
fprintf(stderr, "Falha ao criar a superfície EGL\n");
return -1;
}
EGLint ctx_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
EGLContext ctx = eglCreateContext(egl_dpy, cfg, EGL_NO_CONTEXT, ctx_attribs);
if (ctx == EGL_NO_CONTEXT) {
fprintf(stderr, "Falha ao criar o contexto EGL\n");
return -1;
}
eglMakeCurrent(egl_dpy, surf, surf, ctx);
- Loop de Eventos e Renderização
Uma vez configurado o contexto, o programa entra em um loop que processa eventos do X11, atualiza a cena e apresenta o resultado na tela.
#include <GLES3/gl3.h>
int executando = 1;
XEvent ev;
while (executando) {
while (XPending(dpy) > 0) {
XNextEvent(dpy, &ev);
if (ev.type == ClientMessage &&
(Atom)ev.xclient.data.l[0] == wm_delete) {
executando = 0;
}
}
glClearColor(0.1f, 0.15f, 0.25f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl_dpy, surf);
}
- Liberação de Recursos
Antes de encerrar, o contexto e as superfícies devem ser desvinculados e destruídos, seguidos do fechamento da conexão com o X.
eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(egl_dpy, ctx);
eglDestroySurface(egl_dpy, surf);
eglTerminate(egl_dpy);
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
- Compilação
Para compilar o exemplo em um sistema Linux com o Mesa e X11, utilize um comando semelhante a:
gcc main.c -o app -lX11 -lEGL -lGLESv2