#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); }