From 197708e18678852b1940fba6311b4362b2374d9f Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Fri, 20 Feb 2026 13:05:06 -0800 Subject: [PATCH] [wayland_libdecor_egl] --- wayland_libdecor_egl.c | 576 +++++++++++++++++++++++++++++++++++++++++ wayland_xdg_egl.c | 5 +- x11_egl.c | 148 +++++------ 3 files changed, 654 insertions(+), 75 deletions(-) create mode 100755 wayland_libdecor_egl.c diff --git a/wayland_libdecor_egl.c b/wayland_libdecor_egl.c new file mode 100755 index 0000000..9b7c22d --- /dev/null +++ b/wayland_libdecor_egl.c @@ -0,0 +1,576 @@ +#if 0 +mkdir -p build +clang -o build/demo -g wayland_libdecor_egl.c -I/usr/include/libdecor-0 -ldecor-0 -lwayland-client -lwayland-egl -lEGL +exit 0 +#endif + +/* +** Reading From: +** (1) Wayland Docs https://wayland.freedesktop.org/docs/html/ +** (2) XDG shell Docs https://wayland.app/protocols/xdg-shell#xdg_wm_base +** (egl) EGL spec https://registry.khronos.org/EGL/sdk/docs/man/ +** +** (nodocs-wl_egl) I cannot find any documentation for wl_egl_ except for + ** headers and example code. +** (nodocs-libdecor) not officially documented, deductions from reading +** headers and example code. +*/ + +/* [1] IMPORTANT NOTE +** +** (1) /ch04.html#sect-Protocol-Code-Generation +** " The interfaces, requests and events are defined in +** protocol/wayland.xml. This xml is used to generate the function +** prototypes that can be used by clients and compositors. " +** +** (1) /ch04.html#sect-Protocol-Basic-Principles +** " The Wayland protocol is an asynchronous object oriented protocol. +** All requests are method invocations on some object. +** ... +** The protocol is message-based. A message sent by a client to +** the server is called request. A message from the server to a +** client is called event. " +** +**~ +**~ I have yet to see any place where it is *defined* how the xml +**~ is transformed into function prototypes. So I'm putting notes of +**~ my own inferences here. +**~ +**~ From the client's perspective: +**~ +**~ A request `R(...)` on an object interface `O` becomes the +**~ function `O_R(O *obj, ...)`. +**~ ! Sometimes the arguments for `R(...)` include an argument that +**~ doesn't appear to have a documented type (at least in (1)). +**~ We can also refer to the xml and see such arguments have +**~ the type "new_id". These seem to actually be a return value +**~ that does not appear in the generated signature as an id, +**~ but instead is translated into a typed return pointer. +**~ If the documentation does explicitly write the "new_id" type +**~ that also gets rewritten to a typed return. +**~ +**~ An event `E(...)` on an object interface `O` will becomes a +**~ callback with the signature `(*)(void *data, O *obj, ...)`. +**~ +**~ The collection of event callbacks on an object interface become +**~ `struct wl_O_listener` using the names `E` of the events as the +**~ field names. +**~ +**~ A function for adding the listener to the object is also generated +**~ `wl_O_add_listener(O *obj, struct wl_O_listener *listener, void *data)` +**~ +**~ Each object interface `O` becomes a `wl_O_interface` which (I think) +**~ is a global variable vtable representation of the interface. +**~ +*/ + +#include +#include +#include +/*~ NOTE: wayland-egl.h *before* EGL/ */ +#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 + +typedef struct Ctx{ + /* globals */ + struct wl_display *wl_display; + struct wl_registry *wl_registry; + struct wl_compositor *wl_compositor; + struct xdg_wm_base *xdg_wm_base; + struct libdecor *libdecor; + + /* window */ + struct wl_surface *wl_surface; + struct libdecor_frame *libdecor_frame; + struct wl_egl_window *wl_egl_window; + int configured; + int w; + int h; + int close_signal; + //struct xdg_surface *xdg_surface; + //struct xdg_toplevel *xdg_toplevel; + //struct wl_region *wl_region; + EGLDisplay egl_display; + EGLContext egl_context; + EGLSurface egl_surface; +} Ctx; + +static Ctx ctx = {0}; + + +#if 0 +/* (2) xdg_wm_base::ping */ +static void +wlevent__xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, + uint32_t serial){ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +const struct xdg_wm_base_listener xdg_wm_base_listener = { + wlevent__xdg_wm_base_ping, +}; + + +/* (2) xdg_surface::configure +** " marks the end of a configure sequence ... +** Clients should arrange their surface for the new states, and then +** send an ack_configure request with the serial sent in this +** configure event at some point before committing the new surface. " +*/ +static void +wlevent__xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, + uint32_t serial){ + xdg_surface_ack_configure(xdg_surface, serial); + wl_surface_commit(ctx.wl_surface); +} + +const struct xdg_surface_listener xdg_surface_listener = { + wlevent__xdg_surface_configure, +}; + + +/* (2) xdg_toplevel::configure +** " This configure event asks the client to resize its toplevel surface +** or to change its state. The configured state should not be applied +** immediately. See xdg_surface.configure for details. " +** @see:(xdg_surface::configure) +*/ +static void +wlevent__xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height, + struct wl_array *states){ + if (width > 0 && height > 0){ + /* (nodocs-wl_egl) */ + wl_egl_window_resize(ctx.wl_egl_window, width, height, 0, 0); + } +} + +/* (2) xdg_toplevel::close +** " sent by the compositor when the user wants the surface to be closed. " +*/ +static void +wlevent__xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel){ + ctx.close_signal = 1; +} + +/* (2) xdg_toplevel::configure_bounds +** " sent [...] to communicate the bounds a window geometry size is +** recommended to constrain to " +**~ NOTE: basically "tells you the monitor size". +** @see:xdg_surface.configure +*/ +static void +wlevent__xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height){ +} + +/* (2) xdg_toplevel::wm_capabilities +** " advertises the capabilities supported by the compositor " +**~ NOTE: basically "tells you the monitor size". +** @see:xdg_surface.configure +*/ +static void +wlevent__xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities){ +} + +const struct xdg_toplevel_listener xdg_toplevel_listener = { + wlevent__xdg_toplevel_configure, + wlevent__xdg_toplevel_close, + wlevent__xdg_toplevel_configure_bounds, + wlevent__xdg_toplevel_wm_capabilities, +}; +#endif + + +/* (1) Appendix A: wl_registry::global +** " The event notifies the client that a global object with the given +** name is now available " +*/ +static void +wlevent__wl_registry_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, + uint32_t version){ + /* (1) Appendix A: wl_registry::bind + ** " Binds a new, client-created object to the server " + */ + + if (strcmp(interface, "wl_compositor") == 0){ + ctx.wl_compositor = (struct wl_compositor*) + wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } +#if 0 + else if (strcmp(interface, "xdg_wm_base") == 0){ + ctx.xdg_wm_base = (struct xdg_wm_base*) + wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + + /* [1] */ + xdg_wm_base_add_listener(ctx.xdg_wm_base, &xdg_wm_base_listener, 0); + } +#endif +} + +/* (1) Appendix A: wl_registry::global_remove */ +static void +wlevent__wl_registry_global_remove(void *data, struct wl_registry *registry, + uint32_t name){} + +const struct wl_registry_listener wl_registry_listener = { + wlevent__wl_registry_global, + wlevent__wl_registry_global_remove, +}; + + +/* (nodocs) */ +static void +libdecorevent__error(struct libdecor *libdecor, enum libdecor_error error, + const char *msg){} + +struct libdecor_interface libdecor_interface = { + libdecorevent__error +}; + + +/* (nodocs) */ +static void +libdecorevent__frame_configure(struct libdecor_frame *frame, + struct libdecor_configuration *config, + void *udata){ + int w = ctx.w; + int h = ctx.h; + /* (nodocs) */ + if (libdecor_configuration_get_content_size(config, frame, &w, &h)){ + /* (nodocs) */ + struct libdecor_state *state = libdecor_state_new(w, h); + /* (nodocs) */ + libdecor_frame_commit(frame, state, config); + /* (nodocs) */ + libdecor_state_free(state); + } + ctx.configured = 1; + ctx.w = w; + ctx.h = h; +} + +/* (nodocs) */ +static void +libdecorevent__frame_close(struct libdecor_frame *frame, void *udata){ + ctx.close_signal = 1; +} + +/* (nodocs) */ +static void +libdecorevent__frame_commit(struct libdecor_frame *frame, void *udata){ + wl_surface_commit(ctx.wl_surface); +} + +struct libdecor_frame_interface libdecor_frame_interface = { + libdecorevent__frame_configure, + libdecorevent__frame_close, + libdecorevent__frame_commit, +}; + + +int main(){ + /*~ NOTE: + **~ initialize Wayland, Libdecor, & EGL + */ + + /* (1) Appendix B: wl_display_connect + ** " Connect to a Wayland display. " + */ + ctx.wl_display = wl_display_connect(0); + if (ctx.wl_display == 0){ + printf("wl_display_connect failed\n"); + } + + /* (1) Appendix A: wl_display::get_registry + ** " creates a registry object that allows the client to list + ** and bind the global objects available from the compositor " + */ + if (ctx.wl_display != 0){ + ctx.wl_registry = wl_display_get_registry(ctx.wl_display); + if (ctx.wl_registry == 0){ + printf("wl_display_get_registry failed\n"); + } + } + + if (ctx.wl_registry != 0){ + /* [1] */ + wl_registry_add_listener(ctx.wl_registry, &wl_registry_listener, 0); + + /* (1) Appendix B: wl_display::dispatch + ** " Dispatch events on the default event queue. If the default + ** event queue is empty, this function blocks until there are + ** events to be read from the display fd. " + */ + wl_display_dispatch(ctx.wl_display); + + /* (1) Appendix B: wl_display_roundtrip + ** " Block until all pending request are processed by the server " + */ + wl_display_roundtrip(ctx.wl_display); + + if (ctx.wl_compositor == 0){ + printf("failed to get wl_compositor\n"); + } + } + + /* (nodocs) */ + if (ctx.wl_display != 0 && ctx.wl_compositor != 0){ + ctx.libdecor = libdecor_new(ctx.wl_display, &libdecor_interface); + } + + int opengl_load_success = 0; + if (ctx.libdecor != 0){ + /* (egl) eglGetDisplay + ** " obtains the EGL display connection for the native display " + */ + ctx.egl_display = eglGetDisplay(ctx.wl_display); + if (ctx.egl_display == 0){ + printf("eglGetDisplay failed\n"); + } + + /* (egl) eglInitialize + ** " initializes* the EGL display connection obtained with eglGetDisplay " + */ + int egl_init_success = 0; + EGLint major = 0, minor = 0; + if (ctx.egl_display != 0){ + egl_init_success = eglInitialize(ctx.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; + } + } + + /* (egl) 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); + } + + /* (egl) eglCreateContext + ** " creates an EGL rendering context for the current rendering API + ** (as set with eglBindAPI) and returns a handle to the context " + */ + 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, + }; + ctx.egl_context = eglCreateContext(ctx.egl_display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attr); + if (ctx.egl_context == EGL_NO_CONTEXT){ + printf("eglCreateContext failed\n"); + } + } + + /* (egl) eglMakeCurrent + ** " binds context to the current rendering thread " + */ + EGLBoolean make_current_success = 0; + if (ctx.egl_context != 0){ + make_current_success = eglMakeCurrent(ctx.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx.egl_context); + if (!make_current_success){ + printf("eglMakeCurrent failed\n"); + } + } + + /* (egl) eglGetProcAddress + ** " returns the address of the client API or EGL function " + */ + if (make_current_success){ + opengl_load_success = 1; +#define X(N,R,P) N = (R(*)P)(eglGetProcAddress(#N)); if (N == 0) opengl_load_success = 0; + GL_FUNCS_XLIST(X) +#undef X + if (!opengl_load_success){ + printf("GL procedure loading failed\n"); + } + } + } + + /*~ NOTE: + **~ Create a window + */ + + if (opengl_load_success){ + ctx.w = 640; + ctx.h = 480; + + /* (1) Appendix A: wl_compositor::create_surface + ** " create new surface " + */ + ctx.wl_surface = wl_compositor_create_surface(ctx.wl_compositor); + if (ctx.wl_surface == 0){ + printf("wl_compositor_create_surface failed\n"); + } + } + + if (ctx.wl_surface != 0){ + /* (nodocs-libdecor) */ + ctx.libdecor_frame = libdecor_decorate(ctx.libdecor, ctx.wl_surface, + &libdecor_frame_interface, 0); + } + + if (ctx.libdecor_frame != 0){ + /* (nodocs-libdecor) */ + libdecor_frame_set_title(ctx.libdecor_frame, "Example Window"); + /* (nodocs-libdecor) */ + libdecor_frame_map(ctx.libdecor_frame); + + /* (nodocs-wl_egl) */ + ctx.wl_egl_window = wl_egl_window_create(ctx.wl_surface, ctx.w, ctx.h); + if (ctx.wl_egl_window == EGL_NO_SURFACE){ + printf("wl_egl_window_create failed\n"); + } + } + + if (ctx.wl_egl_window != EGL_NO_SURFACE){ + /* (nodocs-wl_egl) 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(ctx.egl_display, attributes, configs, config_cap, &config_count)){ + printf("eglChooseConfig failed\n"); + config_count = 0; + } + } + + /* (egl) eglCreateWindowSurface + ** " creates an on-screen EGL window surface and returns a handle to it " + */ + { + EGLint attributes[] = { + EGL_RENDER_BUFFER, EGL_BACK_BUFFER, + EGL_NONE, + }; + for (EGLint i = 0; i < config_count; i += 1){ + ctx.egl_surface = eglCreateWindowSurface(ctx.egl_display, configs[i], ctx.wl_egl_window, attributes); + if (ctx.egl_surface != EGL_NO_SURFACE){ + break; + } + } + if (ctx.egl_surface == EGL_NO_SURFACE){ + printf("eglCreateWindowSurface failed\n"); + } + } + } + + /* (egl) eglMakeCurrent */ + EGLBoolean make_current_success2 = 0; + if (ctx.egl_surface != EGL_NO_SURFACE){ + make_current_success2 = eglMakeCurrent(ctx.egl_display, ctx.egl_surface, ctx.egl_surface, ctx.egl_context); + } + + /* (egl) eglSwapInterval + ** " specifies the minimum number of video frame periods per buffer swap + ** for the window associated with the current context " + */ + EGLBoolean swap_interval_success = 0; + if (make_current_success2){ + swap_interval_success = eglSwapInterval(ctx.egl_display, 1); + if (!swap_interval_success){ + printf("eglSwapInterval failed\n"); + } + } + + /* (egl) eglSwapBuffers + **~ NOTE: swap before loop for libdecor_dispatch (nodocs-libdecor) + */ + EGLBoolean initial_swap_success = 0; + if (swap_interval_success){ + initial_swap_success = eglSwapBuffers(ctx.egl_display, ctx.egl_surface); + if (!initial_swap_success){ + printf("eglSwapBuffers failed\n"); + } + } + + /*~ NOTE: Main loop */ + int exit_loop = 0; + if (!initial_swap_success){ + exit_loop = 1; + } + for (;!exit_loop;){ + /* (nodocs-libdecor) */ + libdecor_dispatch(ctx.libdecor, -1); + + if (ctx.close_signal){ + exit_loop = 1; + } + + /* (nodocs-wl_egl) */ + wl_egl_window_resize(ctx.wl_egl_window, ctx.w, ctx.h, 0, 0); + + /*~ NOTE: render */ + { + glDrawBuffer(GL_BACK); + glViewport(0, 0, 640, 480); + glClearColor(0.40f, 0.90f, 0.15f, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + } + + /* (egl) eglSwapBuffers + ** " back-buffered window surface, then the color buffer is copied + ** (posted) to the native window associated with that surface " + */ + EGLBoolean swap_success = eglSwapBuffers(ctx.egl_display, ctx.egl_surface); + if (!swap_success){ + printf("eglSwapBuffers failed\n"); + } + } + + /* (1) #Client-classwl__display_1a9150a7e3213a58b469a6966e60a9f108 + ** " Close the connection to display " + */ + if (ctx.wl_display != 0){ + wl_display_disconnect(ctx.wl_display); + } + + return(0); +} + diff --git a/wayland_xdg_egl.c b/wayland_xdg_egl.c index 10f33d8..8f40c05 100755 --- a/wayland_xdg_egl.c +++ b/wayland_xdg_egl.c @@ -262,8 +262,9 @@ int main(){ wl_registry_add_listener(ctx.wl_registry, &wl_registry_listener, 0); /* (1) Appendix B: wl_display::dispatch - ** " Dispatch events on the default event queue. - ** ... this function blocks until there are events to be read ... " + ** " Dispatch events on the default event queue. If the default + ** event queue is empty, this function blocks until there are + ** events to be read from the display fd. " */ wl_display_dispatch(ctx.wl_display); diff --git a/x11_egl.c b/x11_egl.c index a4663ab..0e11dc3 100755 --- a/x11_egl.c +++ b/x11_egl.c @@ -30,7 +30,7 @@ GL_FUNCS_XLIST(X) int main(int argc, char **argv){ /*~ NOTE: - **~ initialize X11 and EGL API's + **~ initialize X11 and EGL */ /* (1) /display/opening.html @@ -90,10 +90,6 @@ int main(int argc, char **argv){ 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 " @@ -218,7 +214,7 @@ int main(int argc, char **argv){ } } - if (surface != 0){ + if (surface != EGL_NO_SURFACE){ /* (1) /ICC/client-to-window-manager/XStoreName.html ** " assigns the name passed to window_name to the specified window " */ @@ -229,80 +225,86 @@ int main(int argc, char **argv){ **~ 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); + } + + /* (egl) eglSwapInterval + ** " specifies the minimum number of video frame periods per buffer swap + ** for the window associated with the current context " + */ + EGLBoolean swap_interval_success = 0; + if (make_current_success2){ + swap_interval_success = eglSwapInterval(ctx.egl_display, 1); + if (!swap_interval_success){ + printf("eglSwapInterval failed\n"); + } + } + + /*~ NOTE: Main loop */ + int exit_loop = 0; + if (!swap_interval_success){ + exit_loop = 1; + } + for (;!exit_loop;){ - /* (2) eglMakeCurrent */ - EGLBoolean make_current_success2 = 0; - if (surface != EGL_NO_SURFACE){ - make_current_success2 = eglMakeCurrent(egl_display, surface, surface, context); + /* (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 it's + ** easier, and possibly more reliable to just use it to check if + ** there is at lesat 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; + } } - /* (2) eglSwapInterval - ** " Specifies the minimum number of video frames that are displayed before - ** a buffer swap will occur " + /* (1) /window-information/XGetWindowAttributes.html + ** " returns the current attributes for the specified window " */ - EGLBoolean swap_interval_success = 0; - if (make_current_success2){ - swap_interval_success = eglSwapInterval(egl_display, 1); + XWindowAttributes window_attr = {0}; + if (!XGetWindowAttributes(display, window, &window_attr)){ + printf("XGetWindowAttributes failed\n"); } - /*~ 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 it's - ** easier, and possibly more reliable to just use it to check if - ** there is at lesat one input. + /*~ NOTE: render */ + 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 " */ - 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"); - } - - /*~ NOTE: render */ - 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"); - } + EGLBoolean swap_success = eglSwapBuffers(egl_display, surface); + if (!swap_success){ + printf("eglSwapBuffers failed\n"); } }