/*
 * DWN - Desktop Window Manager
 * X11 Backend Implementation
 */

#define _GNU_SOURCE
#include "backends/x11/x11_backend.h"
#include "core/wm_types.h"
#include "core/wm_string.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/*==============================================================================
 * X11 Backend State
 *============================================================================*/

static struct {
    Display *display;
    int screen;
    Window root;
    bool initialized;
    int fd;
    
    /* Error handling */
    int last_error;
    XErrorHandler error_handler;
    XIOErrorHandler io_error_handler;
    
    /* Cursor cache */
    Cursor cursor_cache[XC_num_glyphs];
    bool cursor_loaded[XC_num_glyphs];
    
    /* Atoms cache */
    Atom atom_wm_protocols;
    Atom atom_wm_delete_window;
    Atom atom_wm_state;
    Atom atom_wm_fullscreen;
    Atom atom_wm_maximized_horz;
    Atom atom_wm_maximized_vert;
    Atom atom_net_wm_name;
    Atom atom_utf8_string;
} x11_state = {0};

/*==============================================================================
 * Error Handlers
 *============================================================================*/

static int x11_error_handler(Display *dpy, XErrorEvent *ev) {
    x11_state.last_error = ev->error_code;
    
    char msg[256];
    XGetErrorText(dpy, ev->error_code, msg, sizeof(msg));
    fprintf(stderr, "X11 Error: %s (request %d, minor %d)\n", 
            msg, ev->request_code, ev->minor_code);
    
    return 0;
}

static int x11_io_error_handler(Display *dpy) {
    (void)dpy;
    fprintf(stderr, "X11 IO Error: Connection to display lost\n");
    x11_state.initialized = false;
    return 0;
}

/*==============================================================================
 * Backend Information
 *============================================================================*/

static WmBackendInfo x11_get_info(void) {
    return (WmBackendInfo){
        .name = "x11",
        .version = "1.0",
        .description = "X11 Window System Backend",
        .capabilities = 
            WM_BACKEND_CAP_WINDOWS |
            WM_BACKEND_CAP_INPUT_EVENTS |
            WM_BACKEND_CAP_MULTI_MONITOR |
            WM_BACKEND_CAP_CLIPBOARD,
        .supports_multiple_instances = false,
        .requires_compositor = false
    };
}

/*==============================================================================
 * Lifecycle
 *============================================================================*/

static bool x11_init(void *config) {
    (void)config;
    
    if (x11_state.initialized) return true;
    
    XSetErrorHandler(x11_error_handler);
    XSetIOErrorHandler(x11_io_error_handler);
    
    return true;
}

static void x11_shutdown(void) {
    if (!x11_state.initialized) return;
    
    /* Free cached cursors */
    for (int i = 0; i < XC_num_glyphs; i++) {
        if (x11_state.cursor_loaded[i]) {
            XFreeCursor(x11_state.display, x11_state.cursor_cache[i]);
        }
    }
    
    x11_state.initialized = false;
}

static bool x11_is_initialized(void) {
    return x11_state.initialized;
}

/*==============================================================================
 * Connection
 *============================================================================*/

static bool x11_connect(void) {
    if (x11_state.display) return true;
    
    x11_state.display = XOpenDisplay(NULL);
    if (!x11_state.display) {
        fprintf(stderr, "Failed to open X11 display\n");
        return false;
    }
    
    x11_state.screen = DefaultScreen(x11_state.display);
    x11_state.root = RootWindow(x11_state.display, x11_state.screen);
    x11_state.fd = ConnectionNumber(x11_state.display);
    x11_state.initialized = true;
    
    /* Cache atoms */
    x11_state.atom_wm_protocols = XInternAtom(x11_state.display, "WM_PROTOCOLS", False);
    x11_state.atom_wm_delete_window = XInternAtom(x11_state.display, "WM_DELETE_WINDOW", False);
    x11_state.atom_wm_state = XInternAtom(x11_state.display, "_NET_WM_STATE", False);
    x11_state.atom_wm_fullscreen = XInternAtom(x11_state.display, "_NET_WM_STATE_FULLSCREEN", False);
    x11_state.atom_wm_maximized_horz = XInternAtom(x11_state.display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
    x11_state.atom_wm_maximized_vert = XInternAtom(x11_state.display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
    x11_state.atom_net_wm_name = XInternAtom(x11_state.display, "_NET_WM_NAME", False);
    x11_state.atom_utf8_string = XInternAtom(x11_state.display, "UTF8_STRING", False);
    
    return true;
}

static void x11_disconnect(void) {
    if (x11_state.display) {
        XCloseDisplay(x11_state.display);
        x11_state.display = NULL;
        x11_state.initialized = false;
    }
}

static bool x11_is_connected(void) {
    return x11_state.display != NULL && x11_state.initialized;
}

static int x11_get_file_descriptor(void) {
    return x11_state.fd;
}

static void x11_flush(void) {
    if (x11_state.display) XFlush(x11_state.display);
}

static void x11_sync(void) {
    if (x11_state.display) XSync(x11_state.display, False);
}

/*==============================================================================
 * Screen/Display
 *============================================================================*/

static void x11_get_screen_dimensions(int *width, int *height) {
    if (!x11_state.display) {
        if (width) *width = 0;
        if (height) *height = 0;
        return;
    }
    if (width) *width = DisplayWidth(x11_state.display, x11_state.screen);
    if (height) *height = DisplayHeight(x11_state.display, x11_state.screen);
}

static int x11_get_screen_count(void) {
    return x11_state.display ? ScreenCount(x11_state.display) : 0;
}

static void x11_get_screen_geometry(int screen, WmRect *geometry) {
    if (!x11_state.display || !geometry) return;
    
    geometry->x = 0;
    geometry->y = 0;
    geometry->width = DisplayWidth(x11_state.display, screen);
    geometry->height = DisplayHeight(x11_state.display, screen);
}

/*==============================================================================
 * Window Management - Geometry
 *============================================================================*/

static void x11_window_get_geometry(WmWindowHandle window, WmRect *geometry) {
    if (!x11_state.display || !geometry) return;
    
    Window root;
    int x, y;
    unsigned int width, height, border, depth;
    
    if (XGetGeometry(x11_state.display, (Window)window, &root,
                     &x, &y, &width, &height, &border, &depth)) {
        geometry->x = x;
        geometry->y = y;
        geometry->width = (int)width;
        geometry->height = (int)height;
    }
}

static void x11_window_set_geometry(WmWindowHandle window, const WmRect *geometry) {
    if (!x11_state.display || !geometry) return;
    
    XMoveResizeWindow(x11_state.display, (Window)window,
                      geometry->x, geometry->y,
                      (unsigned int)geometry->width,
                      (unsigned int)geometry->height);
}

static void x11_window_move(WmWindowHandle window, int x, int y) {
    if (!x11_state.display) return;
    XMoveWindow(x11_state.display, (Window)window, x, y);
}

static void x11_window_resize(WmWindowHandle window, int width, int height) {
    if (!x11_state.display) return;
    XResizeWindow(x11_state.display, (Window)window, 
                  (unsigned int)width, (unsigned int)height);
}

static void x11_window_move_resize(WmWindowHandle window, const WmRect *geometry) {
    x11_window_set_geometry(window, geometry);
}

/*==============================================================================
 * Window Management - Visibility
 *============================================================================*/

static void x11_window_show(WmWindowHandle window) {
    if (!x11_state.display) return;
    XMapWindow(x11_state.display, (Window)window);
}

static void x11_window_hide(WmWindowHandle window) {
    if (!x11_state.display) return;
    XUnmapWindow(x11_state.display, (Window)window);
}

static bool x11_window_is_visible(WmWindowHandle window) {
    if (!x11_state.display) return false;
    
    XWindowAttributes attrs;
    if (XGetWindowAttributes(x11_state.display, (Window)window, &attrs)) {
        return attrs.map_state == IsViewable;
    }
    return false;
}

static void x11_window_map(WmWindowHandle window) {
    x11_window_show(window);
}

static void x11_window_unmap(WmWindowHandle window) {
    x11_window_hide(window);
}

static bool x11_window_is_mapped(WmWindowHandle window) {
    return x11_window_is_visible(window);
}

/*==============================================================================
 * Window Management - Stacking
 *============================================================================*/

static void x11_window_raise(WmWindowHandle window) {
    if (!x11_state.display) return;
    XRaiseWindow(x11_state.display, (Window)window);
}

static void x11_window_lower(WmWindowHandle window) {
    if (!x11_state.display) return;
    XLowerWindow(x11_state.display, (Window)window);
}

static void x11_window_reparent(WmWindowHandle window, WmWindowHandle parent, int x, int y) {
    if (!x11_state.display) return;
    XReparentWindow(x11_state.display, (Window)window, (Window)parent, x, y);
}

/*==============================================================================
 * Window Management - Focus
 *============================================================================*/

static void x11_window_focus(WmWindowHandle window) {
    if (!x11_state.display) return;
    XSetInputFocus(x11_state.display, (Window)window, RevertToPointerRoot, CurrentTime);
}

static void x11_window_unfocus(WmWindowHandle window) {
    (void)window;
    if (!x11_state.display) return;
    XSetInputFocus(x11_state.display, PointerRoot, RevertToPointerRoot, CurrentTime);
}

static bool x11_window_is_focused(WmWindowHandle window) {
    if (!x11_state.display) return false;
    
    Window focused;
    int revert_to;
    XGetInputFocus(x11_state.display, &focused, &revert_to);
    return (Window)window == focused;
}

static WmWindowHandle x11_window_get_focused(void) {
    if (!x11_state.display) return NULL;
    
    Window focused;
    int revert_to;
    XGetInputFocus(x11_state.display, &focused, &revert_to);
    return (WmWindowHandle)focused;
}

/*==============================================================================
 * Window Management - Border
 *============================================================================*/

static void x11_window_set_border_width(WmWindowHandle window, int width) {
    if (!x11_state.display) return;
    XSetWindowBorderWidth(x11_state.display, (Window)window, (unsigned int)width);
}

static int x11_window_get_border_width(WmWindowHandle window) {
    if (!x11_state.display) return 0;
    
    XWindowAttributes attrs;
    if (XGetWindowAttributes(x11_state.display, (Window)window, &attrs)) {
        return (int)attrs.border_width;
    }
    return 0;
}

/*==============================================================================
 * Window Management - Properties
 *============================================================================*/

static void x11_window_set_title(WmWindowHandle window, const char *title) {
    if (!x11_state.display || !title) return;
    
    XStoreName(x11_state.display, (Window)window, title);
    
    if (x11_state.atom_net_wm_name != None && x11_state.atom_utf8_string != None) {
        XChangeProperty(x11_state.display, (Window)window,
                        x11_state.atom_net_wm_name, x11_state.atom_utf8_string,
                        8, PropModeReplace,
                        (const unsigned char*)title, (int)strlen(title));
    }
}

static char* x11_window_get_title(WmWindowHandle window) {
    if (!x11_state.display) return NULL;
    
    char *title = NULL;
    
    /* Try _NET_WM_NAME first (UTF-8) */
    if (x11_state.atom_net_wm_name != None) {
        Atom actual_type;
        int actual_format;
        unsigned long nitems, bytes_after;
        unsigned char *data = NULL;
        
        if (XGetWindowProperty(x11_state.display, (Window)window,
                               x11_state.atom_net_wm_name, 0, ~0L, False,
                               x11_state.atom_utf8_string, &actual_type,
                               &actual_format, &nitems, &bytes_after, &data) == Success) {
            if (data && actual_type == x11_state.atom_utf8_string) {
                title = strdup((char*)data);
                XFree(data);
                if (title) return title;
            }
            if (data) XFree(data);
        }
    }
    
    /* Fall back to WM_NAME */
    char *name = NULL;
    if (XFetchName(x11_state.display, (Window)window, &name)) {
        title = strdup(name);
        XFree(name);
    }
    
    return title;
}

/*==============================================================================
 * Window Management - Protocols
 *============================================================================*/

static bool x11_window_supports_protocol(WmWindowHandle window, const char *protocol) {
    if (!x11_state.display || !protocol) return false;
    
    Atom proto = XInternAtom(x11_state.display, protocol, True);
    if (proto == None) return false;
    
    Atom *protocols = NULL;
    int count = 0;
    if (XGetWMProtocols(x11_state.display, (Window)window, &protocols, &count)) {
        for (int i = 0; i < count; i++) {
            if (protocols[i] == proto) {
                XFree(protocols);
                return true;
            }
        }
        XFree(protocols);
    }
    return false;
}

static void x11_window_close(WmWindowHandle window) {
    if (!x11_state.display) return;
    
    if (x11_window_supports_protocol(window, "WM_DELETE_WINDOW")) {
        XEvent ev;
        ev.type = ClientMessage;
        ev.xclient.window = (Window)window;
        ev.xclient.message_type = x11_state.atom_wm_protocols;
        ev.xclient.format = 32;
        ev.xclient.data.l[0] = (long)x11_state.atom_wm_delete_window;
        ev.xclient.data.l[1] = CurrentTime;
        
        XSendEvent(x11_state.display, (Window)window, False, NoEventMask, &ev);
    } else {
        XKillClient(x11_state.display, (Window)window);
    }
}

static void x11_window_kill(WmWindowHandle window) {
    if (!x11_state.display) return;
    XKillClient(x11_state.display, (Window)window);
}

/*==============================================================================
 * Window Management - State
 *============================================================================*/

static void x11_window_set_fullscreen(WmWindowHandle window, bool fullscreen) {
    if (!x11_state.display) return;
    
    XEvent ev;
    ev.type = ClientMessage;
    ev.xclient.window = (Window)window;
    ev.xclient.message_type = x11_state.atom_wm_state;
    ev.xclient.format = 32;
    ev.xclient.data.l[0] = fullscreen ? 1 : 0;  /* _NET_WM_STATE_ADD or _NET_WM_STATE_REMOVE */
    ev.xclient.data.l[1] = (long)x11_state.atom_wm_fullscreen;
    ev.xclient.data.l[2] = 0;
    ev.xclient.data.l[3] = 1;  /* Source indication: normal application */
    ev.xclient.data.l[4] = 0;
    
    XSendEvent(x11_state.display, x11_state.root, False,
               SubstructureRedirectMask | SubstructureNotifyMask, &ev);
}

static void x11_window_set_urgent(WmWindowHandle window, bool urgent) {
    if (!x11_state.display) return;
    
    XWMHints *hints = XGetWMHints(x11_state.display, (Window)window);
    if (!hints) {
        hints = XAllocWMHints();
        if (!hints) return;
    }
    
    if (urgent) {
        hints->flags |= XUrgencyHint;
    } else {
        hints->flags &= ~XUrgencyHint;
    }
    
    XSetWMHints(x11_state.display, (Window)window, hints);
    XFree(hints);
}

static bool x11_window_is_urgent(WmWindowHandle window) {
    if (!x11_state.display) return false;
    
    XWMHints *hints = XGetWMHints(x11_state.display, (Window)window);
    if (!hints) return false;
    
    bool urgent = (hints->flags & XUrgencyHint) != 0;
    XFree(hints);
    return urgent;
}

/*==============================================================================
 * Input - Events
 *============================================================================*/

static bool x11_poll_event(WmBackendEvent *event) {
    if (!x11_state.display || !event) return false;
    
    if (!XPending(x11_state.display)) return false;
    
    XEvent xev;
    XNextEvent(x11_state.display, &xev);
    
    /* Translate X11 event to backend event */
    memset(event, 0, sizeof(*event));
    event->window = (WmWindowHandle)xev.xany.window;
    event->timestamp = (uint64_t)xev.xany.serial;
    
    switch (xev.type) {
        case Expose:
            event->type = WM_BACKEND_EVENT_EXPOSE;
            event->data.configure.x = xev.xexpose.x;
            event->data.configure.y = xev.xexpose.y;
            event->data.configure.width = xev.xexpose.width;
            event->data.configure.height = xev.xexpose.height;
            break;
            
        case ConfigureNotify:
            event->type = WM_BACKEND_EVENT_CONFIGURE;
            event->data.configure.x = xev.xconfigure.x;
            event->data.configure.y = xev.xconfigure.y;
            event->data.configure.width = xev.xconfigure.width;
            event->data.configure.height = xev.xconfigure.height;
            break;
            
        case MapNotify:
            event->type = WM_BACKEND_EVENT_MAP;
            break;
            
        case UnmapNotify:
            event->type = WM_BACKEND_EVENT_UNMAP;
            break;
            
        case DestroyNotify:
            event->type = WM_BACKEND_EVENT_DESTROY;
            break;
            
        case FocusIn:
            event->type = WM_BACKEND_EVENT_FOCUS_IN;
            break;
            
        case FocusOut:
            event->type = WM_BACKEND_EVENT_FOCUS_OUT;
            break;
            
        case KeyPress:
            event->type = WM_BACKEND_EVENT_KEY_PRESS;
            event->data.key.keycode = xev.xkey.keycode;
            event->data.key.state = xev.xkey.state;
            break;
            
        case KeyRelease:
            event->type = WM_BACKEND_EVENT_KEY_RELEASE;
            event->data.key.keycode = xev.xkey.keycode;
            event->data.key.state = xev.xkey.state;
            break;
            
        case ButtonPress:
            event->type = WM_BACKEND_EVENT_BUTTON_PRESS;
            event->data.button.button = xev.xbutton.button;
            event->data.button.x = xev.xbutton.x;
            event->data.button.y = xev.xbutton.y;
            event->data.button.state = xev.xbutton.state;
            break;
            
        case ButtonRelease:
            event->type = WM_BACKEND_EVENT_BUTTON_RELEASE;
            event->data.button.button = xev.xbutton.button;
            event->data.button.x = xev.xbutton.x;
            event->data.button.y = xev.xbutton.y;
            event->data.button.state = xev.xbutton.state;
            break;
            
        case MotionNotify:
            event->type = WM_BACKEND_EVENT_MOTION;
            event->data.button.x = xev.xmotion.x;
            event->data.button.y = xev.xmotion.y;
            event->data.button.state = xev.xmotion.state;
            break;
            
        case EnterNotify:
            event->type = WM_BACKEND_EVENT_ENTER;
            break;
            
        case LeaveNotify:
            event->type = WM_BACKEND_EVENT_LEAVE;
            break;
            
        case PropertyNotify:
            event->type = WM_BACKEND_EVENT_PROPERTY;
            event->data.property.name = XGetAtomName(x11_state.display, xev.xproperty.atom);
            break;
            
        case ClientMessage:
            event->type = WM_BACKEND_EVENT_CLIENT_MESSAGE;
            break;
            
        default:
            event->type = WM_BACKEND_EVENT_NONE;
            break;
    }
    
    return true;
}

static void x11_wait_event(WmBackendEvent *event) {
    if (!x11_state.display || !event) return;
    while (!x11_poll_event(event)) {
        /* Wait for event */
    }
}

/*==============================================================================
 * Input - Grabbing
 *============================================================================*/

static void x11_grab_key(int keycode, uint32_t modifiers, WmWindowHandle window,
                         bool owner_events, uint32_t pointer_mode, uint32_t keyboard_mode) {
    if (!x11_state.display) return;
    XGrabKey(x11_state.display, keycode, modifiers, (Window)window,
             owner_events, pointer_mode, keyboard_mode);
}

static void x11_ungrab_key(int keycode, uint32_t modifiers, WmWindowHandle window) {
    if (!x11_state.display) return;
    XUngrabKey(x11_state.display, keycode, modifiers, (Window)window);
}

static void x11_grab_button(int button, uint32_t modifiers, WmWindowHandle window,
                            bool owner_events, uint32_t event_mask,
                            uint32_t pointer_mode, uint32_t keyboard_mode,
                            WmWindowHandle confine_to, uint32_t cursor) {
    if (!x11_state.display) return;
    XGrabButton(x11_state.display, button, modifiers, (Window)window,
                owner_events, event_mask, pointer_mode, keyboard_mode,
                (Window)confine_to, cursor);
}

static void x11_ungrab_button(int button, uint32_t modifiers, WmWindowHandle window) {
    if (!x11_state.display) return;
    XUngrabButton(x11_state.display, button, modifiers, (Window)window);
}

static void x11_grab_pointer(WmWindowHandle window, bool owner_events, uint32_t event_mask,
                             uint32_t pointer_mode, uint32_t keyboard_mode,
                             WmWindowHandle confine_to, uint32_t cursor, WmTime time) {
    if (!x11_state.display) return;
    XGrabPointer(x11_state.display, (Window)window, owner_events, event_mask,
                 pointer_mode, keyboard_mode, (Window)confine_to,
                 (Cursor)cursor, (Time)time);
}

static void x11_ungrab_pointer(WmTime time) {
    if (!x11_state.display) return;
    XUngrabPointer(x11_state.display, (Time)time);
}

/*==============================================================================
 * Input - Pointer
 *============================================================================*/

static void x11_query_pointer(WmWindowHandle window, int *root_x, int *root_y,
                              int *win_x, int *win_y, uint32_t *mask) {
    if (!x11_state.display) return;
    
    Window root, child;
    int rx, ry, wx, wy;
    unsigned int m;
    
    XQueryPointer(x11_state.display, (Window)window, &root, &child,
                  &rx, &ry, &wx, &wy, &m);
    
    if (root_x) *root_x = rx;
    if (root_y) *root_y = ry;
    if (win_x) *win_x = wx;
    if (win_y) *win_y = wy;
    if (mask) *mask = m;
}

static void x11_warp_pointer(WmWindowHandle dest_w, int dest_x, int dest_y) {
    if (!x11_state.display) return;
    XWarpPointer(x11_state.display, None, (Window)dest_w, 0, 0, 0, 0, dest_x, dest_y);
}

/*==============================================================================
 * Atoms/Properties
 *============================================================================*/

static uint32_t x11_atom_get(const char *name) {
    if (!x11_state.display || !name) return 0;
    return (uint32_t)XInternAtom(x11_state.display, name, False);
}

static const char* x11_atom_get_name(uint32_t atom) {
    if (!x11_state.display || atom == 0) return NULL;
    return XGetAtomName(x11_state.display, (Atom)atom);
}

/*==============================================================================
 * Backend Interface
 *============================================================================*/

static const BackendInterface x11_backend_interface = {
    .name = "x11",
    .get_info = x11_get_info,
    
    .init = x11_init,
    .shutdown = x11_shutdown,
    .is_initialized = x11_is_initialized,
    
    .connect = x11_connect,
    .disconnect = x11_disconnect,
    .is_connected = x11_is_connected,
    .get_file_descriptor = x11_get_file_descriptor,
    .flush = x11_flush,
    .sync = x11_sync,
    
    .get_screen_dimensions = x11_get_screen_dimensions,
    .get_screen_count = x11_get_screen_count,
    .get_screen_geometry = x11_get_screen_geometry,
    
    .window_get_geometry = x11_window_get_geometry,
    .window_set_geometry = x11_window_set_geometry,
    .window_move = x11_window_move,
    .window_resize = x11_window_resize,
    .window_move_resize = x11_window_move_resize,
    
    .window_show = x11_window_show,
    .window_hide = x11_window_hide,
    .window_is_visible = x11_window_is_visible,
    .window_map = x11_window_map,
    .window_unmap = x11_window_unmap,
    .window_is_mapped = x11_window_is_mapped,
    
    .window_raise = x11_window_raise,
    .window_lower = x11_window_lower,
    .window_reparent = x11_window_reparent,
    
    .window_focus = x11_window_focus,
    .window_unfocus = x11_window_unfocus,
    .window_is_focused = x11_window_is_focused,
    .window_get_focused = x11_window_get_focused,
    
    .window_set_border_width = x11_window_set_border_width,
    .window_get_border_width = x11_window_get_border_width,
    
    .window_set_title = x11_window_set_title,
    .window_get_title = x11_window_get_title,
    
    .window_supports_protocol = x11_window_supports_protocol,
    .window_close = x11_window_close,
    .window_kill = x11_window_kill,
    
    .window_set_fullscreen = x11_window_set_fullscreen,
    .window_set_urgent = x11_window_set_urgent,
    .window_is_urgent = x11_window_is_urgent,
    
    .poll_event = x11_poll_event,
    .wait_event = x11_wait_event,
    
    .grab_key = x11_grab_key,
    .ungrab_key = x11_ungrab_key,
    .grab_button = x11_grab_button,
    .ungrab_button = x11_ungrab_button,
    .grab_pointer = x11_grab_pointer,
    .ungrab_pointer = x11_ungrab_pointer,
    
    .query_pointer = x11_query_pointer,
    .warp_pointer = x11_warp_pointer,
    
    .atom_get = x11_atom_get,
    .atom_get_name = x11_atom_get_name,
};

const BackendInterface* x11_backend_get_interface(void) {
    return &x11_backend_interface;
}

Display* x11_backend_get_display(void) {
    return x11_state.display;
}

Window x11_backend_get_root(void) {
    return x11_state.root;
}

int x11_backend_get_screen(void) {
    return x11_state.screen;
}
