libdecor frame commit workaround
parent
71be40cf51
commit
b6797e9931
2
build.sh
2
build.sh
|
|
@ -1,2 +1,2 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
./digesting_libdecor.c
|
./wayland_libdecor_egl__frame_commit_workaround.c
|
||||||
|
|
@ -0,0 +1,497 @@
|
||||||
|
#if 0
|
||||||
|
mkdir -p build
|
||||||
|
clang -o build/demo -g wayland_libdecor_egl__frame_commit_workaround.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/
|
||||||
|
** (egl) EGL spec https://registry.khronos.org/EGL/sdk/docs/man/
|
||||||
|
** (libdecor.h) /usr/include/libdecor-0/libdecor.h
|
||||||
|
**
|
||||||
|
** (nodocs-wl_egl) I cannot find any documentation for wl_egl_ except for
|
||||||
|
** headers and example code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* [1] IMPORTANT NOTE @see([1] in wayland_xdg_egl.c) */
|
||||||
|
|
||||||
|
/* [workaround] IMPORTANT NOTE
|
||||||
|
** The libdecor library has an opaque type called libdecor_configuration
|
||||||
|
** which is passed to event callbacks to announce state changes to
|
||||||
|
** a window, including resizes. This type has to be passed to another
|
||||||
|
** function called libdecor_frame_commit in order to re-render the
|
||||||
|
** decorations with the new size. When multiple resize events come
|
||||||
|
** in during a single frame this commit operation is too slow and the
|
||||||
|
** event queue fills up with more resizes and the main loop hangs, leading
|
||||||
|
** to laggy resizes. The issue is known to the maintainers.
|
||||||
|
** (https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/37)
|
||||||
|
**
|
||||||
|
** We want to only commit a new frame size once per frame in order to
|
||||||
|
** avoid this issue, but since the configured type is opaque, we have
|
||||||
|
** to copy it explicitly out of the library's implementation to do this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <libdecor.h>
|
||||||
|
#include <wayland-egl.h>
|
||||||
|
/*~ NOTE: wayland-egl.h *before* EGL/ */
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
#include <GL/glcorearb.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
/* [workaround] */
|
||||||
|
struct libdecor_configuration {
|
||||||
|
uint32_t serial;
|
||||||
|
|
||||||
|
bool has_window_state;
|
||||||
|
enum libdecor_window_state window_state;
|
||||||
|
|
||||||
|
bool has_size;
|
||||||
|
int window_width;
|
||||||
|
int window_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
EGLDisplay egl_display;
|
||||||
|
EGLContext egl_context;
|
||||||
|
EGLSurface egl_surface;
|
||||||
|
|
||||||
|
/* [workaround] */
|
||||||
|
int has_cached_config;
|
||||||
|
struct libdecor_configuration cached_config;
|
||||||
|
} Ctx;
|
||||||
|
|
||||||
|
static Ctx ctx = {0};
|
||||||
|
|
||||||
|
|
||||||
|
/* (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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (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,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* (libdecor.h) libdecor_interface::error " An error event " */
|
||||||
|
static void
|
||||||
|
libdecorevent__error(struct libdecor *libdecor, enum libdecor_error error,
|
||||||
|
const char *msg){}
|
||||||
|
|
||||||
|
struct libdecor_interface libdecor_interface = {
|
||||||
|
libdecorevent__error
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* (libdecor.h) libdecor_frame_interface::configure
|
||||||
|
** " A new configuration was received. An application should respond to
|
||||||
|
** this by creating a suitable libdecor_state, and apply it using
|
||||||
|
** libdecor_frame_commit. "
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
libdecorevent__frame_configure(struct libdecor_frame *frame,
|
||||||
|
struct libdecor_configuration *config,
|
||||||
|
void *udata){
|
||||||
|
int w = ctx.w;
|
||||||
|
int h = ctx.h;
|
||||||
|
/* (libdecor.h)
|
||||||
|
** " Get the expected size of the content for this configuration. "
|
||||||
|
*/
|
||||||
|
if (libdecor_configuration_get_content_size(config, frame, &w, &h)){
|
||||||
|
ctx.w = w;
|
||||||
|
ctx.h = h;
|
||||||
|
}
|
||||||
|
if (!ctx.configured){
|
||||||
|
ctx.configured = 1;
|
||||||
|
struct libdecor_state *state = libdecor_state_new(w, h);
|
||||||
|
libdecor_frame_commit(frame, state, config);
|
||||||
|
libdecor_state_free(state);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ctx.has_cached_config = 1;
|
||||||
|
ctx.cached_config = *config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (libdecor.h) libdecor_frame_interface::close
|
||||||
|
** " The window was requested to be closed by the compositor. "
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
libdecorevent__frame_close(struct libdecor_frame *frame, void *udata){
|
||||||
|
ctx.close_signal = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (libdecor.h) libdecor_frame_interface::commit
|
||||||
|
** " The window decoration asked to have the main surface to be
|
||||||
|
** committed. This is required when the decoration is implemented
|
||||||
|
** using synchronous subsurfaces. "
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
libdecorevent__frame_commit(struct libdecor_frame *frame, void *udata){
|
||||||
|
wl_surface_commit(ctx.wl_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (libdecor.h) libdecor_frame_interface::dismiss_popup
|
||||||
|
** " Any mapped popup that has a grab on the given seat should be dismissed. "
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
libdecorevent__frame_dismiss_popup(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (libdecor.h) libdecor_frame_interface::dismiss_popup
|
||||||
|
** " Any mapped popup that has a grab on the given seat should
|
||||||
|
** be dismissed. "
|
||||||
|
*/
|
||||||
|
struct libdecor_frame_interface libdecor_frame_interface = {
|
||||||
|
libdecorevent__frame_configure,
|
||||||
|
libdecorevent__frame_close,
|
||||||
|
libdecorevent__frame_commit,
|
||||||
|
libdecorevent__frame_dismiss_popup,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (libdecor.h) " Create a new libdecor context for the given wl_display " */
|
||||||
|
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){
|
||||||
|
/* (libdecor.h) " Decorate the given content wl_surface. " */
|
||||||
|
ctx.libdecor_frame = libdecor_decorate(ctx.libdecor, ctx.wl_surface,
|
||||||
|
&libdecor_frame_interface, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.libdecor_frame != 0){
|
||||||
|
/* (libdecor.h) " Set the title of the window. " */
|
||||||
|
libdecor_frame_set_title(ctx.libdecor_frame, "Example Window");
|
||||||
|
|
||||||
|
/* (libdecor.h) " This translates roughly to xdg_toplevel_set_min_size() "
|
||||||
|
**~ NOTE: I recommend setting this to something greater than 0 on each
|
||||||
|
** axis, to prevent some artifacts when resize goes 0 or negative.
|
||||||
|
*/
|
||||||
|
libdecor_frame_set_min_content_size(ctx.libdecor_frame, 80, 60);
|
||||||
|
|
||||||
|
/* (libdecor.h) " Map the window. " */
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*~ NOTE: Main loop */
|
||||||
|
int exit_loop = 0;
|
||||||
|
if (!swap_interval_success){
|
||||||
|
exit_loop = 1;
|
||||||
|
}
|
||||||
|
for (;!exit_loop;){
|
||||||
|
/* (libdecor.h)
|
||||||
|
** " Dispatch events. This function should be called when data is available on
|
||||||
|
** the file descriptor returned by libdecor_get_fd(). If timeout is zero, this
|
||||||
|
** function will never block. "
|
||||||
|
*/
|
||||||
|
libdecor_dispatch(ctx.libdecor, 0);
|
||||||
|
|
||||||
|
if (ctx.close_signal){
|
||||||
|
exit_loop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.has_cached_config){
|
||||||
|
ctx.has_cached_config = 0;
|
||||||
|
struct libdecor_state *state = libdecor_state_new(ctx.w, ctx.h);
|
||||||
|
libdecor_frame_commit(ctx.libdecor_frame, state, &ctx.cached_config);
|
||||||
|
libdecor_state_free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue