diff options
-rw-r--r-- | include/mgl/graphics/backend/egl.h | 8 | ||||
-rw-r--r-- | include/mgl/graphics/backend/glx.h | 8 | ||||
-rw-r--r-- | include/mgl/graphics/backend/graphics.h | 48 | ||||
-rw-r--r-- | include/mgl/mgl.h | 10 | ||||
-rw-r--r-- | include/mgl/window/wayland.h | 8 | ||||
-rw-r--r-- | include/mgl/window/window.h | 19 | ||||
-rw-r--r-- | include/mgl/window/x11.h | 8 | ||||
-rw-r--r-- | include/mgl/window/x11/window.h | 9 | ||||
-rw-r--r-- | meson.build | 9 | ||||
-rw-r--r-- | project.conf | 4 | ||||
-rw-r--r-- | src/graphics/backend/egl.c | 246 | ||||
-rw-r--r-- | src/graphics/backend/glx.c | 174 | ||||
-rw-r--r-- | src/graphics/backend/graphics.c | 54 | ||||
-rw-r--r-- | src/mgl.c | 208 | ||||
-rw-r--r-- | src/window/wayland.c | 214 | ||||
-rw-r--r-- | src/window/window.c | 21 | ||||
-rw-r--r-- | src/window/x11.c (renamed from src/window/x11/window.c) | 723 | ||||
-rw-r--r-- | tests/main.c | 2 |
18 files changed, 1122 insertions, 651 deletions
diff --git a/include/mgl/graphics/backend/egl.h b/include/mgl/graphics/backend/egl.h new file mode 100644 index 0000000..5dede61 --- /dev/null +++ b/include/mgl/graphics/backend/egl.h @@ -0,0 +1,8 @@ +#ifndef MGL_GRAPHICS_EGL +#define MGL_GRAPHICS_EGL + +#include "graphics.h" + +bool mgl_graphics_egl_init(mgl_graphics *self); + +#endif /* MGL_GRAPHICS_EGL */ diff --git a/include/mgl/graphics/backend/glx.h b/include/mgl/graphics/backend/glx.h new file mode 100644 index 0000000..e2a5758 --- /dev/null +++ b/include/mgl/graphics/backend/glx.h @@ -0,0 +1,8 @@ +#ifndef MGL_GRAPHICS_GLX +#define MGL_GRAPHICS_GLX + +#include "graphics.h" + +bool mgl_graphics_glx_init(mgl_graphics *self); + +#endif /* MGL_GRAPHICS_GLX */ diff --git a/include/mgl/graphics/backend/graphics.h b/include/mgl/graphics/backend/graphics.h new file mode 100644 index 0000000..59f4a0d --- /dev/null +++ b/include/mgl/graphics/backend/graphics.h @@ -0,0 +1,48 @@ +#ifndef MGL_GRAPHICS_H +#define MGL_GRAPHICS_H + +#include <stdbool.h> + +/* Each window should have its own mgl_graphics */ + +typedef struct mgl_graphics mgl_graphics; +typedef void* mgl_window_handle; + +typedef enum { + MGL_GRAPHICS_API_GLX, /* Only available when using X11 (or XWayland) */ + MGL_GRAPHICS_API_EGL +} mgl_graphics_api; + +struct mgl_graphics { + void (*deinit)(mgl_graphics *self); + bool (*make_context_current)(mgl_graphics *self, mgl_window_handle window); + void (*swap_buffers)(mgl_graphics *self, mgl_window_handle window); + bool (*set_swap_interval)(mgl_graphics *self, mgl_window_handle window, bool enabled); + void* (*get_xvisual_info)(mgl_graphics *self); + + /* Optional */ + void* (*get_display)(mgl_graphics *self); + void* (*get_context)(mgl_graphics *self); + + void *impl; + + mgl_graphics_api graphics_api; + bool alpha; +}; + +typedef struct { + mgl_graphics_api graphics_api; /* The graphics api to use. MGL_GRAPHICS_API_EGL by default */ + bool alpha; /* Support window alpha transparency. False by default */ +} mgl_graphics_create_params; + +bool mgl_graphics_init(mgl_graphics *self, const mgl_graphics_create_params *params); +void mgl_graphics_deinit(mgl_graphics *self); + +bool mgl_graphics_make_context_current(mgl_graphics *self, mgl_window_handle window); +void mgl_graphics_swap_buffers(mgl_graphics *self, mgl_window_handle window); +bool mgl_graphics_set_swap_interval(mgl_graphics *self, mgl_window_handle window, bool enabled); +void* mgl_graphics_get_xvisual_info(mgl_graphics *self); +void* mgl_graphics_get_display(mgl_graphics *self); +void* mgl_graphics_get_context(mgl_graphics *self); + +#endif /* MGL_GRAPHICS_H */ diff --git a/include/mgl/mgl.h b/include/mgl/mgl.h index c5d1f2d..fd3367e 100644 --- a/include/mgl/mgl.h +++ b/include/mgl/mgl.h @@ -9,7 +9,15 @@ typedef void* mgl_connection; typedef struct mgl_context mgl_context; typedef struct mgl_window mgl_window; +typedef enum { + MGL_WINDOW_SYSTEM_NATIVE, /* Use X11 on X11 and Wayland on Wayland */ + MGL_WINDOW_SYSTEM_X11, /* Use X11 on X11 and XWayland on Wayland */ + MGL_WINDOW_SYSTEM_WAYLAND, /* Use Wayland. If user runs on X11 then it fails to connect */ +} mgl_window_system; + struct mgl_context { + bool display_server_is_wayland; + mgl_window_system window_system; /* Window system requested with mgl_init */ mgl_connection connection; mgl_gl gl; mgl_window *current_window; @@ -30,7 +38,7 @@ struct mgl_context { Returns non-0 value on failure. Note: not thread safe. */ -int mgl_init(void); +int mgl_init(mgl_window_system window_system); /* Safe to call multiple times, but will only be deinitialized the last time called. diff --git a/include/mgl/window/wayland.h b/include/mgl/window/wayland.h new file mode 100644 index 0000000..081f87c --- /dev/null +++ b/include/mgl/window/wayland.h @@ -0,0 +1,8 @@ +#ifndef MGL_WINDOW_WAYLAND_H +#define MGL_WINDOW_WAYLAND_H + +#include "window.h" + +bool mgl_window_wayland_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window); + +#endif /* MGL_WINDOW_WAYLAND_H */ diff --git a/include/mgl/window/window.h b/include/mgl/window/window.h index 07c19ca..c788e9f 100644 --- a/include/mgl/window/window.h +++ b/include/mgl/window/window.h @@ -2,6 +2,7 @@ #define MGL_WINDOW_H #include "../graphics/color.h" +#include "../graphics/backend/graphics.h" #include "../system/vec.h" #include "../system/clock.h" #include "key.h" @@ -15,8 +16,8 @@ typedef union _XEvent XEvent; typedef struct mgl_event mgl_event; -/* x11 window handle. TODO: Add others when wayland, etc is added */ -typedef unsigned long mgl_window_handle; +/* x11/wayland window handle */ +typedef void* mgl_window_handle; typedef void* mgl_connection; typedef struct mgl_window mgl_window; @@ -41,16 +42,6 @@ typedef struct { } mgl_monitor; typedef enum { - MGL_WINDOW_SYSTEM_NATIVE, /* Use X11 on X11 and Wayland on Wayland */ - MGL_WINDOW_SYSTEM_X11 /* Use X11 on X11 and XWayland on Wayland */ -} mgl_window_system; - -typedef enum { - MGL_RENDER_API_GLX, /* Only available when using X11 (or XWayland) */ - MGL_RENDER_API_EGL -} mgl_render_api; - -typedef enum { MGL_WINDOW_TYPE_NORMAL, MGL_WINDOW_TYPE_DIALOG, /* Also sets the window as always on top */ MGL_WINDOW_TYPE_NOTIFICATION /* Also sets the window as always on top */ @@ -75,6 +66,7 @@ typedef void (*mgl_active_monitor_callback)(const mgl_monitor *monitor, void *us struct mgl_window { mgl_window_handle (*get_system_handle)(const mgl_window *self); + void (*deinit)(mgl_window *self); void (*close)(mgl_window *self); bool (*poll_event)(mgl_window *self, mgl_event *event); bool (*inject_x11_event)(mgl_window *self, XEvent *xev, mgl_event *event); /* Optional */ @@ -136,8 +128,7 @@ typedef struct { const char *class_name; mgl_window_type window_type; /* default: normal */ mgl_window_handle transient_for_window; /* 0 = none */ - mgl_render_api render_api; /* default: MGL_RENDER_API_GLX on X11 and MGL_RENDER_API_EGL on Wayland */ - mgl_window_system window_system; /* default: MGL_WINDOW_SYSTEM_NATIVE */ + mgl_graphics_api graphics_api; /* Can only be MGL_GRAPHICS_API_GLX in an X11 window. default: MGL_GRAPHICS_API_EGL */ } mgl_window_create_params; /* |params| can be NULL. Note: vsync is enabled by default */ diff --git a/include/mgl/window/x11.h b/include/mgl/window/x11.h new file mode 100644 index 0000000..6c4aa93 --- /dev/null +++ b/include/mgl/window/x11.h @@ -0,0 +1,8 @@ +#ifndef MGL_WINDOW_X11_H +#define MGL_WINDOW_X11_H + +#include "window.h" + +bool mgl_window_x11_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window); + +#endif /* MGL_WINDOW_X11_H */ diff --git a/include/mgl/window/x11/window.h b/include/mgl/window/x11/window.h deleted file mode 100644 index 9ffc968..0000000 --- a/include/mgl/window/x11/window.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef MGL_X11_WINDOW_H -#define MGL_X11_WINDOW_H - -#include "../window.h" - -bool mgl_x11_window_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window); -void mgl_x11_window_deinit(mgl_window *self); - -#endif /* MGL_X11_WINDOW_H */ diff --git a/meson.build b/meson.build index 5f20c58..88ebbd8 100644 --- a/meson.build +++ b/meson.build @@ -28,6 +28,10 @@ src = [ 'src/gl.c', ] +src += [ + 'src/window/wayland.c', +] + project_headers = [ 'include/mgl/graphics/rectangle.h', 'include/mgl/graphics/sprite.h', @@ -64,6 +68,11 @@ dep = [ cc.find_library('m'), ] +dep += [ + dependency('wayland-client'), + dependency('wayland-egl'), +] + public_headers = include_directories('include') project_target = static_library( diff --git a/project.conf b/project.conf index 8977367..e4ea76a 100644 --- a/project.conf +++ b/project.conf @@ -11,4 +11,6 @@ expose_include_dirs = ["include"] [dependencies] x11 = ">=0.5" xrender = ">=0.5" -xrandr = ">=0.5"
\ No newline at end of file +xrandr = ">=0.5" +wayland-client = ">=1" +wayland-egl = ">=15"
\ No newline at end of file diff --git a/src/graphics/backend/egl.c b/src/graphics/backend/egl.c new file mode 100644 index 0000000..f92e2fc --- /dev/null +++ b/src/graphics/backend/egl.c @@ -0,0 +1,246 @@ +#include "../../../include/mgl/graphics/backend/egl.h" +#include "../../../include/mgl/mgl.h" + +#include <stdlib.h> +#include <stdio.h> + +#include <X11/Xutil.h> +#include <X11/extensions/Xrender.h> + +static void mgl_graphics_egl_deinit(mgl_graphics *self); + +typedef struct { + EGLDisplay display; + EGLSurface surface; + EGLContext context; + EGLConfig *configs; + EGLConfig ecfg; + XVisualInfo *visual_info; +} mgl_graphics_egl; + +static int32_t mgl_graphics_egl_get_config_attrib(mgl_graphics_egl *self, EGLConfig ecfg, int32_t attribute_name) { + mgl_context *context = mgl_get_context(); + int32_t value = 0; + context->gl.eglGetConfigAttrib(self->display, ecfg, attribute_name, &value); + return value; +} + +static bool xvisual_match_alpha(Display *dpy, XVisualInfo *visual_info, bool alpha) { + XRenderPictFormat *pict_format = XRenderFindVisualFormat(dpy, visual_info->visual); + if(!pict_format) + return false; + return (alpha && pict_format->direct.alphaMask > 0) || (!alpha && pict_format->direct.alphaMask == 0); +} + +static bool mgl_graphics_context_choose(mgl_graphics_egl *self, mgl_context *context, bool alpha) { + self->configs = NULL; + self->ecfg = NULL; + self->visual_info = NULL; + + int32_t num_configs = 0; + context->gl.eglGetConfigs(self->display, NULL, 0, &num_configs); + if(num_configs == 0) { + fprintf(stderr, "mgl error: no configs found\n"); + return false; + } + + self->configs = (EGLConfig*)calloc(num_configs, sizeof(EGLConfig)); + if(!self->configs) { + fprintf(stderr, "mgl error: failed to allocate %d configs\n", (int)num_configs); + return false; + } + + context->gl.eglGetConfigs(self->display, self->configs, num_configs, &num_configs); + for(int i = 0; i < num_configs; i++) { + self->ecfg = self->configs[i]; + + if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) + continue; + + if(!(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) + continue; + + if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_RED_SIZE) != 8) + continue; + + if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_GREEN_SIZE) != 8) + continue; + + if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_BLUE_SIZE) != 8) + continue; + + if(mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_ALPHA_SIZE) != (alpha ? 8 : 0)) + continue; + + if(context->window_system == MGL_WINDOW_SYSTEM_WAYLAND) + break; + + XVisualInfo vi = {0}; + vi.visualid = mgl_graphics_egl_get_config_attrib(self, self->ecfg, EGL_NATIVE_VISUAL_ID); + if(!vi.visualid) + continue; + + int vis_count = 0; + self->visual_info = XGetVisualInfo(context->connection, VisualIDMask, &vi, &vis_count); + if(!self->visual_info) + continue; + + if(xvisual_match_alpha(context->connection, self->visual_info, alpha)) { + break; + } else { + XFree(self->visual_info); + self->visual_info = NULL; + self->ecfg = NULL; + } + } + + if(context->window_system == MGL_WINDOW_SYSTEM_X11 && !self->visual_info) { + if(self->configs) { + free(self->configs); + self->configs = NULL; + } + self->ecfg = NULL; + + fprintf(stderr, "mgl error: mgl_graphics_context_choose: no appropriate visual found\n"); + return false; + } + + return true; +} + +static bool mgl_graphics_egl_make_context_current(mgl_graphics *self, mgl_window_handle window) { + (void)window; + mgl_graphics_egl *impl = self->impl; + mgl_context *context = mgl_get_context(); + + if(!impl->surface) { + impl->surface = context->gl.eglCreateWindowSurface(impl->display, impl->ecfg, (EGLNativeWindowType)window, NULL); + if(!impl->surface) { + fprintf(stderr, "mgl error: mgl_graphics_egl_make_context_current: failed to create window surface\n"); + return false; + } + } + + return context->gl.eglMakeCurrent(impl->display, impl->surface, impl->surface, impl->context) != 0; +} + +static void mgl_graphics_egl_swap_buffers(mgl_graphics *self, mgl_window_handle window) { + (void)window; + mgl_graphics_egl *impl = self->impl; + mgl_context *context = mgl_get_context(); + context->gl.eglSwapBuffers(impl->display, impl->surface); +} + +static bool mgl_graphics_egl_set_swap_interval(mgl_graphics *self, mgl_window_handle window, bool enabled) { + (void)window; + mgl_graphics_egl *impl = self->impl; + mgl_context *context = mgl_get_context(); + return context->gl.eglSwapInterval(impl->display, (int32_t)enabled) == 1; +} + +static void* mgl_graphics_egl_get_xvisual_info(mgl_graphics *self) { + mgl_graphics_egl *impl = self->impl; + return impl->visual_info; +} + +static void* mgl_graphics_egl_get_display(mgl_graphics *self) { + mgl_graphics_egl *impl = self->impl; + return impl->display; +} + +static void* mgl_graphics_egl_get_context(mgl_graphics *self) { + mgl_graphics_egl *impl = self->impl; + return impl->context; +} + +bool mgl_graphics_egl_init(mgl_graphics *self) { + mgl_graphics_egl *impl = calloc(1, sizeof(mgl_graphics_egl)); + if(!impl) + return false; + + self->deinit = mgl_graphics_egl_deinit; + self->make_context_current = mgl_graphics_egl_make_context_current; + self->swap_buffers = mgl_graphics_egl_swap_buffers; + self->set_swap_interval = mgl_graphics_egl_set_swap_interval; + self->get_xvisual_info = mgl_graphics_egl_get_xvisual_info; + self->get_display = mgl_graphics_egl_get_display; + self->get_context = mgl_graphics_egl_get_context; + self->impl = impl; + + const int32_t ctxattr[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE, EGL_NONE + }; + + mgl_context *context = mgl_get_context(); + context->gl.eglBindAPI(EGL_OPENGL_API); + + impl->display = context->gl.eglGetDisplay((EGLNativeDisplayType)context->connection); + if(!impl->display) { + fprintf(stderr, "mgl error: mgl_graphics_egl_init: eglGetDisplay failed\n"); + mgl_graphics_egl_deinit(self); + return false; + } + + if(!context->gl.eglInitialize(impl->display, NULL, NULL)) { + fprintf(stderr, "mgl error: mgl_graphics_egl_init: eglInitialize failed\n"); + mgl_graphics_egl_deinit(self); + return false; + } + + if(!mgl_graphics_context_choose(impl, context, self->alpha)) { + mgl_graphics_egl_deinit(self); + return false; + } + + impl->context = context->gl.eglCreateContext(impl->display, impl->ecfg, NULL, ctxattr); + if(!impl->context) { + fprintf(stderr, "mgl error: mgl_graphics_egl_init: failed to create egl context\n"); + mgl_graphics_egl_deinit(self); + return false; + } + + return true; +} + +void mgl_graphics_egl_deinit(mgl_graphics *self) { + mgl_graphics_egl *impl = self->impl; + if(!impl) + return; + + mgl_context *context = mgl_get_context(); + + if(impl->visual_info) { + XFree(impl->visual_info); + impl->visual_info = NULL; + } + + if(impl->configs) { + free(impl->configs); + impl->configs = NULL; + } + + if(impl->context) { + context->gl.eglMakeCurrent(impl->display, NULL, NULL, NULL); + context->gl.eglDestroyContext(impl->display, impl->context); + impl->context = NULL; + } + + if(impl->surface) { + context->gl.eglDestroySurface(impl->display, impl->surface); + impl->surface = NULL; + } + + if(impl->display) { + context->gl.eglTerminate(impl->display); + impl->display = NULL; + } + + if(impl->visual_info) { + XFree(impl->visual_info); + impl->visual_info = NULL; + } + + free(self->impl); + self->impl = NULL; +} diff --git a/src/graphics/backend/glx.c b/src/graphics/backend/glx.c new file mode 100644 index 0000000..548b0a0 --- /dev/null +++ b/src/graphics/backend/glx.c @@ -0,0 +1,174 @@ +#include "../../../include/mgl/graphics/backend/glx.h" +#include "../../../include/mgl/mgl.h" + +#include <stdlib.h> +#include <stdio.h> + +#include <X11/Xutil.h> +#include <X11/extensions/Xrender.h> + +static void mgl_graphics_glx_deinit(mgl_graphics *self); + +typedef struct { + GLXContext glx_context; + GLXFBConfig *fbconfigs; + GLXFBConfig fbconfig; + XVisualInfo *visual_info; +} mgl_graphics_glx; + +static bool xvisual_match_alpha(Display *dpy, XVisualInfo *visual_info, bool alpha) { + XRenderPictFormat *pict_format = XRenderFindVisualFormat(dpy, visual_info->visual); + if(!pict_format) + return false; + return (alpha && pict_format->direct.alphaMask > 0) || (!alpha && pict_format->direct.alphaMask == 0); +} + +static bool mgl_graphics_glx_context_choose(mgl_graphics_glx *self, mgl_context *context, bool alpha) { + const int attr[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, alpha ? 8 : 0, + // TODO: + //GLX_DEPTH_SIZE, 0, + None + }; + + self->fbconfigs = NULL; + self->visual_info = NULL; + self->fbconfig = NULL; + + int numfbconfigs = 0; + self->fbconfigs = context->gl.glXChooseFBConfig(context->connection, DefaultScreen(context->connection), attr, &numfbconfigs); + for(int i = 0; i < numfbconfigs; i++) { + self->visual_info = (XVisualInfo*)context->gl.glXGetVisualFromFBConfig(context->connection, self->fbconfigs[i]); + if(!self->visual_info) + continue; + + if(xvisual_match_alpha(context->connection, self->visual_info, alpha)) { + self->fbconfig = self->fbconfigs[i]; + break; + } else { + XFree(self->visual_info); + self->visual_info = NULL; + self->fbconfig = NULL; + } + } + + if(!self->visual_info) { + if(self->fbconfigs) { + XFree(self->fbconfigs); + self->fbconfigs = NULL; + } + self->fbconfig = NULL; + + fprintf(stderr, "mgl error: no appropriate visual found\n"); + return false; + } + + return true; +} + +static bool mgl_graphics_glx_make_context_current(mgl_graphics *self, mgl_window_handle window) { + mgl_graphics_glx *impl = self->impl; + mgl_context *context = mgl_get_context(); + return context->gl.glXMakeContextCurrent(context->connection, (GLXDrawable)window, (GLXDrawable)window, impl->glx_context) != 0; +} + +static void mgl_graphics_glx_swap_buffers(mgl_graphics *self, mgl_window_handle window) { + (void)self; + mgl_context *context = mgl_get_context(); + context->gl.glXSwapBuffers(context->connection, (GLXDrawable)window); +} + +/* TODO: Use gl OML present for other platforms than nvidia? nvidia doesn't support present yet */ +/* TODO: check for glx swap control extension string (GLX_EXT_swap_control, etc) */ +static bool mgl_graphics_glx_set_swap_interval(mgl_graphics *self, mgl_window_handle window, bool enabled) { + (void)self; + mgl_context *context = mgl_get_context(); + + int result = 0; + if(context->gl.glXSwapIntervalEXT) { + context->gl.glXSwapIntervalEXT(context->connection, (GLXDrawable)window, enabled); + } else if(context->gl.glXSwapIntervalMESA) { + result = context->gl.glXSwapIntervalMESA(enabled); + } else if(context->gl.glXSwapIntervalSGI) { + result = context->gl.glXSwapIntervalSGI(enabled); + } else { + static int warned = 0; + if (!warned) { + warned = 1; + fprintf(stderr, "mgl warning: setting vertical sync not supported\n"); + } + } + + if(result != 0) + fprintf(stderr, "mgl warning: setting vertical sync failed\n"); + + return result == 0; +} + +static void* mgl_graphics_glx_get_xvisual_info(mgl_graphics *self) { + mgl_graphics_glx *impl = self->impl; + return impl->visual_info; +} + +bool mgl_graphics_glx_init(mgl_graphics *self) { + mgl_graphics_glx *impl = calloc(1, sizeof(mgl_graphics_glx)); + if(!impl) + return false; + + self->deinit = mgl_graphics_glx_deinit; + self->make_context_current = mgl_graphics_glx_make_context_current; + self->swap_buffers = mgl_graphics_glx_swap_buffers; + self->set_swap_interval = mgl_graphics_glx_set_swap_interval; + self->get_xvisual_info = mgl_graphics_glx_get_xvisual_info; + self->impl = impl; + + mgl_context *context = mgl_get_context(); + if(!mgl_graphics_glx_context_choose(impl, context, self->alpha)) { + mgl_graphics_glx_deinit(self); + return false; + } + + impl->glx_context = context->gl.glXCreateNewContext(context->connection, impl->fbconfig, GLX_RGBA_TYPE, 0, True); + if(!impl->glx_context) { + fprintf(stderr, "mgl error: mgl_graphics_glx_init: glXCreateContext failed\n"); + mgl_graphics_glx_deinit(self); + return false; + } + + return true; +} + +void mgl_graphics_glx_deinit(mgl_graphics *self) { + mgl_graphics_glx *impl = self->impl; + if(!impl) + return; + + mgl_context *context = mgl_get_context(); + + if(impl->glx_context) { + context->gl.glXMakeContextCurrent(context->connection, None, None, NULL); + context->gl.glXDestroyContext(context->connection, impl->glx_context); + impl->glx_context = NULL; + } + + if(impl->visual_info) { + XFree(impl->visual_info); + impl->visual_info = NULL; + } + + if(impl->fbconfigs) { + XFree(impl->fbconfigs); + impl->fbconfigs = NULL; + } + + impl->fbconfig = NULL; + + free(self->impl); + self->impl = NULL; +} diff --git a/src/graphics/backend/graphics.c b/src/graphics/backend/graphics.c new file mode 100644 index 0000000..792894e --- /dev/null +++ b/src/graphics/backend/graphics.c @@ -0,0 +1,54 @@ +#include "../../../include/mgl/graphics/backend/graphics.h" +#include "../../../include/mgl/graphics/backend/glx.h" +#include "../../../include/mgl/graphics/backend/egl.h" + +#include <string.h> + +bool mgl_graphics_init(mgl_graphics *self, const mgl_graphics_create_params *params) { + memset(self, 0, sizeof(*self)); + self->graphics_api = params ? params->graphics_api : MGL_GRAPHICS_API_EGL; + self->alpha = params && params->alpha; + + switch(self->graphics_api) { + case MGL_GRAPHICS_API_GLX: + return mgl_graphics_glx_init(self); + case MGL_GRAPHICS_API_EGL: + return mgl_graphics_egl_init(self); + } + return false; +} + +void mgl_graphics_deinit(mgl_graphics *self) { + if(self->deinit) + self->deinit(self); +} + +bool mgl_graphics_make_context_current(mgl_graphics *self, mgl_window_handle window) { + return self->make_context_current(self, window); +} + +void mgl_graphics_swap_buffers(mgl_graphics *self, mgl_window_handle window) { + self->swap_buffers(self, window); +} + +bool mgl_graphics_set_swap_interval(mgl_graphics *self, mgl_window_handle window, bool enabled) { + return self->set_swap_interval(self, window, enabled); +} + +void* mgl_graphics_get_xvisual_info(mgl_graphics *self) { + return self->get_xvisual_info(self); +} + +void* mgl_graphics_get_display(mgl_graphics *self) { + if(self->get_display) + return self->get_display(self); + else + return NULL; +} + +void* mgl_graphics_get_context(mgl_graphics *self) { + if(self->get_context) + return self->get_context(self); + else + return NULL; +} @@ -1,18 +1,21 @@ #include "../include/mgl/mgl.h" -#include <X11/Xutil.h> -#include <X11/XKBlib.h> -#include <X11/extensions/Xrender.h> -#include <X11/extensions/Xrandr.h> #include <stdbool.h> #include <string.h> #include <stdio.h> #include <stdlib.h> +#include <assert.h> + +#include <X11/Xutil.h> +#include <X11/XKBlib.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/Xrandr.h> +#include <wayland-client.h> static mgl_context context; static int init_count = 0; static XErrorHandler prev_xerror = NULL; static XIOErrorHandler prev_xioerror = NULL; -static bool connected_to_x_server = false; +static bool connected_to_display_server = false; static int mgl_x_error_handler(Display *display, XErrorEvent *ee) { (void)display; @@ -22,7 +25,8 @@ static int mgl_x_error_handler(Display *display, XErrorEvent *ee) { static int mgl_x_io_error_handler(Display *display) { (void)display; - connected_to_x_server = false; + /* TODO: Do something equivalent for wayland */ + connected_to_display_server = false; return 0; } @@ -54,45 +58,112 @@ static bool xrandr_is_supported(Display *display, int *event_base, int *error_ba return major_version > 1 || (major_version == 1 && minor_version >= 2); } -int mgl_init(void) { - ++init_count; - if(init_count == 1) { - setenv("__GL_MaxFramesAllowed", "1", true); - memset(&context, 0, sizeof(context)); +static bool is_xwayland(Display *dpy) { + int opcode, event, error; + return XQueryExtension(dpy, "XWAYLAND", &opcode, &event, &error); +} +static int mgl_init_x11(void) { + if(!context.connection) context.connection = XOpenDisplay(NULL); - if(!context.connection) { - fprintf(stderr, "mgl error: XOpenDisplay failed\n"); - mgl_deinit(); - return -1; - } - connected_to_x_server = true; - /* If we dont call we will never get a MappingNotify until a key has been pressed */ - XKeysymToKeycode(context.connection, XK_F1); - prev_xerror = XSetErrorHandler(mgl_x_error_handler); - prev_xioerror = XSetIOErrorHandler(mgl_x_io_error_handler); + if(!context.connection) { + fprintf(stderr, "mgl error: mgl_init_x11: failed to connect to the X11 server\n"); + mgl_deinit(); + return -1; + } + connected_to_display_server = true; + /* If we dont call we will never get a MappingNotify until a key has been pressed */ + XKeysymToKeycode(context.connection, XK_F1); - if(!xrender_is_supported(context.connection, &context.render_event_base, &context.render_error_base)) { - fprintf(stderr, "mgl error: x11 render extension is not supported by your X server\n"); - mgl_deinit(); - return -1; - } + prev_xerror = XSetErrorHandler(mgl_x_error_handler); + prev_xioerror = XSetIOErrorHandler(mgl_x_io_error_handler); - if(!xrandr_is_supported(context.connection, &context.randr_event_base, &context.randr_error_base)) { - fprintf(stderr, "mgl error: x11 randr extension is not supported by your X server\n"); - mgl_deinit(); - return -1; + context.display_server_is_wayland = is_xwayland(context.connection); + + if(!xrender_is_supported(context.connection, &context.render_event_base, &context.render_error_base)) { + fprintf(stderr, "mgl error: mgl_init_x11: x11 render extension is not supported by your X server\n"); + mgl_deinit(); + return -1; + } + + if(!xrandr_is_supported(context.connection, &context.randr_event_base, &context.randr_error_base)) { + fprintf(stderr, "mgl error: mgl_init_x11: x11 randr extension is not supported by your X server\n"); + mgl_deinit(); + return -1; + } + + XRRSelectInput(context.connection, DefaultRootWindow(context.connection), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask); + + XInitThreads(); + XkbSetDetectableAutoRepeat(context.connection, True, NULL); + + context.wm_delete_window_atom = XInternAtom(context.connection, "WM_DELETE_WINDOW", False); + context.net_wm_ping_atom = XInternAtom(context.connection, "_NET_WM_PING", False); + context.net_wm_pid_atom = XInternAtom(context.connection, "_NET_WM_PID", False); + return 0; +} + +static int mgl_init_wayland(void) { + context.connection = wl_display_connect(NULL); + if(!context.connection) { + fprintf(stderr, "mgl error: mgl_init_wayland: failed to connect to the Wayland server\n"); + mgl_deinit(); + return -1; + } + connected_to_display_server = true; + return 0; +} + +static int mgl_init_native(void) { + context.connection = XOpenDisplay(NULL); + if(context.connection) { + context.display_server_is_wayland = is_xwayland(context.connection); + if(context.display_server_is_wayland) { + XCloseDisplay(context.connection); + context.connection = NULL; } + } else { + context.display_server_is_wayland = true; + } - XRRSelectInput(context.connection, DefaultRootWindow(context.connection), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask); + if(context.display_server_is_wayland) { + context.window_system = MGL_WINDOW_SYSTEM_WAYLAND; + if(mgl_init_wayland() != 0) + return -1; + } else { + context.window_system = MGL_WINDOW_SYSTEM_X11; + if(mgl_init_x11() != 0) + return -1; + } - XInitThreads(); - XkbSetDetectableAutoRepeat(context.connection, True, NULL); + return 0; +} - context.wm_delete_window_atom = XInternAtom(context.connection, "WM_DELETE_WINDOW", False); - context.net_wm_ping_atom = XInternAtom(context.connection, "_NET_WM_PING", False); - context.net_wm_pid_atom = XInternAtom(context.connection, "_NET_WM_PID", False); +int mgl_init(mgl_window_system window_system) { + ++init_count; + if(init_count == 1) { + setenv("__GL_MaxFramesAllowed", "1", true); + memset(&context, 0, sizeof(context)); + context.window_system = window_system; + + switch(window_system) { + case MGL_WINDOW_SYSTEM_NATIVE: { + if(mgl_init_native() != 0) + return -1; + break; + } + case MGL_WINDOW_SYSTEM_X11: { + if(mgl_init_x11() != 0) + return -1; + break; + } + case MGL_WINDOW_SYSTEM_WAYLAND: { + if(mgl_init_wayland() != 0) + return -1; + break; + } + } if(mgl_gl_load(&context.gl) != 0) { mgl_deinit(); @@ -102,30 +173,49 @@ int mgl_init(void) { return 0; } -void mgl_deinit(void) { - if(init_count == 1) { - if(context.connection) { - XCloseDisplay(context.connection); - context.connection = NULL; - connected_to_x_server = false; +static void mgl_deinit_x11(void) { + if(context.connection) { + XCloseDisplay(context.connection); + context.connection = NULL; + connected_to_display_server = false; + } - /* - GLX needs to be unloaded after closing the display on nvidia because - nvidia registers cleanup callbacks on exit, that uses the x11 display. - */ - mgl_gl_unload(&context.gl); - } + if(prev_xioerror) { + XSetIOErrorHandler(prev_xioerror); + prev_xioerror = NULL; + } - if(prev_xioerror) { - XSetIOErrorHandler(prev_xioerror); - prev_xioerror = NULL; - } + if(prev_xerror) { + XSetErrorHandler(prev_xerror); + prev_xerror = NULL; + } +} - if(prev_xerror) { - XSetErrorHandler(prev_xerror); - prev_xerror = NULL; +static void mgl_deinit_wayland(void) { + if(context.connection) { + wl_display_disconnect(context.connection); + context.connection = NULL; + connected_to_display_server = false; + } +} + +void mgl_deinit(void) { + if(init_count == 1) { + switch(context.window_system) { + case MGL_WINDOW_SYSTEM_NATIVE: + assert(false); + break; + case MGL_WINDOW_SYSTEM_X11: { + mgl_deinit_x11(); + break; + } + case MGL_WINDOW_SYSTEM_WAYLAND: { + mgl_deinit_wayland(); + break; + } } + mgl_gl_unload(&context.gl); context.current_window = NULL; } @@ -144,11 +234,15 @@ mgl_context* mgl_get_context(void) { } bool mgl_is_connected_to_display_server(void) { - return connected_to_x_server; + return connected_to_display_server; } void mgl_ping_display_server(void) { - if(context.connection) { + if(!context.connection) + return; + + // TODO: Do something equivalent for wayland + if(context.window_system == MGL_WINDOW_SYSTEM_X11) { XNoOp(context.connection); XFlush(context.connection); } diff --git a/src/window/wayland.c b/src/window/wayland.c new file mode 100644 index 0000000..2671e53 --- /dev/null +++ b/src/window/wayland.c @@ -0,0 +1,214 @@ +#include "../../../include/mgl/window/wayland.h" +#include "../../../include/mgl/mgl.h" + +#include <wayland-client.h> +#include <wayland-egl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +static void mgl_window_wayland_deinit(mgl_window *self); + +typedef struct { + struct wl_display *display; + struct wl_egl_window *window; + struct wl_registry *registry; + struct wl_surface *surface; + struct wl_compositor *compositor; + + mgl_graphics graphics; +} mgl_window_wayland; + +static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { + (void)version; + mgl_window *self = data; + mgl_window_wayland *impl = self->impl; + if(strcmp(interface, "wl_compositor") == 0) { + if(impl->compositor) { + wl_compositor_destroy(impl->compositor); + impl->compositor = NULL; + } + impl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } else if(strcmp(interface, wl_output_interface.name) == 0) { + if(version < 4) { + fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor\n"); + return; + } + + if(self->num_monitors == MGL_MAX_MONITORS) { + fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %u\n", MGL_MAX_MONITORS, name); + return; + } + + // gsr_wayland_output *gsr_output = &window_wayland->outputs[window_wayland->num_outputs]; + // ++self->num_monitors; + // *gsr_output = (gsr_wayland_output) { + // .wl_name = name, + // .output = wl_registry_bind(registry, name, &wl_output_interface, 4), + // .pos = { .x = 0, .y = 0 }, + // .size = { .x = 0, .y = 0 }, + // .transform = 0, + // .name = NULL, + // }; + // wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); + } +} + +static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { + (void)data; + (void)registry; + (void)name; + // TODO: Remove output +} + +static struct wl_registry_listener registry_listener = { + .global = registry_add_object, + .global_remove = registry_remove_object, +}; + +// TODO: Use title, params and existing_window +bool mgl_wayland_setup_window(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window) { + mgl_window_wayland *impl = self->impl; + + mgl_vec2i window_size = params ? params->size : (mgl_vec2i){ 0, 0 }; + if(window_size.x <= 0 || window_size.y <= 0) { + window_size.x = 640; + window_size.y = 480; + } + self->size = window_size; + + impl->display = wl_display_connect(NULL); + if(!impl->display) { + fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to connect to the Wayland server\n"); + return false; + } + + impl->registry = wl_display_get_registry(impl->display); /* TODO: Error checking */ + wl_registry_add_listener(impl->registry, ®istry_listener, self); /* TODO: Error checking */ + + /* Fetch globals */ + wl_display_roundtrip(impl->display); + + /* Fetch wl_output */ + //wl_display_roundtrip(impl->display); + + if(!impl->compositor) { + fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to find compositor\n"); + return false; + } + + impl->surface = wl_compositor_create_surface(impl->compositor); + if(!impl->surface) { + fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to create surface\n"); + return false; + } + + impl->window = wl_egl_window_create(impl->surface, self->size.x, self->size.y); + if(!impl->window) { + fprintf(stderr, "mgl error: mgl_wayland_setup_window: failed to create the Wayland window\n"); + return false; + } + + return true; +} + +bool mgl_window_wayland_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window) { + mgl_window_wayland *impl = calloc(1, sizeof(mgl_window_wayland)); + if(!impl) + return false; + + // self->get_system_handle = mgl_window_x11_get_system_handle; + // self->close = mgl_window_x11_close; + // self->inject_x11_event = mgl_window_x11_inject_x11_event; + // self->poll_event = mgl_window_x11_poll_event; + // self->swap_buffers = mgl_window_x11_swap_buffers; + // self->set_visible = mgl_window_x11_set_visible; + // self->is_key_pressed = mgl_window_x11_is_key_pressed; + // self->is_mouse_button_pressed = mgl_window_x11_is_mouse_button_pressed; + // self->set_title = mgl_window_x11_set_title; + // self->set_cursor_visible = mgl_window_x11_set_cursor_visible; + // self->set_vsync_enabled = mgl_window_x11_set_vsync_enabled; + // self->is_vsync_enabled = mgl_window_x11_is_vsync_enabled; + // self->set_fullscreen = mgl_window_x11_set_fullscreen; + // self->is_fullscreen = mgl_window_x11_is_fullscreen; + // self->set_position = mgl_window_x11_set_position; + // self->set_size = mgl_window_x11_set_size; + // self->set_size_limits = mgl_window_x11_set_size_limits; + // self->set_clipboard = mgl_window_x11_set_clipboard; + // self->get_clipboard = mgl_window_x11_get_clipboard; + // self->get_clipboard_string = mgl_window_x11_get_clipboard_string; + // self->set_key_repeat_enabled = mgl_window_x11_set_key_repeat_enabled; + // self->flush = mgl_window_x11_flush; + // self->get_egl_display = mgl_window_x11_get_egl_display; + // self->get_egl_context = mgl_window_x11_get_egl_context; + // self->for_each_active_monitor_output = mgl_window_x11_for_each_active_monitor_output; + + self->deinit = mgl_window_wayland_deinit; + self->impl = impl; + + if(!mgl_wayland_setup_window(self, title, params, existing_window)) { + mgl_window_wayland_deinit(self); + return false; + } + + assert(!params || params->graphics_api == MGL_GRAPHICS_API_EGL); + const bool alpha = params && params->support_alpha; + if(!mgl_graphics_init(&impl->graphics, &(mgl_graphics_create_params){ .graphics_api = MGL_GRAPHICS_API_EGL, .alpha = alpha })) { + mgl_window_wayland_deinit(self); + return false; + } + + if(!mgl_graphics_make_context_current(&impl->graphics, impl->window)) { + fprintf(stderr, "mgl error: mgl_window_wayland_init: failed to make window context current\n"); + mgl_window_wayland_deinit(self); + return false; + } + + self->vsync_enabled = true; + mgl_graphics_set_swap_interval(&impl->graphics, (mgl_window_handle)impl->window, self->vsync_enabled); + + mgl_context *context = mgl_get_context(); + context->current_window = self; + return true; +} + +void mgl_window_wayland_deinit(mgl_window *self) { + mgl_window_wayland *impl = self->impl; + if(!impl) + return; + + mgl_graphics_deinit(&impl->graphics); + + if(impl->window) { + wl_egl_window_destroy(impl->window); + impl->window = NULL; + } + + if(impl->surface) { + wl_surface_destroy(impl->surface); + impl->surface = NULL; + } + + if(impl->compositor) { + wl_compositor_destroy(impl->compositor); + impl->compositor = NULL; + } + + if(impl->registry) { + wl_registry_destroy(impl->registry); + impl->registry = NULL; + } + + if(impl->display) { + wl_display_disconnect(impl->display); + impl->display = NULL; + } + + mgl_context *context = mgl_get_context(); + if(context->current_window == self) + context->current_window = NULL; + + free(self->impl); + self->impl = NULL; +} diff --git a/src/window/window.c b/src/window/window.c index 56df01a..a11bc15 100644 --- a/src/window/window.c +++ b/src/window/window.c @@ -1,5 +1,6 @@ #include "../../include/mgl/window/window.h" -#include "../../include/mgl/window/x11/window.h" +#include "../../include/mgl/window/x11.h" +#include "../../include/mgl/window/wayland.h" #include "../../include/mgl/mgl.h" #include <stdlib.h> @@ -8,12 +9,22 @@ #include <string.h> int mgl_window_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window) { - // TODO: Handle |params->window_system| memset(self, 0, sizeof(*self)); self->vsync_enabled = true; self->key_repeat_enabled = true; mgl_clock_init(&self->frame_timer); - return mgl_x11_window_init(self, title, params, 0) ? 0 : -1; + + mgl_context *context = mgl_get_context(); + switch(context->window_system) { + case MGL_WINDOW_SYSTEM_NATIVE: + assert(false); + break; + case MGL_WINDOW_SYSTEM_X11: + return mgl_window_x11_init(self, title, params, existing_window) ? 0 : -1; + case MGL_WINDOW_SYSTEM_WAYLAND: + return mgl_window_wayland_init(self, title, params, existing_window) ? 0 : -1; + } + return -1; } int mgl_window_create(mgl_window *self, const char *title, const mgl_window_create_params *params) { @@ -25,10 +36,12 @@ int mgl_window_init_from_existing_window(mgl_window *self, mgl_window_handle exi } void mgl_window_deinit(mgl_window *self) { - mgl_x11_window_deinit(self); + if(self->deinit) + self->deinit(self); } void mgl_window_clear(mgl_window *self, mgl_color color) { + (void)self; mgl_context *context = mgl_get_context(); context->gl.glClearColor((float)color.r / 255.0f, (float)color.g / 255.0f, (float)color.b / 255.0f, (float)color.a / 255.0f); context->gl.glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/window/x11/window.c b/src/window/x11.c index 59df3bc..113fc1a 100644 --- a/src/window/x11/window.c +++ b/src/window/x11.c @@ -1,14 +1,16 @@ -#include "../../../include/mgl/window/x11/window.h" +#include "../../../include/mgl/window/x11.h" #include "../../../include/mgl/window/event.h" #include "../../../include/mgl/mgl.h" #include "../../../include/mgl/system/utf8.h" #include "../../../include/mgl/system/clock.h" + #include <X11/Xutil.h> #include <X11/cursorfont.h> #include <X11/Xatom.h> #include <X11/extensions/Xrender.h> #include <X11/extensions/Xrandr.h> #include <X11/XF86keysym.h> + #include <stdlib.h> #include <string.h> #include <stdio.h> @@ -16,6 +18,8 @@ #include <assert.h> #include <unistd.h> +static void mgl_window_x11_deinit(mgl_window *self); + /* TODO: Handle XIM better. Set XIM position to text position on screen (for text input) and reset input when selecting a new text input, etc */ /* TODO: Separate events from windows. Especially when it comes to monitor events */ @@ -30,30 +34,12 @@ typedef struct { } x11_events_circular_buffer; typedef struct { - GLXContext glx_context; - GLXFBConfig *fbconfigs; - GLXFBConfig fbconfig; - XVisualInfo *visual_info; -} mgl_x11_window_glx; - -typedef struct { - EGLDisplay egl_display; - EGLSurface egl_surface; - EGLContext egl_context; - EGLConfig *configs; - EGLConfig ecfg; - XVisualInfo *visual_info; -} mgl_x11_window_egl; - -typedef struct { void *dpy; Window window; char *clipboard_string; size_t clipboard_size; - mgl_x11_window_glx glx; - mgl_x11_window_egl egl; - mgl_render_api render_api; + mgl_graphics graphics; Colormap color_map; XIM xim; XIC xic; @@ -91,7 +77,7 @@ typedef struct { an event for all of them. */ x11_events_circular_buffer events; -} mgl_x11_window; +} mgl_window_x11; static void x11_events_circular_buffer_init(x11_events_circular_buffer *self) { self->start = 0; @@ -119,329 +105,7 @@ static bool x11_events_circular_buffer_pop(x11_events_circular_buffer *self, mgl return true; } -static bool xvisual_match_alpha(Display *dpy, XVisualInfo *visual_info, bool alpha) { - XRenderPictFormat *pict_format = XRenderFindVisualFormat(dpy, visual_info->visual); - if(!pict_format) - return false; - return (alpha && pict_format->direct.alphaMask > 0) || (!alpha && pict_format->direct.alphaMask == 0); -} - -static bool glx_context_choose(mgl_context *context, mgl_x11_window_glx *glx, bool alpha) { - const int attr[] = { - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_DOUBLEBUFFER, True, - GLX_RED_SIZE, 8, - GLX_GREEN_SIZE, 8, - GLX_BLUE_SIZE, 8, - GLX_ALPHA_SIZE, alpha ? 8 : 0, - // TODO: - //GLX_DEPTH_SIZE, 0, - None - }; - - glx->fbconfigs = NULL; - glx->visual_info = NULL; - glx->fbconfig = NULL; - - int numfbconfigs = 0; - glx->fbconfigs = context->gl.glXChooseFBConfig(context->connection, DefaultScreen(context->connection), attr, &numfbconfigs); - for(int i = 0; i < numfbconfigs; i++) { - glx->visual_info = (XVisualInfo*)context->gl.glXGetVisualFromFBConfig(context->connection, glx->fbconfigs[i]); - if(!glx->visual_info) - continue; - - if(xvisual_match_alpha(context->connection, glx->visual_info, alpha)) { - glx->fbconfig = glx->fbconfigs[i]; - break; - } else { - XFree(glx->visual_info); - glx->visual_info = NULL; - glx->fbconfig = NULL; - } - } - - if(!glx->visual_info) { - if(glx->fbconfigs) { - XFree(glx->fbconfigs); - glx->fbconfigs = NULL; - } - glx->fbconfig = NULL; - - fprintf(stderr, "mgl error: no appropriate visual found\n"); - return false; - } - - return true; -} - -static void mgl_x11_window_glx_deinit(mgl_x11_window_glx *self) { - mgl_context *context = mgl_get_context(); - - if(self->glx_context) { - context->gl.glXMakeContextCurrent(context->connection, None, None, NULL); - context->gl.glXDestroyContext(context->connection, self->glx_context); - self->glx_context = NULL; - } - - if(self->visual_info) { - XFree(self->visual_info); - self->visual_info = NULL; - } - - if(self->fbconfigs) { - XFree(self->fbconfigs); - self->fbconfigs = NULL; - } - - self->fbconfig = NULL; -} - -static bool mgl_x11_window_glx_init(mgl_x11_window_glx *self, bool alpha) { - mgl_context *context = mgl_get_context(); - memset(self, 0, sizeof(*self)); - - if(!glx_context_choose(context, self, alpha)) { - mgl_x11_window_glx_deinit(self); - return false; - } - - self->glx_context = context->gl.glXCreateNewContext(context->connection, self->fbconfig, GLX_RGBA_TYPE, 0, True); - if(!self->glx_context) { - fprintf(stderr, "mgl error: mgl_x11_window_glx_init: glXCreateContext failed\n"); - mgl_x11_window_glx_deinit(self); - return false; - } - - return true; -} - -static bool mgl_x11_window_glx_make_context_current(mgl_x11_window_glx *self, Window window) { - mgl_context *context = mgl_get_context(); - return context->gl.glXMakeContextCurrent(context->connection, window, window, self->glx_context) != 0; -} - -static void mgl_x11_window_glx_swap_buffers(mgl_x11_window_glx *self, Window window) { - (void)self; - mgl_context *context = mgl_get_context(); - context->gl.glXSwapBuffers(context->connection, window); -} - -static XVisualInfo* mgl_x11_window_glx_get_xvisual_info(mgl_x11_window_glx *self) { - return self->visual_info; -} - -static bool mgl_x11_window_glx_set_swap_interval(mgl_x11_window_glx *self, Window window, int enabled) { - (void)self; - mgl_context *context = mgl_get_context(); - - int result = 0; - if(context->gl.glXSwapIntervalEXT) { - context->gl.glXSwapIntervalEXT(context->connection, window, enabled ? 1 : 0); - } else if(context->gl.glXSwapIntervalMESA) { - result = context->gl.glXSwapIntervalMESA(enabled ? 1 : 0); - } else if(context->gl.glXSwapIntervalSGI) { - result = context->gl.glXSwapIntervalSGI(enabled ? 1 : 0); - } else { - static int warned = 0; - if (!warned) { - warned = 1; - fprintf(stderr, "mgl warning: setting vertical sync not supported\n"); - } - } - - if(result != 0) - fprintf(stderr, "mgl warning: setting vertical sync failed\n"); - - return result == 0; -} - -static int32_t egl_get_config_attrib(mgl_x11_window_egl *egl, EGLConfig ecfg, int32_t attribute_name) { - mgl_context *context = mgl_get_context(); - int32_t value = 0; - context->gl.eglGetConfigAttrib(egl->egl_display, ecfg, attribute_name, &value); - return value; -} - -static bool egl_context_choose(mgl_context *context, mgl_x11_window_egl *egl, bool alpha) { - egl->configs = NULL; - egl->ecfg = NULL; - egl->visual_info = NULL; - - int32_t num_configs = 0; - context->gl.eglGetConfigs(egl->egl_display, NULL, 0, &num_configs); - if(num_configs == 0) { - fprintf(stderr, "mgl error: no configs found\n"); - return false; - } - - egl->configs = (EGLConfig*)calloc(num_configs, sizeof(EGLConfig)); - if(!egl->configs) { - fprintf(stderr, "mgl error: failed to allocate %d configs\n", (int)num_configs); - return false; - } - - context->gl.eglGetConfigs(egl->egl_display, egl->configs, num_configs, &num_configs); - for(int i = 0; i < num_configs; i++) { - egl->ecfg = egl->configs[i]; - - if(egl_get_config_attrib(egl, egl->ecfg, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) - continue; - - if(!(egl_get_config_attrib(egl, egl->ecfg, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) - continue; - - if(egl_get_config_attrib(egl, egl->ecfg, EGL_RED_SIZE) != 8) - continue; - - if(egl_get_config_attrib(egl, egl->ecfg, EGL_GREEN_SIZE) != 8) - continue; - - if(egl_get_config_attrib(egl, egl->ecfg, EGL_BLUE_SIZE) != 8) - continue; - - if(egl_get_config_attrib(egl, egl->ecfg, EGL_ALPHA_SIZE) != (alpha ? 8 : 0)) - continue; - - XVisualInfo vi = {0}; - vi.visualid = egl_get_config_attrib(egl, egl->ecfg, EGL_NATIVE_VISUAL_ID); - if(!vi.visualid) - continue; - - int vis_count = 0; - egl->visual_info = XGetVisualInfo(context->connection, VisualIDMask, &vi, &vis_count); - if(!egl->visual_info) - continue; - - if(xvisual_match_alpha(context->connection, egl->visual_info, alpha)) { - break; - } else { - XFree(egl->visual_info); - egl->visual_info = NULL; - egl->ecfg = NULL; - } - } - - if(!egl->visual_info) { - if(egl->configs) { - free(egl->configs); - egl->configs = NULL; - } - egl->ecfg = NULL; - - fprintf(stderr, "mgl error: no appropriate visual found\n"); - return false; - } - - return true; -} - -static void mgl_x11_window_egl_deinit(mgl_x11_window_egl *self) { - mgl_context *context = mgl_get_context(); - - if(self->visual_info) { - XFree(self->visual_info); - self->visual_info = NULL; - } - - if(self->configs) { - free(self->configs); - self->configs = NULL; - } - - if(self->egl_context) { - context->gl.eglMakeCurrent(self->egl_display, NULL, NULL, NULL); - context->gl.eglDestroyContext(self->egl_display, self->egl_context); - self->egl_context = NULL; - } - - if(self->egl_surface) { - context->gl.eglDestroySurface(self->egl_display, self->egl_surface); - self->egl_surface = NULL; - } - - if(self->egl_display) { - context->gl.eglTerminate(self->egl_display); - self->egl_display = NULL; - } - - if(self->visual_info) { - XFree(self->visual_info); - self->visual_info = NULL; - } -} - -static bool mgl_x11_window_egl_init(mgl_x11_window_egl *self, bool alpha) { - mgl_context *context = mgl_get_context(); - memset(self, 0, sizeof(*self)); - - const int32_t ctxattr[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE, EGL_NONE - }; - - context->gl.eglBindAPI(EGL_OPENGL_API); - - self->egl_display = context->gl.eglGetDisplay((EGLNativeDisplayType)context->connection); - if(!self->egl_display) { - fprintf(stderr, "mgl error: mgl_x11_window_egl_init failed: eglGetDisplay failed\n"); - mgl_x11_window_egl_deinit(self); - return false; - } - - if(!context->gl.eglInitialize(self->egl_display, NULL, NULL)) { - fprintf(stderr, "mgl error: mgl_x11_window_egl_init failed: eglInitialize failed\n"); - mgl_x11_window_egl_deinit(self); - return false; - } - - if(!egl_context_choose(context, self, alpha)) { - mgl_x11_window_egl_deinit(self); - return false; - } - - self->egl_context = context->gl.eglCreateContext(self->egl_display, self->ecfg, NULL, ctxattr); - if(!self->egl_context) { - fprintf(stderr, "mgl error: mgl_x11_window_egl_init failed: failed to create egl context\n"); - mgl_x11_window_egl_deinit(self); - return false; - } - - return true; -} - -static bool mgl_x11_window_egl_make_context_current(mgl_x11_window_egl *self, Window window) { - (void)window; - mgl_context *context = mgl_get_context(); - - if(!self->egl_surface) { - self->egl_surface = context->gl.eglCreateWindowSurface(self->egl_display, self->ecfg, (EGLNativeWindowType)window, NULL); - if(!self->egl_surface) { - fprintf(stderr, "mgl error: mgl_x11_window_egl_make_context_current: failed to create window surface\n"); - return false; - } - } - - return context->gl.eglMakeCurrent(self->egl_display, self->egl_surface, self->egl_surface, self->egl_context) != 0; -} - -static void mgl_x11_window_egl_swap_buffers(mgl_x11_window_egl *self, Window window) { - (void)window; - mgl_context *context = mgl_get_context(); - context->gl.eglSwapBuffers(self->egl_display, self->egl_surface); -} - -static XVisualInfo* mgl_x11_window_egl_get_xvisual_info(mgl_x11_window_egl *self) { - return self->visual_info; -} - -static bool mgl_x11_window_egl_set_swap_interval(mgl_x11_window_egl *self, Window window, int enabled) { - (void)window; - mgl_context *context = mgl_get_context(); - return context->gl.eglSwapInterval(self->egl_display, enabled) == 1; -} - -static void mgl_x11_window_clear_monitors(mgl_window *self) { +static void mgl_window_x11_clear_monitors(mgl_window *self) { for(int i = 0; i < self->num_monitors; ++i) { mgl_monitor *monitor = &self->monitors[i]; if(monitor->name) { @@ -452,45 +116,15 @@ static void mgl_x11_window_clear_monitors(mgl_window *self) { self->num_monitors = 0; } -static bool mgl_x11_window_make_gl_context_current(mgl_x11_window *self, Window window) { - switch(self->render_api) { - case MGL_RENDER_API_GLX: - return mgl_x11_window_glx_make_context_current(&self->glx, window); - case MGL_RENDER_API_EGL: - return mgl_x11_window_egl_make_context_current(&self->egl, window); - } - return false; -} - -static XVisualInfo* mgl_x11_window_get_xvisual_info(mgl_x11_window *self) { - switch(self->render_api) { - case MGL_RENDER_API_GLX: - return mgl_x11_window_glx_get_xvisual_info(&self->glx); - case MGL_RENDER_API_EGL: - return mgl_x11_window_egl_get_xvisual_info(&self->egl); - } - return NULL; -} - -static bool mgl_x11_window_set_swap_interval(mgl_x11_window *self, Window window, int enabled) { - switch(self->render_api) { - case MGL_RENDER_API_GLX: - return mgl_x11_window_glx_set_swap_interval(&self->glx, window, enabled); - case MGL_RENDER_API_EGL: - return mgl_x11_window_egl_set_swap_interval(&self->egl, window, enabled); - } - return false; -} - -static bool mgl_x11_window_append_event(mgl_x11_window *self, const mgl_event *event) { +static bool mgl_window_x11_append_event(mgl_window_x11 *self, const mgl_event *event) { return x11_events_circular_buffer_append(&self->events, event); } -static bool mgl_x11_window_pop_event(mgl_x11_window *self, mgl_event *event) { +static bool mgl_window_x11_pop_event(mgl_window_x11 *self, mgl_event *event) { return x11_events_circular_buffer_pop(&self->events, event); } -static mgl_monitor* mgl_x11_window_add_monitor(mgl_window *self, RROutput output_id, RRCrtc crtc_id, const char *name, mgl_vec2i pos, mgl_vec2i size, int refresh_rate) { +static mgl_monitor* mgl_window_x11_add_monitor(mgl_window *self, RROutput output_id, RRCrtc crtc_id, const char *name, mgl_vec2i pos, mgl_vec2i size, int refresh_rate) { if(self->num_monitors == MGL_MAX_MONITORS) return NULL; @@ -508,7 +142,7 @@ static mgl_monitor* mgl_x11_window_add_monitor(mgl_window *self, RROutput output return monitor; } -static mgl_monitor* mgl_x11_window_get_monitor_by_id(mgl_window *self, RROutput output_id) { +static mgl_monitor* mgl_window_x11_get_monitor_by_id(mgl_window *self, RROutput output_id) { for(int i = 0; i < self->num_monitors; ++i) { mgl_monitor *monitor = &self->monitors[i]; if(monitor->id == (int)output_id) @@ -517,7 +151,7 @@ static mgl_monitor* mgl_x11_window_get_monitor_by_id(mgl_window *self, RROutput return NULL; } -static mgl_monitor* mgl_x11_window_get_monitor_by_crtc_id(mgl_window *self, RRCrtc crtc_id) { +static mgl_monitor* mgl_window_x11_get_monitor_by_crtc_id(mgl_window *self, RRCrtc crtc_id) { for(int i = 0; i < self->num_monitors; ++i) { mgl_monitor *monitor = &self->monitors[i]; if(monitor->crtc_id == (int)crtc_id) @@ -526,7 +160,7 @@ static mgl_monitor* mgl_x11_window_get_monitor_by_crtc_id(mgl_window *self, RRCr return NULL; } -static bool mgl_x11_window_remove_monitor(mgl_window *self, RROutput output_id, mgl_event *event) { +static bool mgl_window_x11_remove_monitor(mgl_window *self, RROutput output_id, mgl_event *event) { int index_to_remove = -1; for(int i = 0; i < self->num_monitors; ++i) { mgl_monitor *monitor = &self->monitors[i]; @@ -582,7 +216,7 @@ static const XRRModeInfo* get_mode_info(const XRRScreenResources *sr, RRMode id) return NULL; } -static void mgl_x11_window_for_each_active_monitor_output(mgl_window *self, mgl_active_monitor_callback callback, void *userdata) { +static void mgl_window_x11_for_each_active_monitor_output(mgl_window *self, mgl_active_monitor_callback callback, void *userdata) { (void)self; mgl_context *context = mgl_get_context(); XRRScreenResources *screen_res = XRRGetScreenResources(context->connection, DefaultRootWindow(context->connection)); @@ -619,19 +253,12 @@ static void mgl_x11_window_for_each_active_monitor_output(mgl_window *self, mgl_ XRRFreeScreenResources(screen_res); } -static void monitor_callback_add_to_mgl_x11_window(const mgl_monitor *monitor, void *userdata) { +static void monitor_callback_add_to_mgl_window_x11(const mgl_monitor *monitor, void *userdata) { mgl_window *mgl_window = userdata; - mgl_x11_window_add_monitor(mgl_window, monitor->id, monitor->crtc_id, monitor->name, monitor->pos, monitor->size, monitor->refresh_rate); + mgl_window_x11_add_monitor(mgl_window, monitor->id, monitor->crtc_id, monitor->name, monitor->pos, monitor->size, monitor->refresh_rate); } -/* TODO: Use gl OML present for other platforms than nvidia? nvidia doesn't support present yet */ - -/* TODO: check for glx swap control extension string (GLX_EXT_swap_control, etc) */ -static void set_vertical_sync_enabled(mgl_x11_window *self, int enabled) { - mgl_x11_window_set_swap_interval(self, self->window, enabled); -} - -static void mgl_x11_window_set_frame_time_limit_monitor(mgl_window *self) { +static void mgl_window_x11_set_frame_time_limit_monitor(mgl_window *self) { int monitor_refresh_rate = 0; mgl_vec2i window_center = (mgl_vec2i) { self->pos.x + self->size.x / 2, self->pos.y + self->size.y / 2 }; for(int i = 0; i < self->num_monitors; ++i) { @@ -653,13 +280,13 @@ static void mgl_x11_window_set_frame_time_limit_monitor(mgl_window *self) { self->frame_time_limit_monitor = 1.0 / (double)monitor_refresh_rate; } -static void mgl_x11_window_on_move(mgl_window *self, int x, int y) { +static void mgl_window_x11_on_move(mgl_window *self, int x, int y) { self->pos.x = x; self->pos.y = y; - mgl_x11_window_set_frame_time_limit_monitor(self); + mgl_window_x11_set_frame_time_limit_monitor(self); } -static void mgl_x11_window_on_resize(mgl_window *self, int width, int height) { +static void mgl_window_x11_on_resize(mgl_window *self, int width, int height) { self->size.x = width; self->size.y = height; @@ -677,7 +304,7 @@ static unsigned long mgl_color_to_x11_pixel(mgl_color color) { } static void mgl_set_window_type(mgl_window *self, mgl_window_type window_type) { - mgl_x11_window *impl = self->impl; + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); switch(window_type) { case MGL_WINDOW_TYPE_NORMAL: { @@ -714,8 +341,8 @@ typedef struct { #define MWM_DECOR_NONE 0 #define MWM_DECOR_ALL 1 -static void mgl_x11_window_set_decorations_visible(mgl_window *self, bool visible) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_decorations_visible(mgl_window *self, bool visible) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); MotifHints motif_hints = {0}; @@ -725,8 +352,8 @@ static void mgl_x11_window_set_decorations_visible(mgl_window *self, bool visibl XChangeProperty(context->connection, impl->window, impl->motif_wm_hints_atom, impl->motif_wm_hints_atom, 32, PropModeReplace, (unsigned char*)&motif_hints, sizeof(motif_hints) / sizeof(long)); } -static void mgl_x11_window_set_title(mgl_window *self, const char *title) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_title(mgl_window *self, const char *title) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); XStoreName(context->connection, impl->window, title); @@ -734,8 +361,8 @@ static void mgl_x11_window_set_title(mgl_window *self, const char *title) { XFlush(context->connection); } -static void mgl_x11_window_set_size_limits(mgl_window *self, mgl_vec2i minimum, mgl_vec2i maximum) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_size_limits(mgl_window *self, mgl_vec2i minimum, mgl_vec2i maximum) { + mgl_window_x11 *impl = self->impl; XSizeHints *size_hints = XAllocSizeHints(); if(size_hints) { size_hints->width = self->size.x; @@ -762,7 +389,7 @@ static void mgl_x11_window_set_size_limits(mgl_window *self, mgl_vec2i minimum, } static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_window_create_params *params, Window existing_window) { - mgl_x11_window *impl = self->impl; + mgl_window_x11 *impl = self->impl; impl->window = 0; impl->clipboard_string = NULL; impl->clipboard_size = 0; @@ -777,14 +404,14 @@ static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_ mgl_vec2i window_pos = params ? params->position : (mgl_vec2i){ 0, 0 }; mgl_context *context = mgl_get_context(); - Window parent_window = params ? params->parent_window : None; + Window parent_window = params ? (Window)params->parent_window : None; if(parent_window == 0) parent_window = DefaultRootWindow(context->connection); - XVisualInfo *visual_info = mgl_x11_window_get_xvisual_info(impl); + XVisualInfo *visual_info = mgl_graphics_get_xvisual_info(&impl->graphics); impl->color_map = XCreateColormap(context->connection, DefaultRootWindow(context->connection), visual_info->visual, AllocNone); if(!impl->color_map) { - fprintf(stderr, "mgl error: XCreateColormap failed\n"); + fprintf(stderr, "mgl error: mgl_x11_setup_window: XCreateColormap failed\n"); return false; } @@ -804,7 +431,7 @@ static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_ if(existing_window) { if(!XChangeWindowAttributes(context->connection, existing_window, CWColormap | CWEventMask | CWOverrideRedirect | CWBorderPixel | CWBackPixel | CWBitGravity, &window_attr)) { - fprintf(stderr, "mgl error: XChangeWindowAttributes failed\n"); + fprintf(stderr, "mgl error: mgl_x11_setup_window: XChangeWindowAttributes failed\n"); return false; } @@ -815,7 +442,7 @@ static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_ impl->window = existing_window; if(params && params->hide_decorations) { - mgl_x11_window_set_decorations_visible(self, false); + mgl_window_x11_set_decorations_visible(self, false); } if(hide_window) @@ -826,21 +453,21 @@ static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_ visual_info->depth, InputOutput, visual_info->visual, CWColormap | CWEventMask | CWOverrideRedirect | CWBorderPixel | CWBackPixel | CWBitGravity, &window_attr); if(!impl->window) { - fprintf(stderr, "mgl error: XCreateWindow failed\n"); + fprintf(stderr, "mgl error: mgl_x11_setup_window: XCreateWindow failed\n"); return false; } if(params && params->hide_decorations) { - mgl_x11_window_set_decorations_visible(self, false); + mgl_window_x11_set_decorations_visible(self, false); } - mgl_x11_window_set_title(self, title); + mgl_window_x11_set_title(self, title); if(!hide_window) XMapWindow(context->connection, impl->window); } if(params) - mgl_x11_window_set_size_limits(self, params->min_size, params->max_size); + mgl_window_x11_set_size_limits(self, params->min_size, params->max_size); /* TODO: Call XGetWMProtocols and add wm_delete_window_atom on top, to not overwrite existing wm protocol atoms */ Atom wm_protocol_atoms[2] = { @@ -871,7 +498,7 @@ static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_ } if(params && params->transient_for_window) { - XSetTransientForHint(context->connection, impl->window, params->transient_for_window); + XSetTransientForHint(context->connection, impl->window, (Window)params->transient_for_window); } /* TODO: Move this to above XMapWindow? */ @@ -880,13 +507,13 @@ static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_ XFlush(context->connection); - if(!mgl_x11_window_make_gl_context_current(impl, impl->window)) { - fprintf(stderr, "mgl error: failed to make opengl context current!\n"); + if(!mgl_graphics_make_context_current(&impl->graphics, (mgl_window_handle)impl->window)) { + fprintf(stderr, "mgl error: mgl_x11_setup_window: failed to make window context current\n"); return false; } self->vsync_enabled = true; - set_vertical_sync_enabled(impl, self->vsync_enabled ? 1 : 0); + mgl_graphics_set_swap_interval(&impl->graphics, (mgl_window_handle)impl->window, self->vsync_enabled); context->gl.glEnable(GL_TEXTURE_2D); context->gl.glEnable(GL_BLEND); @@ -905,22 +532,22 @@ static bool mgl_x11_setup_window(mgl_window *self, const char *title, const mgl_ impl->xim = XOpenIM(context->connection, NULL, NULL, NULL); if(!impl->xim) { - fprintf(stderr, "mgl error: XOpenIM failed\n"); + fprintf(stderr, "mgl error: mgl_x11_setup_window: XOpenIM failed\n"); return false; } impl->xic = XCreateIC(impl->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, impl->window, NULL); if(!impl->xic) { - fprintf(stderr, "mgl error: XCreateIC failed\n"); + fprintf(stderr, "mgl error: mgl_x11_setup_window: XCreateIC failed\n"); return false; } // TODO: This should be done once and monitor events should be done once, no matter how many windows you have - mgl_x11_window_clear_monitors(self); - mgl_x11_window_for_each_active_monitor_output(self, monitor_callback_add_to_mgl_x11_window, self); + mgl_window_x11_clear_monitors(self); + mgl_window_x11_for_each_active_monitor_output(self, monitor_callback_add_to_mgl_window_x11, self); - mgl_x11_window_on_resize(self, self->size.x, self->size.y); - mgl_x11_window_on_move(self, window_pos.x, window_pos.y); + mgl_window_x11_on_resize(self, self->size.x, self->size.y); + mgl_window_x11_on_move(self, window_pos.x, window_pos.y); self->open = true; self->focused = false; /* TODO: Check if we need to call XGetInputFocus for this, or just wait for focus event */ @@ -1036,7 +663,7 @@ static void mgl_window_handle_key_event(XKeyEvent *xkey, mgl_event *event, mgl_c event->key.system = ((xkey->state & Mod4Mask) != 0); } -static void mgl_window_handle_text_event(mgl_x11_window *self, XEvent *xev) { +static void mgl_window_handle_text_event(mgl_window_x11 *self, XEvent *xev) { /* Ignore silent keys */ if(XFilterEvent(xev, None)) return; @@ -1068,7 +695,7 @@ static void mgl_window_handle_text_event(mgl_x11_window *self, XEvent *xev) { memcpy(text_event.text.str, &buf[i], clen); text_event.text.str[clen] = '\0'; /* Text may contain multiple codepoints so they need to separated into multiple events */ - if(!mgl_x11_window_append_event(self, &text_event)) + if(!mgl_window_x11_append_event(self, &text_event)) break; i += clen; @@ -1084,7 +711,7 @@ static bool mgl_on_monitor_added(Display *display, mgl_window *mgl_window, XRROu if(!rr_output_change_event->mode) return false; - if(mgl_x11_window_get_monitor_by_id(mgl_window, output_id)) + if(mgl_window_x11_get_monitor_by_id(mgl_window, output_id)) return false; if(out_info->nameLen >= (int)sizeof(display_name)) @@ -1101,7 +728,7 @@ static bool mgl_on_monitor_added(Display *display, mgl_window *mgl_window, XRROu memcpy(display_name, out_info->name, out_info->nameLen); display_name[out_info->nameLen] = '\0'; - monitor = mgl_x11_window_add_monitor(mgl_window, output_id, out_info->crtc, display_name, + monitor = mgl_window_x11_add_monitor(mgl_window, output_id, out_info->crtc, display_name, (mgl_vec2i){ .x = crt_info->x, .y = crt_info->y }, (mgl_vec2i){ .x = (int)crt_info->width, .y = (int)crt_info->height }, monitor_info_get_framerate(mode_info)); @@ -1139,7 +766,7 @@ static bool mgl_on_monitor_state_changed(Display *display, mgl_window *mgl_windo if(out_info && out_info->crtc && out_info->connection == RR_Connected) { state_changed = mgl_on_monitor_added(display, mgl_window, rr_output_change_event, screen_res, rr_output_change_event->output, out_info, event); } else { - state_changed = mgl_x11_window_remove_monitor(mgl_window, rr_output_change_event->output, event); + state_changed = mgl_window_x11_remove_monitor(mgl_window, rr_output_change_event->output, event); } if(out_info) @@ -1153,7 +780,7 @@ static bool mgl_on_monitor_property_changed(Display *display, mgl_window *mgl_wi if(!rr_crtc_change_event->crtc) return false; - mgl_monitor *monitor = mgl_x11_window_get_monitor_by_crtc_id(mgl_window, rr_crtc_change_event->crtc); + mgl_monitor *monitor = mgl_window_x11_get_monitor_by_crtc_id(mgl_window, rr_crtc_change_event->crtc); if(!monitor) return false; @@ -1195,8 +822,8 @@ static bool mgl_on_rr_notify(mgl_context *context, mgl_window *mgl_window, XEven return false; } -static void mgl_x11_window_on_receive_event(mgl_window *self, XEvent *xev, mgl_event *event, mgl_context *context, bool injected) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_on_receive_event(mgl_window *self, XEvent *xev, mgl_event *event, mgl_context *context, bool injected) { + mgl_window_x11 *impl = self->impl; switch(xev->type - context->randr_event_base) { case RRScreenChangeNotify: { XRRUpdateConfiguration(xev); @@ -1207,7 +834,7 @@ static void mgl_x11_window_on_receive_event(mgl_window *self, XEvent *xev, mgl_e XRRNotifyEvent *rr_event = (XRRNotifyEvent*)xev; if(mgl_on_rr_notify(context, self, xev, rr_event->subtype, event)) { // TODO: - //impl->num_monitors = mgl_x11_window->num_monitors; + //impl->num_monitors = mgl_window_x11->num_monitors; return; } @@ -1292,11 +919,11 @@ static void mgl_x11_window_on_receive_event(mgl_window *self, XEvent *xev, mgl_e while(XCheckTypedWindowEvent(context->connection, impl->window, ConfigureNotify, xev)) {} if(xev->xconfigure.x != self->pos.x || xev->xconfigure.y != self->pos.y) { - mgl_x11_window_on_move(self, xev->xconfigure.x, xev->xconfigure.y); + mgl_window_x11_on_move(self, xev->xconfigure.x, xev->xconfigure.y); } if(xev->xconfigure.width != self->size.x || xev->xconfigure.height != self->size.y) { - mgl_x11_window_on_resize(self, xev->xconfigure.width, xev->xconfigure.height); + mgl_window_x11_on_resize(self, xev->xconfigure.width, xev->xconfigure.height); event->type = MGL_EVENT_RESIZED; event->size.width = self->size.x; @@ -1415,18 +1042,18 @@ static void mgl_x11_window_on_receive_event(mgl_window *self, XEvent *xev, mgl_e } } -static bool mgl_x11_window_poll_event(mgl_window *self, mgl_event *event) { - mgl_x11_window *impl = self->impl; +static bool mgl_window_x11_poll_event(mgl_window *self, mgl_event *event) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); Display *display = context->connection; - if(mgl_x11_window_pop_event(impl, event)) + if(mgl_window_x11_pop_event(impl, event)) return true; if(XPending(display)) { XNextEvent(display, &impl->xev); if(impl->xev.xany.window == impl->window || impl->xev.type == ClientMessage || impl->xev.type == MappingNotify || impl->xev.type - context->randr_event_base >= 0) - mgl_x11_window_on_receive_event(self, &impl->xev, event, context, false); + mgl_window_x11_on_receive_event(self, &impl->xev, event, context, false); else event->type = MGL_EVENT_UNKNOWN; return true; @@ -1435,29 +1062,20 @@ static bool mgl_x11_window_poll_event(mgl_window *self, mgl_event *event) { } } -static bool mgl_x11_window_inject_x11_event(mgl_window *self, XEvent *xev, mgl_event *event) { +static bool mgl_window_x11_inject_x11_event(mgl_window *self, XEvent *xev, mgl_event *event) { mgl_context *context = mgl_get_context(); event->type = MGL_EVENT_UNKNOWN; - mgl_x11_window_on_receive_event(self, xev, event, context, true); + mgl_window_x11_on_receive_event(self, xev, event, context, true); return event->type != MGL_EVENT_UNKNOWN; } -static void mgl_x11_window_swap_buffers(mgl_window *self) { - mgl_x11_window *impl = self->impl; - switch(impl->render_api) { - case MGL_RENDER_API_GLX: { - mgl_x11_window_glx_swap_buffers(&impl->glx, impl->window); - break; - } - case MGL_RENDER_API_EGL: { - mgl_x11_window_egl_swap_buffers(&impl->egl, impl->window); - break; - } - } +static void mgl_window_x11_swap_buffers(mgl_window *self) { + mgl_window_x11 *impl = self->impl; + mgl_graphics_swap_buffers(&impl->graphics, (mgl_window_handle)impl->window); } -static void mgl_x11_window_set_visible(mgl_window *self, bool visible) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_visible(mgl_window *self, bool visible) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); if(visible) XMapWindow(context->connection, impl->window); @@ -1467,7 +1085,7 @@ static void mgl_x11_window_set_visible(mgl_window *self, bool visible) { } /* TODO: Track keys with events instead, but somehow handle window focus lost */ -static bool mgl_x11_window_is_key_pressed(const mgl_window *self, mgl_key key) { +static bool mgl_window_x11_is_key_pressed(const mgl_window *self, mgl_key key) { (void)self; if(key < 0 || key >= __MGL_NUM_KEYS__) return false; @@ -1488,7 +1106,7 @@ static bool mgl_x11_window_is_key_pressed(const mgl_window *self, mgl_key key) { } /* TODO: Track keys with events instead, but somehow handle window focus lost */ -static bool mgl_x11_window_is_mouse_button_pressed(const mgl_window *self, mgl_mouse_button button) { +static bool mgl_window_x11_is_mouse_button_pressed(const mgl_window *self, mgl_mouse_button button) { (void)self; if(button < 0 || button >= __MGL_NUM_MOUSE_BUTTONS__) return false; @@ -1512,13 +1130,13 @@ static bool mgl_x11_window_is_mouse_button_pressed(const mgl_window *self, mgl_m return false; } -static mgl_window_handle mgl_x11_window_get_system_handle(const mgl_window *self) { - const mgl_x11_window *impl = self->impl; - return impl->window; +static mgl_window_handle mgl_window_x11_get_system_handle(const mgl_window *self) { + const mgl_window_x11 *impl = self->impl; + return (mgl_window_handle)impl->window; } -static void mgl_x11_window_close(mgl_window *self) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_close(mgl_window *self) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); if(impl->window) { XDestroyWindow(context->connection, impl->window); @@ -1528,24 +1146,24 @@ static void mgl_x11_window_close(mgl_window *self) { self->open = false; } -static void mgl_x11_window_set_cursor_visible(mgl_window *self, bool visible) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_cursor_visible(mgl_window *self, bool visible) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); XDefineCursor(context->connection, impl->window, visible ? impl->default_cursor : impl->invisible_cursor); } -static void mgl_x11_window_set_vsync_enabled(mgl_window *self, bool enabled) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_vsync_enabled(mgl_window *self, bool enabled) { + mgl_window_x11 *impl = self->impl; self->vsync_enabled = enabled; - set_vertical_sync_enabled(impl, self->vsync_enabled ? 1 : 0); + mgl_graphics_set_swap_interval(&impl->graphics, (mgl_window_handle)impl->window, self->vsync_enabled); } -static bool mgl_x11_window_is_vsync_enabled(const mgl_window *self) { +static bool mgl_window_x11_is_vsync_enabled(const mgl_window *self) { return self->vsync_enabled; } -static void mgl_x11_window_set_fullscreen(mgl_window *self, bool fullscreen) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_fullscreen(mgl_window *self, bool fullscreen) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); XEvent xev; @@ -1567,8 +1185,8 @@ static void mgl_x11_window_set_fullscreen(mgl_window *self, bool fullscreen) { XFlush(context->connection); } -static bool mgl_x11_window_is_fullscreen(const mgl_window *self) { - mgl_x11_window *impl = self->impl; +static bool mgl_window_x11_is_fullscreen(const mgl_window *self) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); bool is_fullscreen = false; @@ -1584,14 +1202,14 @@ static bool mgl_x11_window_is_fullscreen(const mgl_window *self) { return is_fullscreen; } -static void mgl_x11_window_set_position(mgl_window *self, mgl_vec2i position) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_position(mgl_window *self, mgl_vec2i position) { + mgl_window_x11 *impl = self->impl; XMoveWindow(mgl_get_context()->connection, impl->window, position.x, position.y); XFlush(mgl_get_context()->connection); } -static void mgl_x11_window_set_size(mgl_window *self, mgl_vec2i size) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_size(mgl_window *self, mgl_vec2i size) { + mgl_window_x11 *impl = self->impl; if(size.x < 0) size.x = 0; if(size.y < 0) @@ -1600,8 +1218,8 @@ static void mgl_x11_window_set_size(mgl_window *self, mgl_vec2i size) { XFlush(mgl_get_context()->connection); } -static void mgl_x11_window_set_clipboard(mgl_window *self, const char *str, size_t size) { - mgl_x11_window *impl = self->impl; +static void mgl_window_x11_set_clipboard(mgl_window *self, const char *str, size_t size) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); if(impl->clipboard_string) { @@ -1615,7 +1233,7 @@ static void mgl_x11_window_set_clipboard(mgl_window *self, const char *str, size /* Check if setting the selection owner was successful */ if(XGetSelectionOwner(context->connection, impl->clipboard_atom) != impl->window) { - fprintf(stderr, "mgl error: mgl_x11_window_set_clipboard failed\n"); + fprintf(stderr, "mgl error: mgl_window_x11_set_clipboard failed\n"); return; } @@ -1639,22 +1257,22 @@ static Atom find_matching_atom(const Atom *supported_atoms, size_t num_supported return None; } -static mgl_clipboard_type atom_type_to_supported_clipboard_type(mgl_x11_window *mgl_x11_window, Atom type, int format) { - if((type == mgl_x11_window->utf8_string_atom || type == XA_STRING || type == mgl_x11_window->text_atom) && format == 8) { +static mgl_clipboard_type atom_type_to_supported_clipboard_type(mgl_window_x11 *mgl_window_x11, Atom type, int format) { + if((type == mgl_window_x11->utf8_string_atom || type == XA_STRING || type == mgl_window_x11->text_atom) && format == 8) { return MGL_CLIPBOARD_TYPE_STRING; - } else if(type == mgl_x11_window->image_png_atom && format == 8){ + } else if(type == mgl_window_x11->image_png_atom && format == 8){ return MGL_CLIPBOARD_TYPE_IMAGE_PNG; - } else if((type == mgl_x11_window->image_jpg_atom || type == mgl_x11_window->image_jpeg_atom) && format == 8){ + } else if((type == mgl_window_x11->image_jpg_atom || type == mgl_window_x11->image_jpeg_atom) && format == 8){ return MGL_CLIPBOARD_TYPE_IMAGE_JPG; - } else if(type == mgl_x11_window->image_gif_atom && format == 8){ + } else if(type == mgl_window_x11->image_gif_atom && format == 8){ return MGL_CLIPBOARD_TYPE_IMAGE_GIF; } else { return -1; } } -static bool mgl_x11_window_get_clipboard(mgl_window *self, mgl_clipboard_callback callback, void *userdata, uint32_t clipboard_types) { - mgl_x11_window *impl = self->impl; +static bool mgl_window_x11_get_clipboard(mgl_window *self, mgl_clipboard_callback callback, void *userdata, uint32_t clipboard_types) { + mgl_window_x11 *impl = self->impl; assert(callback); mgl_context *context = mgl_get_context(); @@ -1746,7 +1364,7 @@ static bool mgl_x11_window_get_clipboard(mgl_window *self, mgl_clipboard_callbac bool got_data = false; long chunk_size = 65536; - //XDeleteProperty(context->connection, self->window, mgl_x11_window->incr_atom); + //XDeleteProperty(context->connection, self->window, mgl_window_x11->incr_atom); //XFlush(context->connection); while(mgl_clock_get_elapsed_time_seconds(&timeout_timer) < timeout_seconds) { @@ -1847,7 +1465,7 @@ static bool clipboard_copy_string_callback(const unsigned char *data, size_t siz return true; } -static bool mgl_x11_window_get_clipboard_string(mgl_window *self, char **str, size_t *size) { +static bool mgl_window_x11_get_clipboard_string(mgl_window *self, char **str, size_t *size) { assert(str); assert(size); @@ -1857,7 +1475,7 @@ static bool mgl_x11_window_get_clipboard_string(mgl_window *self, char **str, si ClipboardStringCallbackData callback_data; callback_data.str = str; callback_data.size = size; - const bool success = mgl_x11_window_get_clipboard(self, clipboard_copy_string_callback, &callback_data, MGL_CLIPBOARD_TYPE_STRING); + const bool success = mgl_window_x11_get_clipboard(self, clipboard_copy_string_callback, &callback_data, MGL_CLIPBOARD_TYPE_STRING); if(!success) { free(*str); *str = NULL; @@ -1866,68 +1484,65 @@ static bool mgl_x11_window_get_clipboard_string(mgl_window *self, char **str, si return success; } -static void mgl_x11_window_set_key_repeat_enabled(mgl_window *self, bool enabled) { +static void mgl_window_x11_set_key_repeat_enabled(mgl_window *self, bool enabled) { self->key_repeat_enabled = enabled; } -static void mgl_x11_window_flush(mgl_window *self) { +static void mgl_window_x11_flush(mgl_window *self) { (void)self; mgl_context *context = mgl_get_context(); XFlush(context->connection); } -static void* mgl_x11_window_get_egl_display(mgl_window *self) { - mgl_x11_window *impl = self->impl; - if(impl->render_api == MGL_RENDER_API_EGL) - return impl->egl.egl_display; +static void* mgl_window_x11_get_egl_display(mgl_window *self) { + mgl_window_x11 *impl = self->impl; + if(impl->graphics.graphics_api == MGL_GRAPHICS_API_EGL) + return mgl_graphics_get_display(&impl->graphics); else return NULL; } -static void* mgl_x11_window_get_egl_context(mgl_window *self) { - mgl_x11_window *impl = self->impl; - if(impl->render_api == MGL_RENDER_API_EGL) - return impl->egl.egl_context; +static void* mgl_window_x11_get_egl_context(mgl_window *self) { + mgl_window_x11 *impl = self->impl; + if(impl->graphics.graphics_api == MGL_GRAPHICS_API_EGL) + return mgl_graphics_get_context(&impl->graphics); else return NULL; } -bool mgl_x11_window_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window) { - mgl_x11_window *impl = calloc(1, sizeof(mgl_x11_window)); +bool mgl_window_x11_init(mgl_window *self, const char *title, const mgl_window_create_params *params, mgl_window_handle existing_window) { + mgl_window_x11 *impl = calloc(1, sizeof(mgl_window_x11)); if(!impl) return false; - *self = (mgl_window) { - .get_system_handle = mgl_x11_window_get_system_handle, - .close = mgl_x11_window_close, - .inject_x11_event = mgl_x11_window_inject_x11_event, - .poll_event = mgl_x11_window_poll_event, - .swap_buffers = mgl_x11_window_swap_buffers, - .set_visible = mgl_x11_window_set_visible, - .is_key_pressed = mgl_x11_window_is_key_pressed, - .is_mouse_button_pressed = mgl_x11_window_is_mouse_button_pressed, - .set_title = mgl_x11_window_set_title, - .set_cursor_visible = mgl_x11_window_set_cursor_visible, - .set_vsync_enabled = mgl_x11_window_set_vsync_enabled, - .is_vsync_enabled = mgl_x11_window_is_vsync_enabled, - .set_fullscreen = mgl_x11_window_set_fullscreen, - .is_fullscreen = mgl_x11_window_is_fullscreen, - .set_position = mgl_x11_window_set_position, - .set_size = mgl_x11_window_set_size, - .set_size_limits = mgl_x11_window_set_size_limits, - .set_clipboard = mgl_x11_window_set_clipboard, - .get_clipboard = mgl_x11_window_get_clipboard, - .get_clipboard_string = mgl_x11_window_get_clipboard_string, - .set_key_repeat_enabled = mgl_x11_window_set_key_repeat_enabled, - .flush = mgl_x11_window_flush, - .get_egl_display = mgl_x11_window_get_egl_display, - .get_egl_context = mgl_x11_window_get_egl_context, - .for_each_active_monitor_output = mgl_x11_window_for_each_active_monitor_output, - - .impl = impl, - }; - - impl->render_api = params ? params->render_api : MGL_RENDER_API_GLX; + self->get_system_handle = mgl_window_x11_get_system_handle; + self->deinit = mgl_window_x11_deinit; + self->close = mgl_window_x11_close; + self->inject_x11_event = mgl_window_x11_inject_x11_event; + self->poll_event = mgl_window_x11_poll_event; + self->swap_buffers = mgl_window_x11_swap_buffers; + self->set_visible = mgl_window_x11_set_visible; + self->is_key_pressed = mgl_window_x11_is_key_pressed; + self->is_mouse_button_pressed = mgl_window_x11_is_mouse_button_pressed; + self->set_title = mgl_window_x11_set_title; + self->set_cursor_visible = mgl_window_x11_set_cursor_visible; + self->set_vsync_enabled = mgl_window_x11_set_vsync_enabled; + self->is_vsync_enabled = mgl_window_x11_is_vsync_enabled; + self->set_fullscreen = mgl_window_x11_set_fullscreen; + self->is_fullscreen = mgl_window_x11_is_fullscreen; + self->set_position = mgl_window_x11_set_position; + self->set_size = mgl_window_x11_set_size; + self->set_size_limits = mgl_window_x11_set_size_limits; + self->set_clipboard = mgl_window_x11_set_clipboard; + self->get_clipboard = mgl_window_x11_get_clipboard; + self->get_clipboard_string = mgl_window_x11_get_clipboard_string; + self->set_key_repeat_enabled = mgl_window_x11_set_key_repeat_enabled; + self->flush = mgl_window_x11_flush; + self->get_egl_display = mgl_window_x11_get_egl_display; + self->get_egl_context = mgl_window_x11_get_egl_context; + self->for_each_active_monitor_output = mgl_window_x11_for_each_active_monitor_output; + + self->impl = impl; /* TODO: Use CLIPBOARD_MANAGER and SAVE_TARGETS to save clipboard in clipboard manager on exit */ @@ -1955,33 +1570,23 @@ bool mgl_x11_window_init(mgl_window *self, const char *title, const mgl_window_c impl->support_alpha = params && params->support_alpha; - switch(impl->render_api) { - case MGL_RENDER_API_GLX: { - if(!mgl_x11_window_glx_init(&impl->glx, impl->support_alpha)) { - mgl_x11_window_deinit(self); - return false; - } - break; - } - case MGL_RENDER_API_EGL: { - if(!mgl_x11_window_egl_init(&impl->egl, impl->support_alpha)) { - mgl_x11_window_deinit(self); - return false; - } - break; - } + const mgl_graphics_api graphics_api = params ? params->graphics_api : MGL_GRAPHICS_API_EGL; + const bool alpha = params && params->support_alpha; + if(!mgl_graphics_init(&impl->graphics, &(mgl_graphics_create_params){ .graphics_api = graphics_api, .alpha = alpha })) { + mgl_window_x11_deinit(self); + return false; } impl->default_cursor = XCreateFontCursor(context->connection, XC_arrow); if(!impl->default_cursor) { - mgl_x11_window_deinit(self); + mgl_window_x11_deinit(self); return false; } const char data[1] = {0}; Pixmap blank_bitmap = XCreateBitmapFromData(context->connection, DefaultRootWindow(context->connection), data, 1, 1); if(!blank_bitmap) { - mgl_x11_window_deinit(self); + mgl_window_x11_deinit(self); return false; } @@ -1989,14 +1594,14 @@ bool mgl_x11_window_init(mgl_window *self, const char *title, const mgl_window_c impl->invisible_cursor = XCreatePixmapCursor(context->connection, blank_bitmap, blank_bitmap, &dummy, &dummy, 0, 0); XFreePixmap(context->connection, blank_bitmap); if(!impl->invisible_cursor) { - mgl_x11_window_deinit(self); + mgl_window_x11_deinit(self); return false; } x11_events_circular_buffer_init(&impl->events); - if(!mgl_x11_setup_window(self, title, params, existing_window)) { - mgl_x11_window_deinit(self); + if(!mgl_x11_setup_window(self, title, params, (Window)existing_window)) { + mgl_window_x11_deinit(self); return false; } @@ -2004,13 +1609,13 @@ bool mgl_x11_window_init(mgl_window *self, const char *title, const mgl_window_c return true; } -void mgl_x11_window_deinit(mgl_window *self) { - mgl_x11_window *impl = self->impl; +void mgl_window_x11_deinit(mgl_window *self) { + mgl_window_x11 *impl = self->impl; mgl_context *context = mgl_get_context(); if(!impl) return; - mgl_x11_window_clear_monitors(self); + mgl_window_x11_clear_monitors(self); if(impl->color_map) { XFreeColormap(context->connection, impl->color_map); @@ -2037,18 +1642,8 @@ void mgl_x11_window_deinit(mgl_window *self) { impl->xim = NULL; } - switch(impl->render_api) { - case MGL_RENDER_API_GLX: { - mgl_x11_window_glx_deinit(&impl->glx); - break; - } - case MGL_RENDER_API_EGL: { - mgl_x11_window_egl_deinit(&impl->egl); - break; - } - } - - mgl_x11_window_close(self); + mgl_graphics_deinit(&impl->graphics); + mgl_window_x11_close(self); if(impl->clipboard_string) { free(impl->clipboard_string); diff --git a/tests/main.c b/tests/main.c index 44e3a27..9e6aaa5 100644 --- a/tests/main.c +++ b/tests/main.c @@ -234,7 +234,7 @@ int main(void) { test_hash_map(); test_utf8(); - if(mgl_init() != 0) + if(mgl_init(MGL_WINDOW_SYSTEM_NATIVE) != 0) return 1; mgl_texture texture; |