diff options
author | dec05eba <dec05eba@protonmail.com> | 2025-04-18 12:55:00 +0200 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2025-04-18 12:55:00 +0200 |
commit | 26c56565cc0573ce23eb8d172a6765bce1f657ce (patch) | |
tree | c4ceaacd336946acec12af5f2c5c4d2a78370744 /src/window/x11/window.c | |
parent | 506c271eafec23bf469caf6c29431191fa885e58 (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.c | 2066 |
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; -} |