From 21888aa33ea29f2472f49cd1c1316f905c707780 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Tue, 17 Feb 2026 18:27:15 -0800 Subject: [PATCH] [x11_egl] setup --- .gitignore | 1 + x11_egl.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 .gitignore create mode 100755 x11_egl.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07ed706 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/* \ No newline at end of file diff --git a/x11_egl.c b/x11_egl.c new file mode 100755 index 0000000..9f4dc30 --- /dev/null +++ b/x11_egl.c @@ -0,0 +1,325 @@ +#if 0 +mkdir -p build +clang -o build/demo -g x11_egl.c -lX11 -lEGL +exit 0 +#endif + +/* +** Reading From: +** (1) xlib manual https://tronche.com/gui/x/xlib/ +** (2) EGL spec https://registry.khronos.org/EGL/sdk/docs/man/ +*/ + +#include +#include +#include +#include + +#include + +// X(N:name,R:return,P:params) +#define GL_FUNCS_XLIST(X)\ +X(glDrawBuffer, void, (GLenum buf)) \ +X(glViewport, void, (GLint x, GLint y, GLsizei w, GLsizei h)) \ +X(glClear, void, (GLbitfield mask)) \ +X(glClearColor, void, (GLfloat r, GLfloat g, GLfloat b, GLfloat a)) + + +#define X(N,R,P) R (*N)P = 0; +GL_FUNCS_XLIST(X) +#undef X + +int main(int argc, char **argv){ + /*~ NOTE: + **~ initialize X11 and EGL API's + */ + + /* (1) /display/opening.html + ** " The XOpenDisplay() function returns a Display structure that serves + ** as the connection to the X server and that contains all the + ** information about that X server. " + */ + Display *display = XOpenDisplay(0); + if (display == 0){ + printf("XOpenDisplay failed\n"); + } + + /* (2) eglGetPlatformDisplay + ** " eglGetPlatformDisplay obtains an EGL display connection " + */ + EGLDisplay *egl_display = 0; + if (display != 0){ + egl_display = eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, display, 0); + if (egl_display == 0){ + printf("eglGetPlatformDisplay failed\n"); + } + } + + /* (2) eglInitialize + ** " initializes* the EGL display connection obtained with eglGetDisplay " + */ + int egl_init_success = 0; + EGLint major = 0, minor = 0; + if (egl_display != 0){ + egl_init_success = eglInitialize(egl_display, &major, &minor); + if (!egl_init_success){ + printf("eglInitialize failed\n"); + } + } + + // print version + if (egl_init_success){ + printf("EGL version %d.%d\n", major, minor); + if (major < 1 || (major == 1 && minor < 5)){ + printf("version 1.5 or higher\n"); + egl_init_success = 0; + } + } + + /* (2) eglBindAPI + ** " defines the current rendering API for EGL in the thread it is + ** called from " + */ + EGLBoolean bind_api_success = 0; + if (egl_init_success){ + bind_api_success = eglBindAPI(EGL_OPENGL_API); + } + + /*~ NOTE: + **~ Create OpenGL context & link OpenGL procedures + */ + + /* (2) eglCreateContext + ** " creates an EGL rendering context for the current rendering API + ** (as set with eglBindAPI) and returns a handle to the context " + */ + EGLContext* context = 0; + if (bind_api_success){ + EGLint attr[] = { + EGL_CONTEXT_MAJOR_VERSION, 3, + EGL_CONTEXT_MINOR_VERSION, 3, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_NONE, + }; + context = eglCreateContext(egl_display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attr); + if (context == EGL_NO_CONTEXT){ + printf("eglCreateContext failed\n"); + } + } + + /* (2) eglMakeCurrent + ** " binds context to the current rendering thread " + */ + EGLBoolean make_current_success = 0; + if (context != 0){ + make_current_success = eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); + if (!make_current_success){ + printf("eglMakeCurrent failed\n"); + } + } + + /* (2) eglGetProcAddress + ** " returns the address of the client API or EGL function " + */ + int load_success = 0; + if (make_current_success){ + load_success = 1; +#define X(N,R,P) N = (R(*)P)(eglGetProcAddress(#N)); if (N == 0) load_success = 0; + GL_FUNCS_XLIST(X) +#undef X + if (!load_success){ + printf("GL procedure loading failed\n"); + } + } + + /* (1) /window-information/XInternAtom.html + ** " returns the atom identifier associated with the specified + ** atom_name string " + */ + Atom atom__WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", False); + + /*~ NOTE: + **~ Create a window and a surface for it + */ + + /* (1) /window/XCreateWindow.html + ** " The XCreateWindow function creates an unmapped subwindow for a + ** specified parent window " + */ + Window window = 0; + if (load_success){ + Window parent = DefaultRootWindow(display); + int x = 0; + int y = 0; + int w = 640; + int h = 480; + int border_width = 0; + int depth = CopyFromParent; + unsigned int xclass = InputOutput; + Visual *visual = CopyFromParent; + unsigned long valuemask = CWEventMask; + + XSetWindowAttributes attributes = {0}; + attributes.event_mask = StructureNotifyMask; + + window = XCreateWindow(display, parent, x, y, w, h, border_width, + depth, xclass, visual, valuemask, + &attributes); + + if (window == 0){ + printf("XCreateWindow failed\n"); + } + } + + EGLSurface *surface = EGL_NO_SURFACE; + { + /* (2) eglChooseConfig + ** " returns in configs a list of all EGL frame buffer configurations + ** that match the attributes specified in attrib_list " + */ + EGLConfig configs[64]; + EGLint config_cap = sizeof(configs)/sizeof(*configs); + EGLint config_count = 0; + { + EGLint attributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_CONFORMANT, EGL_OPENGL_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_NONE, + }; + if (!eglChooseConfig(egl_display, attributes, configs, config_cap, &config_count)){ + printf("eglChooseConfig failed\n"); + config_count = 0; + } + } + + /* (2) eglCreatePlatformWindowSurface + ** " creates an on-screen EGL window surface and returns a handle to it " + */ + { + EGLAttrib attributes[] = { + EGL_RENDER_BUFFER, EGL_BACK_BUFFER, + EGL_NONE, + }; + for (EGLint i = 0; i < config_count; i += 1){ + surface = eglCreatePlatformWindowSurface(egl_display, configs[i], &window, attributes); + if (surface != EGL_NO_SURFACE){ + break; + } + } + if (surface == EGL_NO_SURFACE){ + printf("eglCreatePlatformWindowSurface failed\n"); + } + } + } + + /* (1) /ICC/client-to-window-manager/XStoreName.html + ** " assigns the name passed to window_name to the specified window " + */ + XStoreName(display, window, "Example Window"); + + /* (1) /window/XMapWindow.html + ** " maps the window and all of its subwindows that have had map requests " + **~ NOTE: This makes the window visible on the screen. + */ + XMapWindow(display, window); + + /* (2) eglMakeCurrent */ + EGLBoolean make_current_success2 = 0; + if (surface != EGL_NO_SURFACE){ + make_current_success2 = eglMakeCurrent(egl_display, surface, surface, context); + } + + /* (2) eglSwapInterval + ** " Specifies the minimum number of video frames that are displayed before + ** a buffer swap will occur " + */ + EGLBoolean swap_interval_success = 0; + if (make_current_success2){ + swap_interval_success = eglSwapInterval(egl_display, 1); + } + + /*~ NOTE: Main loop */ + int exit_loop = 0; + for (;!exit_loop;){ + + /* (1) /event-handling/XPending.html + ** " returns the number of events that have been received from the X + ** server but have not been removed from the event queue " + **~ NOTE: The docs say this returns the number of events, but in + ** since that number could grow as we process inputs, so it's more + ** usually used as a way to check if there is at least one input. + */ + for (;XPending(display) > 0;){ + /* (1) /event-handling/manipulating-event-queue/XNextEvent.html + ** " copies the first event from the event queue into the specified + ** XEvent structure and then removes it from the queue " + */ + XEvent event; + XNextEvent(display, &event); + + /* (1) /events/structures.html + ** " The XEvent structure is a union of the individual structures + ** declared for each event type. Depending on the type, you should + ** access members of each event by using the XEvent union. " + */ + switch (event.type){ + case ClientMessage: { + Atom atom = event.xclient.data.l[0]; + if (atom == atom__WM_DELETE_WINDOW){ + exit_loop = 1; + } + }break; + } + } + + /* (1) /window-information/XGetWindowAttributes.html + ** " returns the current attributes for the specified window " + */ + XWindowAttributes window_attr = {0}; + if (!XGetWindowAttributes(display, window, &window_attr)){ + printf("XGetWindowAttributes failed\n"); + } + + if (window_attr.width > 0 && window_attr.height > 0){ + glDrawBuffer(GL_BACK); + glViewport(0, 0, window_attr.width, window_attr.height); + glClearColor(0.90f, 0.15f, 0.40f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + } + + /* (2) eglSwapBuffers + ** " back-buffered window surface, then the color buffer is copied + ** (posted) to the native window associated with that surface " + */ + EGLBoolean swap_success = eglSwapBuffers(egl_display, surface); + if (!swap_success){ + printf("eglSwapBuffers failed\n"); + } + } + + /*~ NOTE: Shutdown */ + + /* (1) /window/XDestroyWindow.html + ** " destroys the specified window as well as all of its subwindows " + */ + if (display != 0 && window != 0){ + XDestroyWindow(display, window); + } + + /* (1) /display/closing.html + ** " To close a display or disconnect from the X server, use + ** XCloseDisplay(). " + */ + if (display != 0){ + XCloseDisplay(display); + } + + return(0); +}