Skip to content

Instantly share code, notes, and snippets.

@jfuerth
Last active December 26, 2023 08:01
Show Gist options
  • Save jfuerth/82b816510bb2cc063c9945baf1093fd9 to your computer and use it in GitHub Desktop.
Save jfuerth/82b816510bb2cc063c9945baf1093fd9 to your computer and use it in GitHub Desktop.
Self-contained example of an EGL + X11 OpenGL ES 2.0 rectangle moving across the screen
// egl_bar.c - self-contained example of a vertical bar moving across the screen.
// compile with gcc -Wall -O0 -g -o egl_bar egl_bar.c log.c -lX11 -lEGL -lGLESv2 -lm
#include <stdio.h>
#include <math.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <GLES2/gl2.h>
#include <EGL/egl.h>
#include "log.h"
static void log_egl_details(EGLDisplay egl_display, EGLConfig egl_conf) {
log_info("EGL Client APIs: %s", eglQueryString(egl_display, EGL_CLIENT_APIS));
log_info("EGL Vendor: %s", eglQueryString(egl_display, EGL_VENDOR));
log_info("EGL Version: %s", eglQueryString(egl_display, EGL_VERSION));
log_info("EGL Extensions: %s", eglQueryString(egl_display, EGL_EXTENSIONS));
int i = -1;
eglGetConfigAttrib(egl_display, egl_conf, EGL_CONFIG_ID, &i);
log_debug("EGL_CONFIG_ID = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_RED_SIZE, &i);
log_debug("EGL_RED_SIZE = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_GREEN_SIZE, &i);
log_debug("EGL_GREEN_SIZE = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_BLUE_SIZE, &i);
log_debug("EGL_BLUE_SIZE = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_ALPHA_SIZE, &i);
log_debug("EGL_ALPHA_SIZE = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_DEPTH_SIZE, &i);
log_debug("EGL_DEPTH_SIZE = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_LEVEL, &i);
log_debug("EGL_LEVEL = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_NATIVE_RENDERABLE, &i);
log_debug("EGL_NATIVE_RENDERABLE = %s", i ? "EGL_TRUE" : "EGL_FALSE");
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_NATIVE_VISUAL_TYPE, &i);
log_debug("EGL_NATIVE_VISUAL_TYPE = %d", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_RENDERABLE_TYPE, &i);
log_debug("EGL_RENDERABLE_TYPE = 0x%04x", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_SURFACE_TYPE, &i);
log_debug("EGL_SURFACE_TYPE = 0x%04x", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_TYPE, &i);
if (i == EGL_TRANSPARENT_RGB) {
log_debug("EGL_TRANSPARENT_TYPE = EGL_TRANSPARENT_RGB");
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_RED_VALUE, &i);
log_debug("EGL_TRANSPARENT_RED = 0x%02x", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_GREEN_VALUE, &i);
log_debug("EGL_TRANSPARENT_GREEN = 0x%02x", i);
i = 0;
eglGetConfigAttrib(egl_display, egl_conf, EGL_TRANSPARENT_BLUE_VALUE, &i);
log_debug("EGL_TRANSPARENT_BLUE = 0x%02x", i);
} else {
log_debug("EGL_TRANSPARENT_TYPE = EGL_NONE");
}
}
void print_shader_info_log(GLuint shader) {
GLint length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
if (length) {
char* buffer = malloc(length);
glGetShaderInfoLog(shader, length, NULL, buffer);
log_info("%s", buffer);
free(buffer);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (success != GL_TRUE) {
log_fatal("aborting because shader compilation failed.");
exit(1);
}
}
}
GLuint load_shader(const char *shaderSource, GLenum type) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &shaderSource, NULL);
glCompileShader(shader);
print_shader_info_log(shader);
return shader;
}
static GLuint getUniformLocationOrDie(GLuint shaderProgram, const char *name) {
GLuint loc = glGetUniformLocation(shaderProgram, name);
if (loc < 0) {
log_fatal("Couldn't find uniform %s", name);
exit(1);
}
return loc;
}
static GLuint getAttribLocationOrDie(GLuint shaderProgram, const char *name) {
GLuint loc = glGetAttribLocation(shaderProgram, name);
if (loc < 0) {
log_fatal("Couldn't find attribute %s", name);
exit(1);
}
return loc;
}
int main(int argc, char **argv) {
Display *x_display = XOpenDisplay(NULL);
if (x_display == NULL) {
log_fatal("cannot connect to X server");
return 1;
}
Window root = DefaultRootWindow(x_display); // get the root window (usually the whole screen)
int root_x, root_y;
unsigned int root_w, root_h, root_border_width, root_depth;
Window root_again;
XGetGeometry(x_display, root, &root_again, &root_x, &root_y, &root_w, &root_h, &root_border_width, &root_depth);
log_info("Matching X11 root window geometry: +%d,%d %dx%d border %d, %dbpp\n",
root_x, root_y, root_w, root_h, root_border_width, root_depth);
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | ButtonPressMask | KeyPressMask;
Window win = XCreateWindow(x_display, root,
0, 0, root_w, root_h, 0,
CopyFromParent, InputOutput,
CopyFromParent, CWEventMask,
&swa);
XSetWindowAttributes xattr;
xattr.override_redirect = False;
XChangeWindowAttributes(x_display, win, CWOverrideRedirect, &xattr);
/* This fails when there is no window manager running
Atom atom;
atom = XInternAtom(x_display, "_NET_WM_STATE_FULLSCREEN", True);
XChangeProperty(
x_display, win,
XInternAtom(x_display, "_NET_WM_STATE", True),
XA_ATOM, 32, PropModeReplace,
(unsigned char*) &atom, 1);
*/
int one = 1;
XChangeProperty(
x_display, win,
XInternAtom ( x_display, "_HILDON_NON_COMPOSITED_WINDOW", False ),
XA_INTEGER, 32, PropModeReplace,
(unsigned char*) &one, 1);
XWMHints hints;
hints.input = True;
hints.flags = InputHint;
XSetWMHints(x_display, win, &hints);
XMapWindow(x_display, win); // makes the window visible on the screen
XStoreName(x_display, win, argv[0]);
//// get identifiers for the provided atom name strings
Atom wm_state = XInternAtom(x_display, "_NET_WM_STATE", False);
Atom fullscreen = XInternAtom(x_display, "_NET_WM_STATE_FULLSCREEN", False);
XEvent xev;
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
xev.xclient.window = win;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1;
xev.xclient.data.l[1] = fullscreen;
XSendEvent( // set up event mask (which events we want to receive)
x_display,
DefaultRootWindow(x_display),
False,
SubstructureNotifyMask,
&xev);
/////// the egl part //////////////////////////////////////////////////////////////////
// egl provides an interface to connect the graphics related functionality of openGL ES
// with the windowing interface and functionality of the native operation system (X11
// in our case.)
EGLDisplay egl_display = eglGetDisplay((EGLNativeDisplayType) x_display);
if (egl_display == EGL_NO_DISPLAY) {
log_fatal("Got no EGL display.");
return 1;
}
EGLint egl_version_major, egl_version_minor;
if (!eglInitialize(egl_display, &egl_version_major, &egl_version_minor)) {
log_fatal("Unable to initialize EGL");
return 1;
}
log_info("Initialized EGL version %d.%d", egl_version_major, egl_version_minor);
EGLint egl_config_constraints[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 0,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_NONE
};
EGLConfig egl_conf;
EGLint num_config;
if (!eglChooseConfig(egl_display, egl_config_constraints, &egl_conf, 1, &num_config)) {
log_fatal("Failed to choose config (eglError: %s)", eglGetError());
return 1;
}
if (num_config != 1) {
log_fatal("Didn't get exactly one config, but %d", num_config);
return 1;
}
EGLSurface egl_surface = eglCreateWindowSurface(egl_display, egl_conf, win, NULL);
if (egl_surface == EGL_NO_SURFACE) {
log_fatal("Unable to create EGL surface (eglError: %s)", eglGetError());
return 1;
}
//// egl-contexts collect all state descriptions needed required for operation
EGLint ctxattr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLContext egl_context = eglCreateContext(egl_display, egl_conf, EGL_NO_CONTEXT, ctxattr);
if (egl_context == EGL_NO_CONTEXT) {
log_fatal("Unable to create EGL context (eglError: %s)", eglGetError());
return 1;
}
//// associate the egl-context with the egl-surface
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
log_egl_details(egl_display, egl_conf);
EGLint queriedRenderBuffer;
if (eglQueryContext(egl_display, egl_context, EGL_RENDER_BUFFER, &queriedRenderBuffer)) {
switch (queriedRenderBuffer) {
case EGL_SINGLE_BUFFER: log_info("Render Buffer: EGL_SINGLE_BUFFER"); break;
case EGL_BACK_BUFFER: log_info("Render Buffer: EGL_BACK_BUFFER"); break;
case EGL_NONE: log_info("Render Buffer: EGL_NONE"); break;
default: log_info("Render Buffer: unknown value %d", queriedRenderBuffer); break;
}
} else {
log_error("Failed to query EGL_RENDER_BUFFER: %d", eglGetError());
}
if (!eglSwapInterval(egl_display, 1)) {
log_warn("eglSwapInterval failed: %d", eglGetError());
} else {
log_info("Set swap interval");
}
/////// the openGL part ///////////////////////////////////////////////////////////////
GLuint vertexShader = load_shader
(" precision mediump float;"
"\n "
"\n // Model-view-projection transformation matrix"
"\n uniform mat4 mvp_matrix;"
"\n "
"\n // Incoming"
"\n attribute vec2 a_position; // vertex position in 2d Model Space"
"\n attribute vec3 a_color; // color of the rectangular segment this vertex is part of"
"\n "
"\n // Outgoing interpolated values for the fragment shader"
"\n varying vec3 color;"
"\n "
"\n void main() {"
"\n gl_Position = mvp_matrix * vec4(a_position, 0, 1);"
"\n color = a_color;"
"\n }", GL_VERTEX_SHADER);
GLuint fragmentShader = load_shader
(" precision mediump float;"
"\n "
"\n varying vec3 color;"
"\n "
"\n void main() {"
"\n gl_FragColor = vec4(color, 1.0);"
"\n }", GL_FRAGMENT_SHADER);
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram);
// OpenGL Rendering Parameters
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
XWindowAttributes gwa;
XGetWindowAttributes(x_display, win, &gwa);
glViewport(0, 0, gwa.width, gwa.height);
glClearColor(0.0, 0.0, 0.0, 1.0);
float attribute_array[] =
// x y r g b
{ 0.0, -1, 1, 0, 0,
0.0, 1, 0, 1, 1,
0.2, -1, 0, 0, 1,
0.2, 1, 0, 1, 0
};
size_t attribute_stride = 5 * sizeof(float);
// figure out a display matrix that doesn't stretch the image
float display_aspect = ((float) gwa.width) / ((float) gwa.height);
float scale = 1.5;
float xscale = scale / display_aspect;
float yscale = scale;
float display_transform_cols[4*4] =
{ // WARNING: THIS IS A COLUMN MATRIX SO BE CAREFUL IF YOU GO OFF THE DIAGONAL!
xscale, 0, 0, 0,
0, yscale, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
GLuint mvp_matrix_loc = getUniformLocationOrDie(shaderProgram, "mvp_matrix");
glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, display_transform_cols);
GLuint position_loc = getAttribLocationOrDie(shaderProgram, "a_position");
GLuint color_loc = getAttribLocationOrDie(shaderProgram, "a_color");
glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, attribute_stride, (void *) &attribute_array);
glVertexAttribPointer(color_loc, 3, GL_FLOAT, GL_FALSE, attribute_stride, ((void *) &attribute_array) + 2 * sizeof(float));
glEnableVertexAttribArray(position_loc);
glEnableVertexAttribArray(color_loc);
float speed = 0.01;
float bar_width = 0.2;
// render loop
for (;;) {
display_transform_cols[12] += speed;
if (display_transform_cols[12] < -1 || display_transform_cols[12] > (1.0 - bar_width)) {
speed *= -1;
}
glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, display_transform_cols);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
eglSwapBuffers(egl_display, egl_surface);
}
}
/*
* Copyright (c) 2017 rxi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include "log.h"
static struct {
void *udata;
log_LockFn lock;
FILE *fp;
int level;
int quiet;
} L;
static const char *level_names[] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};
#ifdef LOG_USE_COLOR
static const char *level_colors[] = {
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
};
#endif
static void lock(void) {
if (L.lock) {
L.lock(L.udata, 1);
}
}
static void unlock(void) {
if (L.lock) {
L.lock(L.udata, 0);
}
}
void log_set_udata(void *udata) {
L.udata = udata;
}
void log_set_lock(log_LockFn fn) {
L.lock = fn;
}
void log_set_fp(FILE *fp) {
L.fp = fp;
}
void log_set_level(int level) {
L.level = level;
}
void log_set_quiet(int enable) {
L.quiet = enable ? 1 : 0;
}
void log_log(int level, const char *file, int line, const char *fmt, ...) {
if (level < L.level) {
return;
}
/* Acquire lock */
lock();
/* Get current time */
time_t t = time(NULL);
struct tm *lt = localtime(&t);
/* Log to stderr */
if (!L.quiet) {
va_list args;
char buf[16];
buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0';
#ifdef LOG_USE_COLOR
fprintf(
stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ",
buf, level_colors[level], level_names[level], file, line);
#else
fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line);
#endif
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
fflush(stderr);
}
/* Log to file */
if (L.fp) {
va_list args;
char buf[32];
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0';
fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line);
va_start(args, fmt);
vfprintf(L.fp, fmt, args);
va_end(args);
fprintf(L.fp, "\n");
fflush(L.fp);
}
/* Release lock */
unlock();
}
/**
* Copyright (c) 2017 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See `log.c` for details.
*/
#ifndef LOG_H
#define LOG_H
#include <stdio.h>
#include <stdarg.h>
#define LOG_VERSION "0.1.0"
typedef void (*log_LockFn)(void *udata, int lock);
enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };
#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
void log_set_udata(void *udata);
void log_set_lock(log_LockFn fn);
void log_set_fp(FILE *fp);
void log_set_level(int level);
void log_set_quiet(int enable);
void log_log(int level, const char *file, int line, const char *fmt, ...);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment