aboutsummaryrefslogtreecommitdiff
path: root/src/window/x11/window.c
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-04-18 12:55:00 +0200
committerdec05eba <dec05eba@protonmail.com>2025-04-18 12:55:00 +0200
commit26c56565cc0573ce23eb8d172a6765bce1f657ce (patch)
treec4ceaacd336946acec12af5f2c5c4d2a78370744 /src/window/x11/window.c
parent506c271eafec23bf469caf6c29431191fa885e58 (diff)
Separate glx and egl from window system to prepare for wayland
Diffstat (limited to 'src/window/x11/window.c')
-rw-r--r--src/window/x11/window.c2066
1 files changed, 0 insertions, 2066 deletions
diff --git a/src/window/x11/window.c b/src/window/x11/window.c
deleted file mode 100644
index 59df3bc..0000000
--- a/src/window/x11/window.c
+++ /dev/null
@@ -1,2066 +0,0 @@
-#include "../../../include/mgl/window/x11/window.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>
-#include <limits.h>
-#include <assert.h>
-#include <unistd.h>
-
-/* 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 */
-
-/* Should be in range [2,] */
-#define MAX_STACKED_EVENTS 32
-
-typedef struct {
- mgl_event stack[MAX_STACKED_EVENTS];
- int start;
- int end;
- int size;
-} 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;
- Colormap color_map;
- XIM xim;
- XIC xic;
- Atom clipboard_atom;
- Atom targets_atom;
- Atom text_atom;
- Atom utf8_string_atom;
- Atom image_png_atom;
- Atom image_jpg_atom;
- Atom image_jpeg_atom;
- Atom image_gif_atom;
- Atom incr_atom;
- Atom net_wm_state_atom;
- Atom net_wm_state_fullscreen_atom;
- Atom net_wm_state_above_atom;
- Atom net_wm_name_atom;
- Atom net_wm_window_type_atom;
- Atom net_wm_window_type_normal_atom;
- Atom net_wm_window_type_dialog_atom;
- Atom net_wm_window_type_notification_atom;
- Atom net_wm_window_type_utility;
- Atom motif_wm_hints_atom;
- Cursor default_cursor;
- Cursor invisible_cursor;
- unsigned int prev_keycode_pressed;
- bool key_was_released;
- bool support_alpha;
-
- XEvent xev;
-
- /*
- Used to stack text event on top of key press/release events and other text events.
- For example pressing a key should give the user both key press and text events
- and for IM with multiple characters entered (such as chinese), we want to trigger
- an event for all of them.
- */
- x11_events_circular_buffer events;
-} mgl_x11_window;
-
-static void x11_events_circular_buffer_init(x11_events_circular_buffer *self) {
- self->start = 0;
- self->end = 0;
- self->size = 0;
-}
-
-static bool x11_events_circular_buffer_append(x11_events_circular_buffer *self, const mgl_event *event) {
- if(self->size == MAX_STACKED_EVENTS)
- return false;
-
- self->stack[self->end] = *event;
- self->end = (self->end + 1) % MAX_STACKED_EVENTS;
- ++self->size;
- return true;
-}
-
-static bool x11_events_circular_buffer_pop(x11_events_circular_buffer *self, mgl_event *event) {
- if(self->size == 0)
- return false;
-
- *event = self->stack[self->start];
- self->start = (self->start + 1) % MAX_STACKED_EVENTS;
- --self->size;
- 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) {
- for(int i = 0; i < self->num_monitors; ++i) {
- mgl_monitor *monitor = &self->monitors[i];
- if(monitor->name) {
- free((char*)monitor->name);
- monitor->name = NULL;
- }
- }
- 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) {
- return x11_events_circular_buffer_append(&self->events, event);
-}
-
-static bool mgl_x11_window_pop_event(mgl_x11_window *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) {
- if(self->num_monitors == MGL_MAX_MONITORS)
- return NULL;
-
- mgl_monitor *monitor = &self->monitors[self->num_monitors];
- monitor->id = output_id;
- monitor->crtc_id = crtc_id;
- monitor->name = strdup(name);
- if(!monitor->name)
- return NULL;
- monitor->pos = pos;
- monitor->size = size;
- monitor->refresh_rate = refresh_rate;
- self->num_monitors++;
-
- return monitor;
-}
-
-static mgl_monitor* mgl_x11_window_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)
- return monitor;
- }
- return NULL;
-}
-
-static mgl_monitor* mgl_x11_window_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)
- return monitor;
- }
- return NULL;
-}
-
-static bool mgl_x11_window_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];
- if(monitor->id == (int)output_id) {
- index_to_remove = i;
- break;
- }
- }
-
- if(index_to_remove == -1)
- return false;
-
- mgl_monitor *monitor = &self->monitors[index_to_remove];
- free((char*)monitor->name);
- monitor->name = NULL;
-
- for(int i = index_to_remove + 1; i < self->num_monitors; ++i) {
- self->monitors[i - 1] = self->monitors[i];
- }
- self->num_monitors--;
-
- event->monitor_disconnected.id = output_id;
- event->type = MGL_EVENT_MONITOR_DISCONNECTED;
- return true;
-}
-
-static int round_int(double value) {
- return value + 0.5;
-}
-
-static int monitor_info_get_framerate(const XRRModeInfo *mode_info) {
- double v_total = mode_info->vTotal;
- if(mode_info->modeFlags & RR_DoubleScan) {
- v_total *= 2;
- }
-
- if(mode_info->modeFlags & RR_Interlace) {
- v_total /= 2;
- }
-
- if(mode_info->hTotal > 0 && v_total > 0.0001) {
- return round_int((double)mode_info->dotClock / ((double)mode_info->hTotal * v_total));
- } else {
- return 0;
- }
-}
-
-static const XRRModeInfo* get_mode_info(const XRRScreenResources *sr, RRMode id) {
- for(int i = 0; i < sr->nmode; ++i) {
- if(sr->modes[i].id == id)
- return &sr->modes[i];
- }
- return NULL;
-}
-
-static void mgl_x11_window_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));
- if(!screen_res)
- return;
-
- char display_name[256];
- for(int i = 0; i < screen_res->noutput; ++i) {
- XRROutputInfo *out_info = XRRGetOutputInfo(context->connection, screen_res, screen_res->outputs[i]);
- if(out_info && out_info->crtc && out_info->connection == RR_Connected) {
- XRRCrtcInfo *crt_info = XRRGetCrtcInfo(context->connection, screen_res, out_info->crtc);
- if(crt_info && crt_info->mode) {
- const XRRModeInfo *mode_info = get_mode_info(screen_res, crt_info->mode);
- if(mode_info) {
- snprintf(display_name, sizeof(display_name), "%s", out_info->name);
- mgl_monitor monitor = {
- .id = screen_res->outputs[i],
- .crtc_id = out_info->crtc,
- .name = display_name,
- .pos = { .x = crt_info->x, .y = crt_info->y },
- .size = { .x = (int)crt_info->width, .y = (int)crt_info->height },
- .refresh_rate = monitor_info_get_framerate(mode_info),
- };
- callback(&monitor, userdata);
- }
- }
- if(crt_info)
- XRRFreeCrtcInfo(crt_info);
- }
- if(out_info)
- XRRFreeOutputInfo(out_info);
- }
-
- XRRFreeScreenResources(screen_res);
-}
-
-static void monitor_callback_add_to_mgl_x11_window(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);
-}
-
-/* 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) {
- 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) {
- mgl_monitor *monitor = &self->monitors[i];
- if(window_center.x >= monitor->pos.x && window_center.x <= monitor->pos.x + monitor->size.x
- && window_center.y >= monitor->pos.y && window_center.y <= monitor->pos.y + monitor->size.y)
- {
- monitor_refresh_rate = monitor->refresh_rate;
- break;
- }
- }
-
- if(monitor_refresh_rate == 0 && self->num_monitors > 0)
- monitor_refresh_rate = self->monitors[0].refresh_rate;
-
- if(monitor_refresh_rate == 0)
- monitor_refresh_rate = 60;
-
- 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) {
- self->pos.x = x;
- self->pos.y = y;
- mgl_x11_window_set_frame_time_limit_monitor(self);
-}
-
-static void mgl_x11_window_on_resize(mgl_window *self, int width, int height) {
- self->size.x = width;
- self->size.y = height;
-
- mgl_view view;
- view.position = (mgl_vec2i){ 0, 0 };
- view.size = self->size;
- mgl_window_set_view(self, &view);
- mgl_window_set_scissor(self, &(mgl_scissor){ .position = { 0, 0 }, .size = self->size });
-}
-
-static unsigned long mgl_color_to_x11_pixel(mgl_color color) {
- if(color.a == 0)
- return 0;
- return ((uint32_t)color.a << 24) | (((uint32_t)color.r * color.a / 0xFF) << 16) | (((uint32_t)color.g * color.a / 0xFF) << 8) | ((uint32_t)color.b * color.a / 0xFF);
-}
-
-static void mgl_set_window_type(mgl_window *self, mgl_window_type window_type) {
- mgl_x11_window *impl = self->impl;
- mgl_context *context = mgl_get_context();
- switch(window_type) {
- case MGL_WINDOW_TYPE_NORMAL: {
- XChangeProperty(context->connection, impl->window, impl->net_wm_window_type_atom, XA_ATOM, 32, PropModeReplace, (unsigned char*)&impl->net_wm_window_type_normal_atom, 1L);
- break;
- }
- case MGL_WINDOW_TYPE_DIALOG: {
- XChangeProperty(context->connection, impl->window, impl->net_wm_window_type_atom, XA_ATOM, 32, PropModeReplace, (unsigned char*)&impl->net_wm_window_type_dialog_atom, 1L);
- XChangeProperty(context->connection, impl->window, impl->net_wm_state_atom, XA_ATOM, 32, PropModeReplace, (unsigned char*)&impl->net_wm_state_above_atom, 1L);
- break;
- }
- case MGL_WINDOW_TYPE_NOTIFICATION: {
- const Atom data[2] = {
- impl->net_wm_window_type_notification_atom,
- impl->net_wm_window_type_utility
- };
- XChangeProperty(context->connection, impl->window, impl->net_wm_window_type_atom, XA_ATOM, 32, PropModeReplace, (const unsigned char*)data, 2L);
- XChangeProperty(context->connection, impl->window, impl->net_wm_state_atom, XA_ATOM, 32, PropModeReplace, (unsigned char*)&impl->net_wm_state_above_atom, 1L);
- break;
- }
- }
-}
-
-typedef struct {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long input_mode;
- unsigned long status;
-} MotifHints;
-
-#define MWM_HINTS_DECORATIONS 2
-
-#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;
- mgl_context *context = mgl_get_context();
-
- MotifHints motif_hints = {0};
- motif_hints.flags = MWM_HINTS_DECORATIONS;
- motif_hints.decorations = visible ? MWM_DECOR_ALL : MWM_DECOR_NONE;
-
- 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;
- mgl_context *context = mgl_get_context();
-
- XStoreName(context->connection, impl->window, title);
- XChangeProperty(context->connection, impl->window, impl->net_wm_name_atom, impl->utf8_string_atom, 8, PropModeReplace, (unsigned char*)title, strlen(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;
- XSizeHints *size_hints = XAllocSizeHints();
- if(size_hints) {
- size_hints->width = self->size.x;
- size_hints->min_width = minimum.x;
- size_hints->max_width = maximum.x;
-
- size_hints->height = self->size.y;
- size_hints->min_height = minimum.y;
- size_hints->max_height = maximum.y;
-
- size_hints->flags = PSize;
- if(size_hints->min_width || size_hints->min_height)
- size_hints->flags |= PMinSize;
- if(size_hints->max_width || size_hints->max_height)
- size_hints->flags |= PMaxSize;
-
- mgl_context *context = mgl_get_context();
- XSetWMNormalHints(context->connection, impl->window, size_hints);
- XFree(size_hints);
- XFlush(context->connection);
- } else {
- fprintf(stderr, "mgl warning: failed to set window size hints\n");
- }
-}
-
-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;
- impl->window = 0;
- impl->clipboard_string = NULL;
- impl->clipboard_size = 0;
-
- 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;
-
- 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;
- if(parent_window == 0)
- parent_window = DefaultRootWindow(context->connection);
-
- XVisualInfo *visual_info = mgl_x11_window_get_xvisual_info(impl);
- impl->color_map = XCreateColormap(context->connection, DefaultRootWindow(context->connection), visual_info->visual, AllocNone);
- if(!impl->color_map) {
- fprintf(stderr, "mgl error: XCreateColormap failed\n");
- return false;
- }
-
- XSetWindowAttributes window_attr;
- window_attr.override_redirect = params ? params->override_redirect : false;
- window_attr.colormap = impl->color_map;
- window_attr.background_pixel = mgl_color_to_x11_pixel(params ? params->background_color : (mgl_color){ .r = 0, .g = 0, .b = 0, .a = 255 });
- window_attr.border_pixel = 0;
- window_attr.bit_gravity = NorthWestGravity;
- window_attr.event_mask =
- KeyPressMask | KeyReleaseMask |
- ButtonPressMask | ButtonReleaseMask |
- PointerMotionMask | Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask | ButtonMotionMask |
- StructureNotifyMask | EnterWindowMask | LeaveWindowMask | VisibilityChangeMask | PropertyChangeMask | FocusChangeMask;
-
- const bool hide_window = params ? params->hidden : false;
-
- if(existing_window) {
- if(!XChangeWindowAttributes(context->connection, existing_window, CWColormap | CWEventMask | CWOverrideRedirect | CWBorderPixel | CWBackPixel | CWBitGravity, &window_attr)) {
- fprintf(stderr, "mgl error: XChangeWindowAttributes failed\n");
- return false;
- }
-
- if(params && params->size.x > 0 && params->size.y > 0) {
- XResizeWindow(context->connection, existing_window, params->size.x, params->size.y);
- }
-
- impl->window = existing_window;
-
- if(params && params->hide_decorations) {
- mgl_x11_window_set_decorations_visible(self, false);
- }
-
- if(hide_window)
- XUnmapWindow(context->connection, existing_window);
- } else {
- impl->window = XCreateWindow(context->connection, parent_window, window_pos.x, window_pos.y,
- window_size.x, window_size.y, 0,
- 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");
- return false;
- }
-
- if(params && params->hide_decorations) {
- mgl_x11_window_set_decorations_visible(self, false);
- }
-
- mgl_x11_window_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);
-
- /* TODO: Call XGetWMProtocols and add wm_delete_window_atom on top, to not overwrite existing wm protocol atoms */
- Atom wm_protocol_atoms[2] = {
- context->wm_delete_window_atom,
- context->net_wm_ping_atom
- };
- XSetWMProtocols(context->connection, impl->window, wm_protocol_atoms, 2);
-
- if(context->net_wm_pid_atom) {
- const long pid = getpid();
- XChangeProperty(context->connection, impl->window, context->net_wm_pid_atom, XA_CARDINAL,
- 32, PropModeReplace, (const unsigned char*)&pid, 1);
- }
-
- char host_name[HOST_NAME_MAX];
- if(gethostname(host_name, sizeof(host_name)) == 0) {
- XTextProperty txt_prop;
- txt_prop.value = (unsigned char*)host_name;
- txt_prop.encoding = XA_STRING;
- txt_prop.format = 8;
- txt_prop.nitems = strlen(host_name);
- XSetWMClientMachine(context->connection, impl->window, &txt_prop);
- }
-
- if(params && params->class_name) {
- XClassHint class_hint = { (char*)params->class_name, (char*)params->class_name };
- XSetClassHint(context->connection, impl->window, &class_hint);
- }
-
- if(params && params->transient_for_window) {
- XSetTransientForHint(context->connection, impl->window, params->transient_for_window);
- }
-
- /* TODO: Move this to above XMapWindow? */
- mgl_window_type window_type = params ? params->window_type : MGL_WINDOW_TYPE_NORMAL;
- mgl_set_window_type(self, window_type);
-
- 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");
- return false;
- }
-
- self->vsync_enabled = true;
- set_vertical_sync_enabled(impl, self->vsync_enabled ? 1 : 0);
-
- context->gl.glEnable(GL_TEXTURE_2D);
- context->gl.glEnable(GL_BLEND);
- context->gl.glEnable(GL_SCISSOR_TEST);
- context->gl.glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- context->gl.glEnableClientState(GL_VERTEX_ARRAY);
- context->gl.glEnableClientState(GL_TEXTURE_COORD_ARRAY);
- context->gl.glEnableClientState(GL_COLOR_ARRAY);
- context->gl.glPixelStorei(GL_PACK_ALIGNMENT, 1);
- context->gl.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- Window dummy_w;
- int dummy_i;
- unsigned int dummy_u;
- XQueryPointer(context->connection, impl->window, &dummy_w, &dummy_w, &dummy_i, &dummy_i, &self->cursor_position.x, &self->cursor_position.y, &dummy_u);
-
- impl->xim = XOpenIM(context->connection, NULL, NULL, NULL);
- if(!impl->xim) {
- fprintf(stderr, "mgl error: 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");
- 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_x11_window_on_resize(self, self->size.x, self->size.y);
- mgl_x11_window_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 */
- return true;
-}
-
-/* Returns MGL_KEY_UNKNOWN on no match */
-static mgl_key x11_keysym_to_mgl_key(KeySym key_sym) {
- if(key_sym >= XK_A && key_sym <= XK_Z)
- return MGL_KEY_A + (key_sym - XK_A);
- /* TODO: Check if this ever happens */
- if(key_sym >= XK_a && key_sym <= XK_z)
- return MGL_KEY_A + (key_sym - XK_a);
- if(key_sym >= XK_0 && key_sym <= XK_9)
- return MGL_KEY_NUM0 + (key_sym - XK_0);
- if(key_sym >= XK_KP_0 && key_sym <= XK_KP_9)
- return MGL_KEY_NUMPAD0 + (key_sym - XK_KP_0);
-
- /* TODO: Fill in the rest */
- switch(key_sym) {
- case XK_space: return MGL_KEY_SPACE;
- case XK_BackSpace: return MGL_KEY_BACKSPACE;
- case XK_Tab: return MGL_KEY_TAB;
- case XK_Return: return MGL_KEY_ENTER;
- case XK_Escape: return MGL_KEY_ESCAPE;
- case XK_Control_L: return MGL_KEY_LCONTROL;
- case XK_Shift_L: return MGL_KEY_LSHIFT;
- case XK_Alt_L: return MGL_KEY_LALT;
- case XK_Super_L: return MGL_KEY_LSYSTEM;
- case XK_Control_R: return MGL_KEY_RCONTROL;
- case XK_Shift_R: return MGL_KEY_RSHIFT;
- case XK_Alt_R: return MGL_KEY_RALT;
- case XK_Super_R: return MGL_KEY_RSYSTEM;
- case XK_Delete: return MGL_KEY_DELETE;
- case XK_Home: return MGL_KEY_HOME;
- case XK_Left: return MGL_KEY_LEFT;
- case XK_Up: return MGL_KEY_UP;
- case XK_Right: return MGL_KEY_RIGHT;
- case XK_Down: return MGL_KEY_DOWN;
- case XK_Page_Up: return MGL_KEY_PAGEUP;
- case XK_Page_Down: return MGL_KEY_PAGEDOWN;
- case XK_End: return MGL_KEY_END;
- case XK_F1: return MGL_KEY_F1;
- case XK_F2: return MGL_KEY_F2;
- case XK_F3: return MGL_KEY_F3;
- case XK_F4: return MGL_KEY_F4;
- case XK_F5: return MGL_KEY_F5;
- case XK_F6: return MGL_KEY_F6;
- case XK_F7: return MGL_KEY_F7;
- case XK_F8: return MGL_KEY_F8;
- case XK_F9: return MGL_KEY_F9;
- case XK_F10: return MGL_KEY_F10;
- case XK_F11: return MGL_KEY_F11;
- case XK_F12: return MGL_KEY_F12;
- case XK_F13: return MGL_KEY_F13;
- case XK_F14: return MGL_KEY_F14;
- case XK_F15: return MGL_KEY_F15;
- case XK_Insert: return MGL_KEY_INSERT;
- case XK_Pause: return MGL_KEY_PAUSE;
- case XK_Print: return MGL_KEY_PRINTSCREEN;
- case XK_KP_Insert: return MGL_KEY_NUMPAD0;
- case XK_KP_End: return MGL_KEY_NUMPAD1;
- case XK_KP_Down: return MGL_KEY_NUMPAD2;
- case XK_KP_Page_Down: return MGL_KEY_NUMPAD3;
- case XK_KP_Left: return MGL_KEY_NUMPAD4;
- case XK_KP_Begin: return MGL_KEY_NUMPAD5;
- case XK_KP_Right: return MGL_KEY_NUMPAD6;
- case XK_KP_Home: return MGL_KEY_NUMPAD7;
- case XK_KP_Up: return MGL_KEY_NUMPAD8;
- case XK_KP_Page_Up: return MGL_KEY_NUMPAD9;
- case XK_KP_Enter: return MGL_KEY_NUMPAD_ENTER;
- case XF86XK_AudioLowerVolume: return MGL_KEY_AUDIO_LOWER_VOLUME;
- case XF86XK_AudioRaiseVolume: return MGL_KEY_AUDIO_RAISE_VOLUME;
- case XF86XK_AudioPlay: return MGL_KEY_AUDIO_PLAY;
- case XF86XK_AudioStop: return MGL_KEY_AUDIO_STOP;
- case XF86XK_AudioPause: return MGL_KEY_AUDIO_PAUSE;
- case XF86XK_AudioMute: return MGL_KEY_AUDIO_MUTE;
- case XF86XK_AudioPrev: return MGL_KEY_AUDIO_PREV;
- case XF86XK_AudioNext: return MGL_KEY_AUDIO_NEXT;
- case XF86XK_AudioRewind: return MGL_KEY_AUDIO_REWIND;
- case XF86XK_AudioForward: return MGL_KEY_AUDIO_FORWARD;
- case XK_dead_acute: return MGL_KEY_DEAD_ACUTE;
- case XK_apostrophe: return MGL_KEY_APOSTROPHE;
- case XK_F16: return MGL_KEY_F16;
- case XK_F17: return MGL_KEY_F17;
- case XK_F18: return MGL_KEY_F18;
- case XK_F19: return MGL_KEY_F19;
- case XK_F20: return MGL_KEY_F20;
- case XK_F21: return MGL_KEY_F21;
- case XK_F22: return MGL_KEY_F22;
- case XK_F23: return MGL_KEY_F23;
- case XK_F24: return MGL_KEY_F24;
- }
- return MGL_KEY_UNKNOWN;
-}
-
-static mgl_mouse_button x11_button_to_mgl_button(unsigned int button) {
- switch(button) {
- case Button1: return MGL_BUTTON_LEFT;
- case Button2: return MGL_BUTTON_MIDDLE;
- case Button3: return MGL_BUTTON_RIGHT;
- case 8: return MGL_BUTTON_XBUTTON1;
- case 9: return MGL_BUTTON_XBUTTON2;
- }
- return MGL_BUTTON_UNKNOWN;
-}
-
-static void mgl_window_handle_key_event(XKeyEvent *xkey, mgl_event *event, mgl_context *context) {
- event->key.code = x11_keysym_to_mgl_key(XKeycodeToKeysym(context->connection, xkey->keycode, 0));
- event->key.alt = ((xkey->state & Mod1Mask) != 0) || ((xkey->state & Mod5Mask) != 0);
- event->key.control = ((xkey->state & ControlMask) != 0);
- event->key.shift = ((xkey->state & ShiftMask) != 0);
- event->key.system = ((xkey->state & Mod4Mask) != 0);
-}
-
-static void mgl_window_handle_text_event(mgl_x11_window *self, XEvent *xev) {
- /* Ignore silent keys */
- if(XFilterEvent(xev, None))
- return;
-
- char buf[128];
-
- Status status;
- KeySym ksym;
- const size_t input_str_len = Xutf8LookupString(self->xic, &xev->xkey, buf, sizeof(buf), &ksym, &status);
- /* TODO: Handle XBufferOverflow */
- if(status == XBufferOverflow || input_str_len == 0)
- return;
-
- /* TODO: Set XIC location on screen with XSetICValues */
-
- for(size_t i = 0; i < input_str_len;) {
- const unsigned char *cp = (const unsigned char*)&buf[i];
- uint32_t codepoint;
- size_t clen;
- if(!mgl_utf8_decode(cp, input_str_len - i, &codepoint, &clen)) {
- codepoint = *cp;
- clen = 1;
- }
-
- mgl_event text_event;
- text_event.type = MGL_EVENT_TEXT_ENTERED;
- text_event.text.codepoint = codepoint;
- text_event.text.size = clen;
- 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))
- break;
-
- i += clen;
- }
-}
-
-static bool mgl_on_monitor_added(Display *display, mgl_window *mgl_window, XRROutputChangeNotifyEvent *rr_output_change_event, XRRScreenResources *screen_res, RROutput output_id, XRROutputInfo *out_info, mgl_event *event) {
- char display_name[256];
- mgl_monitor *monitor = NULL;
- XRRCrtcInfo *crt_info = NULL;
- const XRRModeInfo *mode_info = NULL;
-
- if(!rr_output_change_event->mode)
- return false;
-
- if(mgl_x11_window_get_monitor_by_id(mgl_window, output_id))
- return false;
-
- if(out_info->nameLen >= (int)sizeof(display_name))
- return false;
-
- crt_info = XRRGetCrtcInfo(display, screen_res, out_info->crtc);
- if(!crt_info)
- goto done;
-
- mode_info = get_mode_info(screen_res, rr_output_change_event->mode);
- if(!mode_info)
- goto done;
-
- 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,
- (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));
-
- if(!monitor)
- goto done;
-
- event->monitor_connected.id = monitor->id;
- event->monitor_connected.name = monitor->name;
- event->monitor_connected.x = monitor->pos.x;
- event->monitor_connected.y = monitor->pos.y;
- event->monitor_connected.width = monitor->size.x;
- event->monitor_connected.height = monitor->size.y;
- event->monitor_connected.refresh_rate = monitor->refresh_rate;
- event->type = MGL_EVENT_MONITOR_CONNECTED;
-
- done:
- if(crt_info)
- XRRFreeCrtcInfo(crt_info);
- return monitor != NULL;
-}
-
-static bool mgl_on_monitor_state_changed(Display *display, mgl_window *mgl_window, XRROutputChangeNotifyEvent *rr_output_change_event, mgl_event *event) {
- bool state_changed = false;
- XRROutputInfo *out_info = NULL;
-
- if(!rr_output_change_event->output)
- return false;
-
- XRRScreenResources *screen_res = XRRGetScreenResources(display, DefaultRootWindow(display));
- if(!screen_res)
- return false;
-
- out_info = XRRGetOutputInfo(display, screen_res, rr_output_change_event->output);
- 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);
- }
-
- if(out_info)
- XRRFreeOutputInfo(out_info);
-
- XRRFreeScreenResources(screen_res);
- return state_changed;
-}
-
-static bool mgl_on_monitor_property_changed(Display *display, mgl_window *mgl_window, XRRCrtcChangeNotifyEvent *rr_crtc_change_event, mgl_event *event) {
- 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);
- if(!monitor)
- return false;
-
- XRRScreenResources *screen_res = XRRGetScreenResources(display, DefaultRootWindow(display));
- if(!screen_res)
- return false;
-
- monitor->pos = (mgl_vec2i){ .x = rr_crtc_change_event->x, .y = rr_crtc_change_event->y };
- monitor->size = (mgl_vec2i){ .x = rr_crtc_change_event->width, .y = rr_crtc_change_event->height };
- const XRRModeInfo *mode_info = get_mode_info(screen_res, rr_crtc_change_event->mode);
- if(mode_info)
- monitor->refresh_rate = monitor_info_get_framerate(mode_info);
-
- XRRFreeScreenResources(screen_res);
-
- event->monitor_property_changed.id = monitor->id;
- event->monitor_property_changed.name = monitor->name;
- event->monitor_property_changed.x = monitor->pos.x;
- event->monitor_property_changed.y = monitor->pos.y;
- event->monitor_property_changed.width = monitor->size.x;
- event->monitor_property_changed.height = monitor->size.y;
- event->monitor_property_changed.refresh_rate = monitor->refresh_rate;
- event->type = MGL_EVENT_MONITOR_PROPERTY_CHANGED;
- return true;
-}
-
-/* Returns true if an event was generated */
-static bool mgl_on_rr_notify(mgl_context *context, mgl_window *mgl_window, XEvent *xev, int subtype, mgl_event *event) {
- switch(subtype) {
- case RRNotify_CrtcChange: {
- XRRCrtcChangeNotifyEvent *rr_crtc_change_event = (XRRCrtcChangeNotifyEvent*)xev;
- return mgl_on_monitor_property_changed(context->connection, mgl_window, rr_crtc_change_event, event);
- }
- case RRNotify_OutputChange: {
- XRROutputChangeNotifyEvent *rr_output_change_event = (XRROutputChangeNotifyEvent*)xev;
- return mgl_on_monitor_state_changed(context->connection, mgl_window, rr_output_change_event, event);
- }
- }
- 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;
- switch(xev->type - context->randr_event_base) {
- case RRScreenChangeNotify: {
- XRRUpdateConfiguration(xev);
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- case RRNotify: {
- 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;
- return;
- }
-
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- }
-
- switch(xev->type) {
- case KeyPress: {
- if(!self->key_repeat_enabled && xev->xkey.keycode == impl->prev_keycode_pressed && !impl->key_was_released) {
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
-
- impl->prev_keycode_pressed = xev->xkey.keycode;
- impl->key_was_released = false;
-
- event->type = MGL_EVENT_KEY_PRESSED;
- mgl_window_handle_key_event(&xev->xkey, event, context);
- mgl_window_handle_text_event(impl, xev);
- return;
- }
- case KeyRelease: {
- if(xev->xkey.keycode == impl->prev_keycode_pressed)
- impl->key_was_released = true;
-
- event->type = MGL_EVENT_KEY_RELEASED;
- mgl_window_handle_key_event(&xev->xkey, event, context);
- return;
- }
- case ButtonPress: {
- if(xev->xbutton.button == Button4) {
- /* Mouse scroll up */
- event->type = MGL_EVENT_MOUSE_WHEEL_SCROLLED;
- event->mouse_wheel_scroll.delta = 1;
- event->mouse_wheel_scroll.x = xev->xbutton.x;
- event->mouse_wheel_scroll.y = xev->xbutton.y;
- } else if(xev->xbutton.button == Button5) {
- /* Mouse scroll down */
- event->type = MGL_EVENT_MOUSE_WHEEL_SCROLLED;
- event->mouse_wheel_scroll.delta = -1;
- event->mouse_wheel_scroll.x = xev->xbutton.x;
- event->mouse_wheel_scroll.y = xev->xbutton.y;
- } else {
- event->type = MGL_EVENT_MOUSE_BUTTON_PRESSED;
- event->mouse_button.button = x11_button_to_mgl_button(xev->xbutton.button);
- event->mouse_button.x = xev->xbutton.x;
- event->mouse_button.y = xev->xbutton.y;
- }
- return;
- }
- case ButtonRelease: {
- event->type = MGL_EVENT_MOUSE_BUTTON_RELEASED;
- event->mouse_button.button = x11_button_to_mgl_button(xev->xbutton.button);
- event->mouse_button.x = xev->xbutton.x;
- event->mouse_button.y = xev->xbutton.y;
- return;
- }
- case FocusIn: {
- XSetICFocus(impl->xic);
-
- XWMHints* hints = XGetWMHints(context->connection, impl->window);
- if(hints) {
- hints->flags &= ~XUrgencyHint;
- XSetWMHints(context->connection, impl->window, hints);
- XFree(hints);
- }
-
- event->type = MGL_EVENT_GAINED_FOCUS;
- self->focused = true;
- return;
- }
- case FocusOut: {
- XUnsetICFocus(impl->xic);
- event->type = MGL_EVENT_LOST_FOCUS;
- self->focused = false;
- return;
- }
- case ConfigureNotify: {
- if(!injected)
- 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);
- }
-
- 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);
-
- event->type = MGL_EVENT_RESIZED;
- event->size.width = self->size.x;
- event->size.height = self->size.y;
- return;
- }
-
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- case MotionNotify: {
- if(!injected)
- while(XCheckTypedWindowEvent(context->connection, impl->window, MotionNotify, xev)) {}
-
- self->cursor_position.x = xev->xmotion.x;
- self->cursor_position.y = xev->xmotion.y;
-
- event->type = MGL_EVENT_MOUSE_MOVED;
- event->mouse_move.x = self->cursor_position.x;
- event->mouse_move.y = self->cursor_position.y;
- return;
- }
- case SelectionClear: {
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- case SelectionRequest: {
- XSelectionEvent selection_event;
- selection_event.type = SelectionNotify;
- selection_event.requestor = xev->xselectionrequest.requestor;
- selection_event.selection = xev->xselectionrequest.selection;
- selection_event.property = xev->xselectionrequest.property;
- selection_event.time = xev->xselectionrequest.time;
- selection_event.target = xev->xselectionrequest.target;
-
- if(selection_event.selection == impl->clipboard_atom) {
- /* TODO: Support ascii text separately by unsetting the 8th bit in the clipboard string? */
- if(selection_event.target == impl->targets_atom) {
- /* A client requested for our valid conversion targets */
- int num_targets = 3;
- Atom targets[4];
- targets[0] = impl->targets_atom;
- targets[1] = impl->text_atom;
- targets[2] = XA_STRING;
- if(impl->utf8_string_atom) {
- targets[3] = impl->utf8_string_atom;
- num_targets = 4;
- }
-
- XChangeProperty(context->connection, selection_event.requestor, selection_event.property, XA_ATOM, 32, PropModeReplace, (unsigned char*)targets, num_targets);
- selection_event.target = impl->targets_atom;
- XSendEvent(context->connection, selection_event.requestor, True, NoEventMask, (XEvent*)&selection_event);
- XFlush(context->connection);
- event->type = MGL_EVENT_UNKNOWN;
- return;
- } else if(selection_event.target == XA_STRING || (!impl->utf8_string_atom && selection_event.target == impl->text_atom)) {
- /* A client requested ascii clipboard */
- XChangeProperty(context->connection, selection_event.requestor, selection_event.property, XA_STRING, 8, PropModeReplace, (const unsigned char*)impl->clipboard_string, impl->clipboard_size);
- selection_event.target = XA_STRING;
- XSendEvent(context->connection, selection_event.requestor, True, NoEventMask, (XEvent*)&selection_event);
- XFlush(context->connection);
- event->type = MGL_EVENT_UNKNOWN;
- return;
- } else if(impl->utf8_string_atom && (selection_event.target == impl->utf8_string_atom || selection_event.target == impl->text_atom)) {
- /* A client requested utf8 clipboard */
- XChangeProperty(context->connection, selection_event.requestor, selection_event.property, impl->utf8_string_atom, 8, PropModeReplace, (const unsigned char*)impl->clipboard_string, impl->clipboard_size);
- selection_event.target = impl->utf8_string_atom;
- XSendEvent(context->connection, selection_event.requestor, True, NoEventMask, (XEvent*)&selection_event);
- XFlush(context->connection);
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- }
-
- selection_event.property = None;
- XSendEvent(context->connection, selection_event.requestor, True, NoEventMask, (XEvent*)&selection_event);
- XFlush(context->connection);
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- case ClientMessage: {
- if(xev->xclient.format == 32 && (unsigned long)xev->xclient.data.l[0] == context->wm_delete_window_atom) {
- event->type = MGL_EVENT_CLOSED;
- self->open = false;
- return;
- } else if(xev->xclient.format == 32 && (unsigned long)xev->xclient.data.l[0] == context->net_wm_ping_atom) {
- xev->xclient.window = DefaultRootWindow(context->connection);
- XSendEvent(context->connection, DefaultRootWindow(context->connection), False, SubstructureNotifyMask | SubstructureRedirectMask, xev);
- XFlush(context->connection);
- }
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- case MappingNotify: {
- /* TODO: Only handle this globally once for mgl, not for each window */
- XRefreshKeyboardMapping(&xev->xmapping);
- event->type = MGL_EVENT_MAPPING_CHANGED;
- event->mapping_changed.type = MappingModifier;
- switch(xev->xmapping.request) {
- case MappingModifier:
- event->mapping_changed.type = MGL_MAPPING_CHANGED_MODIFIER;
- break;
- case MappingKeyboard:
- event->mapping_changed.type = MGL_MAPPING_CHANGED_KEYBOARD;
- break;
- case MappingPointer:
- event->mapping_changed.type = MGL_MAPPING_CHANGED_POINTER;
- break;
- }
- return;
- }
- default: {
- event->type = MGL_EVENT_UNKNOWN;
- return;
- }
- }
-}
-
-static bool mgl_x11_window_poll_event(mgl_window *self, mgl_event *event) {
- mgl_x11_window *impl = self->impl;
- mgl_context *context = mgl_get_context();
- Display *display = context->connection;
-
- if(mgl_x11_window_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);
- else
- event->type = MGL_EVENT_UNKNOWN;
- return true;
- } else {
- return false;
- }
-}
-
-static bool mgl_x11_window_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);
- 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_x11_window_set_visible(mgl_window *self, bool visible) {
- mgl_x11_window *impl = self->impl;
- mgl_context *context = mgl_get_context();
- if(visible)
- XMapWindow(context->connection, impl->window);
- else
- XUnmapWindow(context->connection, impl->window);
- XFlush(context->connection);
-}
-
-/* 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) {
- (void)self;
- if(key < 0 || key >= __MGL_NUM_KEYS__)
- return false;
-
- mgl_context *context = mgl_get_context();
- const KeySym keysym = mgl_key_to_x11_keysym(key);
- if(keysym == XK_VoidSymbol)
- return false;
-
- KeyCode keycode = XKeysymToKeycode(context->connection, keysym);
- if(keycode == 0)
- return false;
-
- char keys[32];
- XQueryKeymap(context->connection, keys);
-
- return (keys[keycode / 8] & (1 << (keycode % 8))) != 0;
-}
-
-/* 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) {
- (void)self;
- if(button < 0 || button >= __MGL_NUM_MOUSE_BUTTONS__)
- return false;
-
- mgl_context *context = mgl_get_context();
- Window root, child;
- int root_x, root_y, win_x, win_y;
-
- unsigned int buttons_mask = 0;
- XQueryPointer(context->connection, DefaultRootWindow(context->connection), &root, &child, &root_x, &root_y, &win_x, &win_y, &buttons_mask);
-
- switch(button) {
- case MGL_BUTTON_LEFT: return buttons_mask & Button1Mask;
- case MGL_BUTTON_MIDDLE: return buttons_mask & Button2Mask;
- case MGL_BUTTON_RIGHT: return buttons_mask & Button3Mask;
- case MGL_BUTTON_XBUTTON1: return false; /* Not supported by x11 */
- case MGL_BUTTON_XBUTTON2: return false; /* Not supported by x11 */
- default: return false;
- }
-
- 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 void mgl_x11_window_close(mgl_window *self) {
- mgl_x11_window *impl = self->impl;
- mgl_context *context = mgl_get_context();
- if(impl->window) {
- XDestroyWindow(context->connection, impl->window);
- XSync(context->connection, False);
- impl->window = None;
- }
- self->open = false;
-}
-
-static void mgl_x11_window_set_cursor_visible(mgl_window *self, bool visible) {
- mgl_x11_window *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;
- self->vsync_enabled = enabled;
- set_vertical_sync_enabled(impl, self->vsync_enabled ? 1 : 0);
-}
-
-static bool mgl_x11_window_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;
- mgl_context *context = mgl_get_context();
-
- XEvent xev;
- xev.type = ClientMessage;
- xev.xclient.window = impl->window;
- xev.xclient.message_type = impl->net_wm_state_atom;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = fullscreen ? 1 : 0;
- xev.xclient.data.l[1] = impl->net_wm_state_fullscreen_atom;
- xev.xclient.data.l[2] = 0;
- xev.xclient.data.l[3] = 1;
- xev.xclient.data.l[4] = 0;
-
- if(!XSendEvent(context->connection, DefaultRootWindow(context->connection), 0, SubstructureRedirectMask | SubstructureNotifyMask, &xev)) {
- fprintf(stderr, "mgl warning: failed to change window fullscreen state\n");
- return;
- }
-
- XFlush(context->connection);
-}
-
-static bool mgl_x11_window_is_fullscreen(const mgl_window *self) {
- mgl_x11_window *impl = self->impl;
- mgl_context *context = mgl_get_context();
-
- bool is_fullscreen = false;
- Atom type = None;
- int format = 0;
- unsigned long items = 0;
- unsigned long remaining_bytes = 0;
- unsigned char *data = NULL;
- if(XGetWindowProperty(context->connection, impl->window, impl->net_wm_state_atom, 0, 1024, False, PropertyNewValue, &type, &format, &items, &remaining_bytes, &data) == Success && data) {
- is_fullscreen = format == 32 && *(unsigned long*)data == impl->net_wm_state_fullscreen_atom;
- XFree(data);
- }
- return is_fullscreen;
-}
-
-static void mgl_x11_window_set_position(mgl_window *self, mgl_vec2i position) {
- mgl_x11_window *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;
- if(size.x < 0)
- size.x = 0;
- if(size.y < 0)
- size.y = 0;
- XResizeWindow(mgl_get_context()->connection, impl->window, size.x, size.y);
- 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;
- mgl_context *context = mgl_get_context();
-
- if(impl->clipboard_string) {
- free(impl->clipboard_string);
- impl->clipboard_string = NULL;
- impl->clipboard_size = 0;
- }
-
- XSetSelectionOwner(context->connection, impl->clipboard_atom, impl->window, CurrentTime);
- XFlush(context->connection);
-
- /* 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");
- return;
- }
-
- impl->clipboard_string = malloc(size + 1);
- if(!impl->clipboard_string) {
- fprintf(stderr, "mgl error: failed to allocate string for clipboard\n");
- return;
- }
- memcpy(impl->clipboard_string, str, size);
- impl->clipboard_string[size] = '\0';
- impl->clipboard_size = size;
-}
-
-static Atom find_matching_atom(const Atom *supported_atoms, size_t num_supported_atoms, const Atom *atoms, size_t num_atoms) {
- for(size_t j = 0; j < num_supported_atoms; ++j) {
- for(size_t i = 0; i < num_atoms; ++i) {
- if(atoms[i] == supported_atoms[j])
- return atoms[i];
- }
- }
- 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) {
- return MGL_CLIPBOARD_TYPE_STRING;
- } else if(type == mgl_x11_window->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){
- return MGL_CLIPBOARD_TYPE_IMAGE_JPG;
- } else if(type == mgl_x11_window->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;
- assert(callback);
-
- mgl_context *context = mgl_get_context();
- Window selection_owner = XGetSelectionOwner(context->connection, impl->clipboard_atom);
-
- if(!selection_owner)
- return false;
-
- /* Return data immediately if we are the owner of the clipboard, because we can't process other events in the below event loop */
- if(selection_owner == impl->window) {
- if(!impl->clipboard_string)
- return false;
-
- return callback((const unsigned char*)impl->clipboard_string, impl->clipboard_size, MGL_CLIPBOARD_TYPE_STRING, userdata);
- }
-
- XEvent xev;
- while(XCheckTypedWindowEvent(context->connection, impl->window, SelectionNotify, &xev)) {}
-
- /* Sorted by preference */
- /* TODO: Support more types (BITMAP?, PIXMAP?) */
- Atom supported_clipboard_types[7];
-
- int supported_clipboard_type_index = 0;
- if(clipboard_types & MGL_CLIPBOARD_TYPE_IMAGE_PNG) {
- supported_clipboard_types[supported_clipboard_type_index++] = impl->image_png_atom;
- }
-
- if(clipboard_types & MGL_CLIPBOARD_TYPE_IMAGE_JPG) {
- supported_clipboard_types[supported_clipboard_type_index++] = impl->image_jpeg_atom;
- }
-
- if(clipboard_types & MGL_CLIPBOARD_TYPE_IMAGE_GIF) {
- supported_clipboard_types[supported_clipboard_type_index++] = impl->image_gif_atom;
- }
-
- if(clipboard_types & MGL_CLIPBOARD_TYPE_STRING) {
- supported_clipboard_types[supported_clipboard_type_index++] = impl->utf8_string_atom;
- supported_clipboard_types[supported_clipboard_type_index++] = XA_STRING;
- supported_clipboard_types[supported_clipboard_type_index++] = impl->text_atom;
- }
-
- const unsigned long num_supported_clipboard_types = supported_clipboard_type_index;
-
- Atom requested_clipboard_type = None;
- const Atom XA_TARGETS = XInternAtom(context->connection, "TARGETS", False);
- XConvertSelection(context->connection, impl->clipboard_atom, XA_TARGETS, impl->clipboard_atom, impl->window, CurrentTime);
-
- mgl_clock timeout_timer;
- mgl_clock_init(&timeout_timer);
- bool success = false;
-
- const double timeout_seconds = 5.0;
- while(mgl_clock_get_elapsed_time_seconds(&timeout_timer) < timeout_seconds) {
- /* TODO: Wait for SelectionNotify event instead */
- while(XCheckTypedWindowEvent(context->connection, impl->window, SelectionNotify, &xev)) {
- if(mgl_clock_get_elapsed_time_seconds(&timeout_timer) >= timeout_seconds)
- break;
-
- if(!xev.xselection.property)
- continue;
-
- if(!xev.xselection.target || xev.xselection.selection != impl->clipboard_atom)
- continue;
-
- Atom type = None;
- int format;
- unsigned long items;
- unsigned long remaining_bytes = 0;
- unsigned char *data = NULL;
-
- if(xev.xselection.target == XA_TARGETS && requested_clipboard_type == None) {
- /* TODO: Wait for PropertyNotify event instead */
- if(XGetWindowProperty(context->connection, xev.xselection.requestor, xev.xselection.property, 0, 1024, False, PropertyNewValue, &type, &format, &items, &remaining_bytes, &data) == Success && data) {
- if(type != impl->incr_atom && type == XA_ATOM && format == 32) {
- requested_clipboard_type = find_matching_atom(supported_clipboard_types, num_supported_clipboard_types, (Atom*)data, items);
- if(requested_clipboard_type == None) {
- /* Pasting clipboard data type we dont support */
- XFree(data);
- goto done;
- } else {
- XConvertSelection(context->connection, impl->clipboard_atom, requested_clipboard_type, impl->clipboard_atom, impl->window, CurrentTime);
- }
- }
-
- XFree(data);
- }
- } else if(xev.xselection.target == requested_clipboard_type) {
- bool got_data = false;
- long chunk_size = 65536;
-
- //XDeleteProperty(context->connection, self->window, mgl_x11_window->incr_atom);
- //XFlush(context->connection);
-
- while(mgl_clock_get_elapsed_time_seconds(&timeout_timer) < timeout_seconds) {
- unsigned long offset = 0;
-
- /* offset is specified in XGetWindowProperty as a multiple of 32-bit (4 bytes) */
- /* TODO: Wait for PropertyNotify event instead */
- while(XGetWindowProperty(context->connection, xev.xselection.requestor, xev.xselection.property, offset/4, chunk_size, True, PropertyNewValue, &type, &format, &items, &remaining_bytes, &data) == Success) {
- if(mgl_clock_get_elapsed_time_seconds(&timeout_timer) >= timeout_seconds)
- break;
-
- if(type == impl->incr_atom) {
- if(data)
- chunk_size = *(long*)data;
- XDeleteProperty(context->connection, impl->window, impl->incr_atom);
- XFlush(context->connection);
- XFree(data);
- data = NULL;
- break;
- } else if(type == requested_clipboard_type) {
- got_data = true;
- }
-
- if(!got_data && items == 0) {
- XDeleteProperty(context->connection, impl->window, type);
- XFlush(context->connection);
- if(data) {
- XFree(data);
- data = NULL;
- }
- break;
- }
-
- const size_t num_items_bytes = items * (format/8); /* format is the bit size of the data */
- if(got_data) {
- const mgl_clipboard_type clipboard_type = atom_type_to_supported_clipboard_type(impl, type, format);
- if(data && num_items_bytes > 0 && (int)clipboard_type != -1) {
- if(!callback(data, num_items_bytes, clipboard_type, userdata)) {
- XFree(data);
- goto done;
- }
- }
- }
- offset += num_items_bytes;
-
- if(data) {
- XFree(data);
- data = NULL;
- }
-
- if(got_data && num_items_bytes == 0/* && format == 8*/) {
- success = true;
- goto done;
- }
-
- if(remaining_bytes == 0)
- break;
- }
- }
-
- goto done;
- }
- }
- }
-
- done:
- if(requested_clipboard_type)
- XDeleteProperty(context->connection, impl->window, requested_clipboard_type);
- return success;
-}
-
-typedef struct {
- char **str;
- size_t *size;
-} ClipboardStringCallbackData;
-
-static bool clipboard_copy_string_callback(const unsigned char *data, size_t size, mgl_clipboard_type clipboard_type, void *userdata) {
- ClipboardStringCallbackData *callback_data = userdata;
- if(clipboard_type != MGL_CLIPBOARD_TYPE_STRING) {
- free(*callback_data->str);
- *callback_data->str = NULL;
- *callback_data->size = 0;
- return false;
- }
-
- char *new_data = realloc(*callback_data->str, *callback_data->size + size);
- if(!new_data) {
- free(*callback_data->str);
- *callback_data->str = NULL;
- *callback_data->size = 0;
- return false;
- }
-
- memcpy(new_data + *callback_data->size, data, size);
-
- *callback_data->str = new_data;
- *callback_data->size += size;
- return true;
-}
-
-static bool mgl_x11_window_get_clipboard_string(mgl_window *self, char **str, size_t *size) {
- assert(str);
- assert(size);
-
- *str = NULL;
- *size = 0;
-
- 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);
- if(!success) {
- free(*str);
- *str = NULL;
- *size = 0;
- }
- return success;
-}
-
-static void mgl_x11_window_set_key_repeat_enabled(mgl_window *self, bool enabled) {
- self->key_repeat_enabled = enabled;
-}
-
-static void mgl_x11_window_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;
- 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;
- 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));
- 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;
-
- /* TODO: Use CLIPBOARD_MANAGER and SAVE_TARGETS to save clipboard in clipboard manager on exit */
-
- mgl_context *context = mgl_get_context();
- /* TODO: Create all of these with one XInternAtoms call instead */
- impl->clipboard_atom = XInternAtom(context->connection, "CLIPBOARD", False);
- impl->targets_atom = XInternAtom(context->connection, "TARGETS", False);
- impl->text_atom = XInternAtom(context->connection, "TEXT", False);
- impl->utf8_string_atom = XInternAtom(context->connection, "UTF8_STRING", False);
- impl->image_png_atom = XInternAtom(context->connection, "image/png", False);
- impl->image_jpg_atom = XInternAtom(context->connection, "image/jpg", False);
- impl->image_jpeg_atom = XInternAtom(context->connection, "image/jpeg", False);
- impl->image_gif_atom = XInternAtom(context->connection, "image/gif", False);
- impl->incr_atom = XInternAtom(context->connection, "INCR", False);
- impl->net_wm_state_atom = XInternAtom(context->connection, "_NET_WM_STATE", False);
- impl->net_wm_state_fullscreen_atom = XInternAtom(context->connection, "_NET_WM_STATE_FULLSCREEN", False);
- impl->net_wm_state_above_atom = XInternAtom(context->connection, "_NET_WM_STATE_ABOVE", False);
- impl->net_wm_name_atom = XInternAtom(context->connection, "_NET_WM_NAME", False);
- impl->net_wm_window_type_atom = XInternAtom(context->connection, "_NET_WM_WINDOW_TYPE", False);
- impl->net_wm_window_type_normal_atom = XInternAtom(context->connection, "_NET_WM_WINDOW_TYPE_NORMAL", False);
- impl->net_wm_window_type_dialog_atom = XInternAtom(context->connection, "_NET_WM_WINDOW_TYPE_DIALOG", False);
- impl->net_wm_window_type_notification_atom = XInternAtom(context->connection, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False);
- impl->net_wm_window_type_utility = XInternAtom(context->connection, "_NET_WM_WINDOW_TYPE_UTILITY", False);
- impl->motif_wm_hints_atom = XInternAtom(context->connection, "_MOTIF_WM_HINTS", False);
-
- 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;
- }
- }
-
- impl->default_cursor = XCreateFontCursor(context->connection, XC_arrow);
- if(!impl->default_cursor) {
- mgl_x11_window_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);
- return false;
- }
-
- XColor dummy;
- 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);
- return false;
- }
-
- x11_events_circular_buffer_init(&impl->events);
-
- if(!mgl_x11_setup_window(self, title, params, existing_window)) {
- mgl_x11_window_deinit(self);
- return false;
- }
-
- context->current_window = self;
- return true;
-}
-
-void mgl_x11_window_deinit(mgl_window *self) {
- mgl_x11_window *impl = self->impl;
- mgl_context *context = mgl_get_context();
- if(!impl)
- return;
-
- mgl_x11_window_clear_monitors(self);
-
- if(impl->color_map) {
- XFreeColormap(context->connection, impl->color_map);
- impl->color_map = None;
- }
-
- if(impl->invisible_cursor) {
- XFreeCursor(context->connection, impl->invisible_cursor);
- impl->invisible_cursor = None;
- }
-
- if(impl->default_cursor) {
- XFreeCursor(context->connection, impl->default_cursor);
- impl->default_cursor = None;
- }
-
- if(impl->xic) {
- XDestroyIC(impl->xic);
- impl->xic = NULL;
- }
-
- if(impl->xim) {
- XCloseIM(impl->xim);
- 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);
-
- if(impl->clipboard_string) {
- free(impl->clipboard_string);
- impl->clipboard_string = NULL;
- }
- impl->clipboard_size = 0;
-
- self->open = false;
-
- if(context->current_window == self)
- context->current_window = NULL;
-
- free(self->impl);
- self->impl = NULL;
-}