diff --git a/Makefile b/Makefile index 58f4cff9..03f4ac28 100644 --- a/Makefile +++ b/Makefile @@ -66,7 +66,7 @@ ifeq ($(RGFW_WAYLAND),1) NO_VULKAN = 1 NO_GLES = 0 NO_OSMESA = 0 - LIBS = -D RGFW_WAYLAND relative-pointer-unstable-v1-client-protocol.c xdg-decoration-unstable-v1.c xdg-shell.c -lwayland-cursor -lwayland-client -lxkbcommon + LIBS += -D RGFW_WAYLAND relative-pointer-unstable-v1-client-protocol.c xdg-decoration-unstable-v1.c xdg-shell.c -lwayland-cursor -lwayland-client -lxkbcommon LINK_GL1 = -lEGL -lGL -lwayland-egl endif diff --git a/RGFW.h b/RGFW.h index 4d3d3a87..9d0f8bb0 100644 --- a/RGFW.h +++ b/RGFW.h @@ -399,6 +399,11 @@ int main() { #include /* GLX defs, xlib.h, gl.h */ #endif +/*! (unix) Toggle use of wayland, this will be on by default if you use `RGFW_WAYLAND` (if you don't use RGFW_WAYLAND, you dont' expose WAYLAND functions) + this is mostly used to allow you to force the use of XWayland +*/ +RGFWDEF void RGFW_useWayland(b8 wayland); + /* RGFW_allocator (optional) you can ignore this if you use standard malloc/free @@ -1326,6 +1331,7 @@ typedef RGFW_ENUM(u8, RGFW_mouseIcons) { #ifdef RGFW_IMPLEMENTATION +b8 RGFW_useWaylandBool = 1; #ifdef RGFW_DEBUG #include @@ -2319,7 +2325,7 @@ void RGFW_createOpenGLContext(RGFW_window* win) { #elif defined(RGFW_MACOS) win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType)0); #else - win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.display); + win->src.EGL_display = eglGetDisplay((EGLNativeDisplayType) win->src.wl_display); #endif EGLint major, minor; @@ -2355,6 +2361,13 @@ void RGFW_createOpenGLContext(RGFW_window* win) { RGFW_window_cocoaSetLayer(win, layer); win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) layer, NULL); + #elif defined(RGFW_WINDOWS) + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.hwnd, NULL); + #elif defined(RGFW_WAYLAND) + if (RGFW_useWaylandBool) + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.eglWindow, NULL); + else + win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); #else win->src.EGL_surface = eglCreateWindowSurface(win->src.EGL_display, config, (EGLNativeWindowType) win->src.window, NULL); #endif @@ -2616,2867 +2629,2699 @@ This is where OS specific stuff starts #endif #endif -/* -Start of Linux / Unix defines +/* + + Start of Wayland defines */ -#ifdef RGFW_X11 -#ifndef RGFW_NO_X11_CURSOR -#include -#endif +#ifdef RGFW_WAYLAND +/* +Wayland TODO: +- fix RGFW_keyPressed lock state -#include + RGFW_windowMoved, the window was moved (by the user) + RGFW_windowResized the window was resized (by the user), [on webASM this means the browser was resized] + RGFW_windowRefresh The window content needs to be refreshed -#ifndef RGFW_NO_DPI -#include -#include -#endif + RGFW_DND a file has been dropped into the window + RGFW_DNDInit -#include -#include -#include -#include +- window args: + #define RGFW_windowNoResize the window cannot be resized by the user + #define RGFW_windowAllowDND the window supports drag and drop + #define RGFW_scaleToMonitor scale the window to the screen -#include /* for converting keycode to string */ -#include /* for hiding */ -#include -#include -#include +- other missing functions functions ("TODO wayland") (~30 functions) +- fix buffer rendering weird behavior +*/ +#include +#include +#include +#include +#include +#include +#include +#include -#include /* for data limits (mainly used in drag and drop functions) */ -#include +RGFW_window* RGFW_key_win = NULL; +void RGFW_eventPipe_push(RGFW_window* win, RGFW_event event) { + if (win == NULL) { + win = RGFW_key_win; -#if defined(__linux__) && !defined(RGFW_NO_LINUX) -#include -#endif + if (win == NULL) return; + } -u8 RGFW_mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor}; -/*atoms needed for drag and drop*/ -Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XtextPlain, XtextUriList; -Atom RGFW_XUTF8_STRING = 0; + if (win->src.eventLen >= (i32)(sizeof(win->src.events) / sizeof(win->src.events[0]))) + return; -Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; + win->src.events[win->src.eventLen] = event; + win->src.eventLen += 1; +} -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); - typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); - typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); -#endif -#ifdef RGFW_OPENGL - typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); -#endif +RGFW_event RGFW_eventPipe_pop(RGFW_window* win) { + RGFW_event ev; + ev.type = 0; -#if !defined(RGFW_NO_X11_XI_PRELOAD) - typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); - PFN_XISelectEvents XISelectEventsSRC = NULL; - #define XISelectEvents XISelectEventsSRC + if (win->src.eventLen > -1) + win->src.eventLen -= 1; - void* X11Xihandle = NULL; -#endif + if (win->src.eventLen >= 0) + ev = win->src.events[win->src.eventLen]; -#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL; - PFN_XcursorImageCreate XcursorImageCreateSRC = NULL; - PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL; + return ev; +} - #define XcursorImageLoadCursor XcursorImageLoadCursorSRC - #define XcursorImageCreate XcursorImageCreateSRC - #define XcursorImageDestroy XcursorImageDestroySRC +/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ +#include "xdg-shell.h" +#include "xdg-decoration-unstable-v1.h" - void* X11Cursorhandle = NULL; -#endif +static struct xkb_context *xkb_context; +static struct xkb_keymap *keymap = NULL; +static struct xkb_state *xkb_state = NULL; +enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode; +static struct zxdg_decoration_manager_v1 *decoration_manager = NULL; -u32 RGFW_windowsOpen = 0; +struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL; +struct wl_surface* RGFW_cursor_surface = NULL; +struct wl_cursor_image* RGFW_cursor_image = NULL; -#ifdef RGFW_OPENGL - void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); } -#endif +static void xdg_wm_base_ping_handler(void *data, + struct xdg_wm_base *wm_base, uint32_t serial) +{ + RGFW_UNUSED(data); + xdg_wm_base_pong(wm_base, serial); +} -RGFWDEF void RGFW_init_buffer(RGFW_window* win, void* vi); -void RGFW_init_buffer(RGFW_window* win, void* vi) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) - RGFW_bufferSize = RGFW_getScreenSize(); +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + .ping = xdg_wm_base_ping_handler, +}; - win->buffer = (u8*)RGFW_alloc(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); - win->_flags |= RGFW_BUFFER_ALLOC; +b8 RGFW_wl_configured = 0; +static void xdg_surface_configure_handler(void *data, + struct xdg_surface *xdg_surface, uint32_t serial) +{ + RGFW_UNUSED(data); + xdg_surface_ack_configure(xdg_surface, serial); #ifdef RGFW_DEBUG - printf("RGFW INFO: createing a 4 channel %i by %i buffer\n", RGFW_bufferSize.w, RGFW_bufferSize.h); + printf("Surface configured\n"); #endif + RGFW_wl_configured = 1; +} - #ifdef RGFW_OSMESA - win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, RGFW_bufferSize.w, RGFW_bufferSize.h); +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_configure_handler, +}; + +static void xdg_toplevel_configure_handler(void *data, + struct xdg_toplevel *toplevel, int32_t width, int32_t height, + struct wl_array *states) +{ + RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states); + #ifdef RGFW_DEBUG + fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height); #endif +} - win->src.bitmap = XCreateImage( - win->src.display, XDefaultVisual(win->src.display, vi->screen), - vi->depth, - ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h, - 32, 0 - ); +static void xdg_toplevel_close_handler(void *data, + struct xdg_toplevel *toplevel) +{ + RGFW_UNUSED(data); + RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel); + if (win == NULL) + win = RGFW_key_win; - win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); + RGFW_event ev; + ev.type = RGFW_quit; - #else - RGFW_UNUSED(win); /*!< if buffer rendering is not being used */ - RGFW_UNUSED(vi); + RGFW_eventPipe_push(win, ev); + + RGFW_windowQuitCallback(win); +} + +static void shm_format_handler(void *data, + struct wl_shm *shm, uint32_t format) +{ + RGFW_UNUSED(data); RGFW_UNUSED(shm); + #ifdef RGFW_DEBUG + fprintf(stderr, "Format %d\n", format); #endif } -#define RGFW_LOAD_ATOM(name) \ - static Atom name = 0; \ - if (name == 0) name = XInternAtom(RGFW_root->src.display, #name, False); +static const struct wl_shm_listener shm_listener = { + .format = shm_format_handler, +}; -void RGFW_window_setBorder(RGFW_window* win, u8 border) { - RGFW_LOAD_ATOM(_MOTIF_WM_HINTS); +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_configure_handler, + .close = xdg_toplevel_close_handler, +}; - struct __x11WindowHints { - unsigned long flags, functions, decorations, status; - long input_mode; - } hints; - hints.flags = (1L << 1); - hints.decorations = border; +RGFW_window* RGFW_mouse_win = NULL; - XChangeProperty( - win->src.display, win->src.window, - _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, - 32, PropModeReplace, (u8*)&hints, 5 - ); -} +static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y); + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + RGFW_mouse_win = win; -void RGFW_releaseCursor(RGFW_window* win) { - XUngrabPointer(win->src.display, CurrentTime); + RGFW_event ev; + ev.type = RGFW_mouseEnter; + ev.point = win->event.point; - /* disable raw input */ - unsigned char mask[] = { 0 }; - XIEventMask em; - em.deviceid = XIAllMasterDevices; - em.mask_len = sizeof(mask); - em.mask = mask; + RGFW_eventPipe_push(win, ev); - XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); + RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_TRUE); } +static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface); + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + if (RGFW_mouse_win == win) + RGFW_mouse_win = NULL; -void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { - /* enable raw input */ - unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; - XISetMask(mask, XI_RawMotion); + RGFW_event ev; + ev.type = RGFW_mouseLeave; + ev.point = win->event.point; + RGFW_eventPipe_push(win, ev); - XIEventMask em; - em.deviceid = XIAllMasterDevices; - em.mask_len = sizeof(mask); - em.mask = mask; + RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_FALSE); +} +static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y); - XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); + RGFW_ASSERT(RGFW_mouse_win != NULL); - XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); - RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2))); + RGFW_event ev; + ev.type = RGFW_mousePosChanged; + ev.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)); + RGFW_eventPipe_push(RGFW_mouse_win, ev); + + RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y))); } +static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial); + RGFW_ASSERT(RGFW_mouse_win != NULL); -#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL) -#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) name##SRC = (PFN_##name)(void*)dlsym(proc, #name) + u32 b = (button - 0x110) + 1; -RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { - #ifdef RGFW_USE_XDL - XDL_init(); - #endif - - #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) - #if defined(__CYGWIN__) - RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so"); - #elif defined(__OpenBSD__) || defined(__NetBSD__) - RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so"); - #else - RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1"); - #endif - RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate); - RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy); - RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor); - #endif - - #if !defined(RGFW_NO_X11_XI_PRELOAD) - #if defined(__CYGWIN__) - RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so"); - #elif defined(__OpenBSD__) || defined(__NetBSD__) - RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so"); - #else - RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6"); - #endif - RGFW_PROC_DEF(X11Xihandle, XISelectEvents); - #endif + /* flip right and middle button codes */ + if (b == 2) b = 3; + else if (b == 3) b = 2; - XInitThreads(); /*!< init X11 threading*/ + RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; + RGFW_mouseButtons[b].current = state; - if (flags & RGFW_windowOpenglSoftware) - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); + RGFW_event ev; + ev.type = RGFW_mouseButtonPressed + state; + ev.button = b; + RGFW_eventPipe_push(RGFW_mouse_win, ev); - RGFW_window_basic_init(win, rect, flags); + RGFW_mouseButtonCallback(RGFW_mouse_win, b, 0, state); +} +static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { + RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis); + RGFW_ASSERT(RGFW_mouse_win != NULL); - u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted*/ + double scroll = wl_fixed_to_double(value); - #ifdef RGFW_OPENGL - u32* visual_attribs = (u32*)RGFW_initFormatAttribs(flags & RGFW_windowOpenglSoftware); - i32 fbcount; - GLXFBConfig* fbc = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount); + RGFW_event ev; + ev.type = RGFW_mouseButtonPressed; + ev.button = RGFW_mouseScrollUp + (scroll < 0); + RGFW_eventPipe_push(RGFW_mouse_win, ev); - i32 best_fbc = -1; + RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1); +} - if (fbcount == 0) { - #ifdef RGFW_DEBUG - fprintf(stderr, "Failed to find any valid GLX visual configs\n"); - #endif - return NULL; - } +void RGFW_doNothing(void) { } +static struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing}; - u32 i; - for (i = 0; i < (u32)fbcount; i++) { - XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, fbc[i]); - if (vi == NULL) - continue; +static void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format); - XFree(vi); + char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); + xkb_keymap_unref (keymap); + keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); - i32 samp_buf, samples; - glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); - glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLES, &samples); + munmap (keymap_string, size); + close (fd); + xkb_state_unref (xkb_state); + xkb_state = xkb_state_new (keymap); +} +static void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys); - if ((!(flags & RGFW_windowTransparent) || vi->depth == 32) && - (best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) { - best_fbc = i; - } - } + RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface); - if (best_fbc == -1) { - #ifdef RGFW_DEBUG - fprintf(stderr, "Failed to get a valid GLX visual\n"); - #endif - return NULL; - } + RGFW_event ev; + ev.type = RGFW_focusIn; + ev.inFocus = RGFW_TRUE; + RGFW_key_win->event.inFocus = RGFW_TRUE; - GLXFBConfig bestFbc = fbc[best_fbc]; + RGFW_eventPipe_push((RGFW_window*)RGFW_mouse_win, ev); - /* Get a visual */ - XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, bestFbc); + RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); +} +static void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); - XFree(fbc); - #else - XVisualInfo viNorm; + RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); + if (RGFW_key_win == win) + RGFW_key_win = NULL; - viNorm.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display)); + RGFW_event ev; + ev.type = RGFW_focusOut; + ev.inFocus = RGFW_FALSE; + win->event.inFocus = RGFW_FALSE; + RGFW_eventPipe_push(win, ev); - viNorm.depth = 0; - XVisualInfo* vi = &viNorm; + RGFW_focusCallback(win, RGFW_FALSE); +} +static void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); - XMatchVisualInfo(win->src.display, DefaultScreen(win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/ - #endif - /* make X window attrubutes*/ - XSetWindowAttributes swa; - Colormap cmap; + RGFW_ASSERT(RGFW_key_win != NULL); - swa.colormap = cmap = XCreateColormap(win->src.display, - DefaultRootWindow(win->src.display), - vi->visual, AllocNone); + xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8); - swa.background_pixmap = None; - swa.border_pixel = 0; - swa.event_mask = event_mask; + u32 RGFW_key = RGFW_apiKeyToRGFW(key + 8); + RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current; + RGFW_keyboard[RGFW_key].current = state; + RGFW_event ev; + ev.type = RGFW_keyPressed + state; + ev.key = RGFW_key; + ev.keyChar = (u8)keysym; - swa.background_pixel = 0; + ev.repeat = RGFW_isHeld(RGFW_key_win, RGFW_key); + RGFW_eventPipe_push(RGFW_key_win, ev); - /* create the window*/ - win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), win->r.x, win->r.y, win->r.w, win->r.h, - 0, vi->depth, InputOutput, vi->visual, - CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); + RGFW_updateKeyMods(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2")); - XFreeColors(win->src.display, cmap, NULL, 0, 0); + RGFW_keyCallback(RGFW_key_win, RGFW_key, (u8)keysym, RGFW_key_win->event.keyMod, state); +} +static void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { + RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); + xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} +static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void*)&RGFW_doNothing}; - #ifdef RGFW_OPENGL - XFree(vi); - #endif +static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { + RGFW_UNUSED(data); - // In your .desktop app, if you set the property - // StartupWMClass=RGFW that will assoicate the launcher icon - // with your application - robrohan + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + struct wl_pointer *pointer = wl_seat_get_pointer (seat); + wl_pointer_add_listener (pointer, &pointer_listener, NULL); + } + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat); + wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL); + } +} +static struct wl_seat_listener seat_listener = {&seat_capabilities, (void*)&RGFW_doNothing}; - if (RGFW_className == NULL) - RGFW_className = (char*)name; - - XClassHint hint; - hint.res_class = (char*)RGFW_className; - hint.res_name = (char*)name; // just use the window name as the app name - XSetClassHint(win->src.display, win->src.window, &hint); +static void wl_global_registry_handler(void *data, + struct wl_registry *registry, uint32_t id, const char *interface, + uint32_t version) +{ + RGFW_window* win = (RGFW_window*)data; + RGFW_UNUSED(version); + if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) { + win->src.compositor = wl_registry_bind(registry, + id, &wl_compositor_interface, 4); + } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) { + win->src.xdg_wm_base = wl_registry_bind(registry, + id, &xdg_wm_base_interface, 1); + } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) { + decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); + } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) { + win->src.shm = wl_registry_bind(registry, + id, &wl_shm_interface, 1); + wl_shm_add_listener(win->src.shm, &shm_listener, NULL); + } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) { + win->src.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); + wl_seat_add_listener(win->src.seat, &seat_listener, NULL); + } - if ((flags & RGFW_windowNoInitAPI) == 0) { - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) /* This is the second part of setting up opengl. This is where we ask OpenGL for a specific version. */ - i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; - context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; - if (RGFW_profile == RGFW_glCore) - context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; - else - context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + else { + #ifdef RGFW_DEBUG + printf("did not register %s\n", interface); + return; + #endif + } - if (RGFW_majorVersion || RGFW_minorVersion) { - context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; - context_attribs[3] = RGFW_majorVersion; - context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; - context_attribs[5] = RGFW_minorVersion; - } + #ifdef RGFW_DEBUG + printf("registered %s\n", interface); + #endif +} - glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; - glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) - glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); +static void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); } +static const struct wl_registry_listener registry_listener = { + .global = wl_global_registry_handler, + .global_remove = wl_global_registry_remove, +}; - GLXContext ctx = NULL; +static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) { + switch (mode) { + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: + return "client-side decorations"; + case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: + return "server-side decorations"; + } + abort(); +} - if (RGFW_root != NULL) - ctx = RGFW_root->src.ctx; - win->src.ctx = glXCreateContextAttribsARB(win->src.display, bestFbc, ctx, True, context_attribs); - #endif +static void decoration_handle_configure(void *data, + struct zxdg_toplevel_decoration_v1 *decoration, + enum zxdg_toplevel_decoration_v1_mode mode) { + RGFW_UNUSED(data); RGFW_UNUSED(decoration); + #ifdef RGFW_DEBUG + printf("Using %s\n", get_mode_name(mode)); + #endif + RGFW_current_mode = mode; +} - RGFW_init_buffer(win, vi); +static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { + .configure = decoration_handle_configure, +}; + +static void randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; } +} - #ifndef RGFW_NO_MONITOR - if (flags & RGFW_windowScaleToMonitor) - RGFW_window_scaleToMonitor(win); - #endif +size_t wl_stringlen(char* name) { + size_t i = 0; + for (i; name[i]; i++); + return i; +} - if (flags & RGFW_windowCenter) { - RGFW_area screenR = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); - } +static int anonymous_shm_open(void) { + char name[] = "/RGFW-wayland-XXXXXX"; + int retries = 100; - if (flags & RGFW_windowNoResize) { /* make it so the user can't resize the window*/ - XSizeHints sh; - sh.flags = (1L << 4) | (1L << 5); - sh.min_width = sh.max_width = win->r.w; - sh.min_height = sh.max_height = win->r.h; + do { + randname(name + wl_stringlen(name) - 6); - XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); + --retries; + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); - win->_flags |= RGFW_windowNoResize; - } + return -1; +} - if (flags & RGFW_windowNoBorder) { - RGFW_window_setBorder(win, 0); +int create_shm_file(off_t size) { + int fd = anonymous_shm_open(); + if (fd < 0) { + return fd; } - XSelectInput(win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want*/ - - /* make it so the user can't close the window until the program does*/ - if (wm_delete_window == 0) { - wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False); - RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False); - RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False); + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; } - XSetWMProtocols(win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); + return fd; +} - /* connect the context to the window*/ - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - if ((flags & RGFW_windowNoInitAPI) == 0) - glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); +static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { + RGFW_UNUSED(data); RGFW_UNUSED(cb); RGFW_UNUSED(time); + + #ifdef RGFW_BUFFER + RGFW_window* win = (RGFW_window*)data; + if ((win->_flags & RGFW_NO_CPU_RENDER)) + return; + + wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); + wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h); + wl_surface_commit(win->src.surface); #endif +} - /* set the background*/ - RGFW_window_setName(win, name); +static const struct wl_callback_listener wl_surface_frame_listener = { + .done = wl_surface_frame_done, +}; - XMapWindow(win->src.display, (Drawable) win->src.window); /* draw the window*/ - XMoveWindow(win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords*/ +void RGFW_useWayland(b8 wayland) { RGFW_useWaylandBool = wayland; } +#define RGFW_GOTO_WAYLAND() if (RGFW_useWaylandBool) goto wayland - if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */ - win->_flags |= RGFW_windowAllowDND; +#else /* RGFW_WAYLAND */ +#define RGFW_GOTO_WAYLAND() +void RGFW_useWayland(b8 wayland) { RGFW_UNUSED(wayland); } +#endif /* no RGFW_WAYLAND */ - XdndTypeList = XInternAtom(win->src.display, "XdndTypeList", False); - XdndSelection = XInternAtom(win->src.display, "XdndSelection", False); +/* + End of Wayland defines +*/ - /* client messages */ - XdndEnter = XInternAtom(win->src.display, "XdndEnter", False); - XdndPosition = XInternAtom(win->src.display, "XdndPosition", False); - XdndStatus = XInternAtom(win->src.display, "XdndStatus", False); - XdndLeave = XInternAtom(win->src.display, "XdndLeave", False); - XdndDrop = XInternAtom(win->src.display, "XdndDrop", False); - XdndFinished = XInternAtom(win->src.display, "XdndFinished", False); +/* - /* actions */ - XdndActionCopy = XInternAtom(win->src.display, "XdndActionCopy", False); - XtextUriList = XInternAtom(win->src.display, "text/uri-list", False); - XtextPlain = XInternAtom(win->src.display, "text/plain", False); +Start of Linux / Unix defines - XdndAware = XInternAtom(win->src.display, "XdndAware", False); - const u8 version = 5; - XChangeProperty(win->src.display, win->src.window, - XdndAware, 4, 32, - PropModeReplace, &version, 1); /*!< turns on drag and drop */ - } +*/ - #ifdef RGFW_EGL - if ((flags & RGFW_windowNoInitAPI) == 0) - RGFW_createOpenGLContext(win); - #endif +#ifdef RGFW_UNIX +#if !defined(RGFW_NO_X11_CURSOR) && defined(RGFW_X11) +#include +#endif - RGFW_window_setMouseDefault(win); +#include - RGFW_windowsOpen++; +#ifndef RGFW_NO_DPI +#include +#include +#endif - #ifdef RGFW_DEBUG - printf("RGFW INFO: a window with a rect of {%i, %i, %i, %i} \n", win->r.x, win->r.y, win->r.w, win->r.h); - #endif +#include +#include +#include +#include - return win; /*return newly created window*/ -} +#include /* for converting keycode to string */ +#include /* for hiding */ +#include +#include +#include -RGFW_area RGFW_getScreenSize(void) { - RGFW_ASSERT(RGFW_root != NULL); +#include /* for data limits (mainly used in drag and drop functions) */ +#include - Screen* scrn = DefaultScreenOfDisplay(RGFW_root->src.display); - return RGFW_AREA(scrn->width, scrn->height); -} -RGFW_point RGFW_getGlobalMousePoint(void) { - RGFW_ASSERT(RGFW_root != NULL); +#if defined(__linux__) && !defined(RGFW_NO_LINUX) +#include +#endif - RGFW_point RGFWMouse; +u8 RGFW_mouseIconSrc[] = { XC_arrow, XC_left_ptr, XC_xterm, XC_crosshair, XC_hand2, XC_sb_h_double_arrow, XC_sb_v_double_arrow, XC_bottom_left_corner, XC_bottom_right_corner, XC_fleur, XC_X_cursor}; +/*atoms needed for drag and drop*/ +Atom XdndAware, XdndTypeList, XdndSelection, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished, XdndActionCopy, XtextPlain, XtextUriList; +Atom RGFW_XUTF8_STRING = 0; - i32 x, y; - u32 z; - Window window1, window2; - XQueryPointer(RGFW_root->src.display, XDefaultRootWindow(RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z); +Atom wm_delete_window = 0, RGFW_XCLIPBOARD = 0; - return RGFWMouse; -} +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + typedef XcursorImage* (*PFN_XcursorImageCreate)(int, int); + typedef void (*PFN_XcursorImageDestroy)(XcursorImage*); + typedef Cursor(*PFN_XcursorImageLoadCursor)(Display*, const XcursorImage*); +#endif +#ifdef RGFW_OPENGL + typedef GLXContext(*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); +#endif -RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - RGFW_ASSERT(win != NULL); +#if !defined(RGFW_NO_X11_XI_PRELOAD) + typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); + PFN_XISelectEvents XISelectEventsSRC = NULL; + #define XISelectEvents XISelectEventsSRC - RGFW_point RGFWMouse; + void* X11Xihandle = NULL; +#endif - i32 x, y; - u32 z; - Window window1, window2; - XQueryPointer(win->src.display, win->src.window, &window1, &window2, &x, &y, &RGFWMouse.x, &RGFWMouse.y, &z); +#if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + PFN_XcursorImageLoadCursor XcursorImageLoadCursorSRC = NULL; + PFN_XcursorImageCreate XcursorImageCreateSRC = NULL; + PFN_XcursorImageDestroy XcursorImageDestroySRC = NULL; - return RGFWMouse; -} + #define XcursorImageLoadCursor XcursorImageLoadCursorSRC + #define XcursorImageCreate XcursorImageCreateSRC + #define XcursorImageDestroy XcursorImageDestroySRC -char* RGFW_strtok(char* str, const char* delimStr) { - static char* static_str = NULL; + void* X11Cursorhandle = NULL; +#endif - if (str != NULL) - static_str = str; +u32 RGFW_windowsOpen = 0; - if (static_str == NULL) { - return NULL; - } +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) + void* RGFW_getProcAddress(const char* procname) { return (void*) glXGetProcAddress((GLubyte*) procname); } +#endif - while (*static_str != '\0') { - b8 delim = 0; - for (const char* d = delimStr; *d != '\0'; d++) { - if (*static_str == *d) { - delim = 1; - break; - } - } - if (!delim) - break; - static_str++; - } +RGFWDEF void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi); +void RGFW_init_buffer(RGFW_window* win, XVisualInfo* vi) { + RGFW_GOTO_WAYLAND(); - if (*static_str == '\0') - return NULL; + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + #ifdef RGFW_X11 + if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) + RGFW_bufferSize = RGFW_getScreenSize(); - char* token_start = static_str; - while (*static_str != '\0') { - int delim = 0; - for (const char* d = delimStr; *d != '\0'; d++) { - if (*static_str == *d) { - delim = 1; - break; - } - } + win->buffer = (u8*)RGFW_alloc(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); + win->_flags |= RGFW_BUFFER_ALLOC; - if (delim) { - *static_str = '\0'; - static_str++; - break; - } - static_str++; - } - - return token_start; -} -int xAxis = 0, yAxis = 0; - -RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - if (win->event.type == 0) - RGFW_resetKey(); + #ifdef RGFW_DEBUG + printf("RGFW INFO: createing a 4 channel %i by %i buffer\n", RGFW_bufferSize.w, RGFW_bufferSize.h); + #endif - if (win->event.type == RGFW_quit) { - return NULL; - } + #ifdef RGFW_OSMESA + win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, RGFW_bufferSize.w, RGFW_bufferSize.h); + #endif - win->event.type = 0; + win->src.bitmap = XCreateImage( + win->src.display, XDefaultVisual(win->src.display, vi->screen), + vi->depth, + ZPixmap, 0, NULL, RGFW_bufferSize.w, RGFW_bufferSize.h, + 32, 0 + ); - #if defined(__linux__) && !defined(RGFW_NO_LINUX) - if (RGFW_linux_updateGamepad(win)) return &win->event; + win->src.gc = XCreateGC(win->src.display, win->src.window, 0, NULL); #endif + #ifdef RGFW_WAYLAND + wayland: + size_t size = win->r.w * win->r.h * 4; + int fd = create_shm_file(size); + if (fd < 0) { + fprintf(stderr, "Failed to create a buffer. size: %ld\n", size); + exit(1); + } + + if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) + RGFW_bufferSize = RGFW_getScreenSize(); - XPending(win->src.display); + win->buffer = (u8*)RGFW_alloc(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); + win->src.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (win->src.buffer == MAP_FAILED) { + fprintf(stderr, "mmap failed!\n"); + close(fd); + exit(1); + } - XEvent E; /*!< raw X11 event */ + win->_flags |= RGFW_BUFFER_ALLOC; - /* if there is no unread qued events, get a new one */ - if ((QLength(win->src.display) || XEventsQueued(win->src.display, QueuedAlready) + XEventsQueued(win->src.display, QueuedAfterReading)) - && win->event.type != RGFW_quit - ) - XNextEvent(win->src.display, &E); - else { - return NULL; - } + struct wl_shm_pool* pool = wl_shm_create_pool(win->src.shm, fd, size); + win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4, + WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy(pool); - win->event.type = 0; + close(fd); - /* xdnd data */ - Window source = 0; - long version = 0; - i32 format = 0; - - XEvent reply = { ClientMessage }; + wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); + wl_surface_commit(win->src.surface); - switch (E.type) { - case KeyPress: - case KeyRelease: { - win->event.repeat = RGFW_FALSE; - /* check if it's a real key release */ - if (E.type == KeyRelease && XEventsQueued(win->src.display, QueuedAfterReading)) { /* get next event if there is one*/ - XEvent NE; - XPeekEvent(win->src.display, &NE); + u8 color[] = {0x00, 0x00, 0x00, 0xFF}; - if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/ - win->event.repeat = RGFW_TRUE; + size_t i; + for (i = 0; i < RGFW_bufferSize.w * RGFW_bufferSize.h * 4; i += 4) { + RGFW_MEMCPY(&win->buffer[i], color, 4); } + + RGFW_MEMCPY(win->src.buffer, win->buffer, win->r.w * win->r.h * 4); - /* set event key data */ - win->event.key = RGFW_apiKeyToRGFW(E.xkey.keycode); - - KeySym sym = (KeySym)XkbKeycodeToKeysym(win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); + #if defined(RGFW_OSMESA) + win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); + OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, RGFW_bufferSize.w, RGFW_bufferSize.h); + #endif + #endif + #else + wayland: + RGFW_UNUSED(win); + RGFW_UNUSED(vi); + #endif +} - if ((E.xkey.state & LockMask) && sym >= XK_a && sym <= XK_z) - sym = (E.xkey.state & ShiftMask) ? sym + 32 : sym - 32; - if ((u8)sym != (u32)sym) - sym = 0; +#define RGFW_LOAD_ATOM(name) \ + static Atom name = 0; \ + if (name == 0) name = XInternAtom(RGFW_root->src.display, #name, False); - win->event.keyChar = (u8)sym; +void RGFW_window_setBorder(RGFW_window* win, u8 border) { + RGFW_GOTO_WAYLAND(); + #ifdef RGFW_X11 + RGFW_LOAD_ATOM(_MOTIF_WM_HINTS); - RGFW_keyboard[win->event.key].prev = RGFW_isPressed(win, win->event.key); + struct __x11WindowHints { + unsigned long flags, functions, decorations, status; + long input_mode; + } hints; + hints.flags = (1L << 1); + hints.decorations = border; - /* get keystate data */ - win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; + XChangeProperty( + win->src.display, win->src.window, + _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, + 32, PropModeReplace, (u8*)&hints, 5 + ); + #endif + #ifdef RGFW_WAYLAND + wayland: + #endif +} - XKeyboardState keystate; - XGetKeyboardControl(win->src.display, &keystate); +void RGFW_releaseCursor(RGFW_window* win) { +RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + XUngrabPointer(win->src.display, CurrentTime); - RGFW_keyboard[win->event.key].current = (E.type == KeyPress); - RGFW_updateKeyMods(win, (keystate.led_mask & 1), (keystate.led_mask & 2)); - RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, (E.type == KeyPress)); - break; - } - case ButtonPress: - case ButtonRelease: - win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match + /* disable raw input */ + unsigned char mask[] = { 0 }; + XIEventMask em; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; - win->event.button = E.xbutton.button; - switch(win->event.button) { - case RGFW_mouseScrollUp: - win->event.scroll = 1; - break; - case RGFW_mouseScrollDown: - win->event.scroll = -1; - break; - default: break; - } + XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); +#endif +#ifdef RGFW_WAYLAND + wayland: +#endif +} - RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; +void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { +RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + /* enable raw input */ + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; + XISetMask(mask, XI_RawMotion); - if (win->event.repeat == RGFW_FALSE) - win->event.repeat = RGFW_isPressed(win, win->event.key); + XIEventMask em; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; - RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress); - RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress)); - break; + XISelectEvents(win->src.display, XDefaultRootWindow(win->src.display), &em, 1); - case MotionNotify: - win->event.point.x = E.xmotion.x; - win->event.point.y = E.xmotion.y; + XGrabPointer(win->src.display, win->src.window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (i32)(r.w / 2), win->r.y + (i32)(r.h / 2))); +#endif +#ifdef RGFW_WAYLAND + wayland: +#endif +} - if ((win->_flags & RGFW_HOLD_MOUSE)) { - win->event.point.y = E.xmotion.y; +#define RGFW_LOAD_LIBRARY(x, lib) if (x == NULL) x = dlopen(lib, RTLD_LAZY | RTLD_LOCAL) +#define RGFW_PROC_DEF(proc, name) if (name##SRC == NULL && proc != NULL) name##SRC = (PFN_##name)(void*)dlsym(proc, #name) - win->event.point.x = win->event.point.x - win->_lastMousePoint.x; - win->event.point.y = win->event.point.y - win->_lastMousePoint.y; - } +RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { + #ifdef RGFW_USE_XDL + XDL_init(); + #endif - win->_lastMousePoint = RGFW_POINT(E.xmotion.x, E.xmotion.y); + #if !defined(RGFW_NO_X11_CURSOR) && !defined(RGFW_NO_X11_CURSOR_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor-1.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so"); + #else + RGFW_LOAD_LIBRARY(X11Cursorhandle, "libXcursor.so.1"); + #endif + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageCreate); + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageDestroy); + RGFW_PROC_DEF(X11Cursorhandle, XcursorImageLoadCursor); + #endif - win->event.type = RGFW_mousePosChanged; - RGFW_mousePosCallback(win, win->event.point); - break; + #if !defined(RGFW_NO_X11_XI_PRELOAD) + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi-6.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so"); + #else + RGFW_LOAD_LIBRARY(X11Xihandle, "libXi.so.6"); + #endif + RGFW_PROC_DEF(X11Xihandle, XISelectEvents); + #endif - case GenericEvent: { - /* MotionNotify is used for mouse events if the mouse isn't held */ - if (!(win->_flags & RGFW_HOLD_MOUSE)) { - XFreeEventData(win->src.display, &E.xcookie); - break; - } + XInitThreads(); /*!< init X11 threading*/ - XGetEventData(win->src.display, &E.xcookie); - if (E.xcookie.evtype == XI_RawMotion) { - XIRawEvent *raw = (XIRawEvent *)E.xcookie.data; - if (raw->valuators.mask_len == 0) { - XFreeEventData(win->src.display, &E.xcookie); - break; - } + if (flags & RGFW_windowOpenglSoftware) + setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); - double deltaX = 0.0f; - double deltaY = 0.0f; + RGFW_window_basic_init(win, rect, flags); - /* check if relative motion data exists where we think it does */ - if (XIMaskIsSet(raw->valuators.mask, 0) != 0) - deltaX += raw->raw_values[0]; - if (XIMaskIsSet(raw->valuators.mask, 1) != 0) - deltaY += raw->raw_values[1]; +#ifdef RGFW_WAYLAND + win->src.compositor = NULL; +#endif + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + u64 event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask | EnterWindowMask | ExposureMask; /*!< X11 events accepted*/ - win->event.point = RGFW_POINT((i32)deltaX, (i32)deltaY); + #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) + u32* visual_attribs = (u32*)RGFW_initFormatAttribs(flags & RGFW_windowOpenglSoftware); + i32 fbcount; + GLXFBConfig* fbc = glXChooseFBConfig(win->src.display, DefaultScreen(win->src.display), (i32*) visual_attribs, &fbcount); - RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); + i32 best_fbc = -1; - win->event.type = RGFW_mousePosChanged; - RGFW_mousePosCallback(win, win->event.point); + if (fbcount == 0) { + #ifdef RGFW_DEBUG + fprintf(stderr, "Failed to find any valid GLX visual configs\n"); + #endif + return NULL; } - XFreeEventData(win->src.display, &E.xcookie); - break; - } + u32 i; + for (i = 0; i < (u32)fbcount; i++) { + XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, fbc[i]); + if (vi == NULL) + continue; - case Expose: - win->event.type = RGFW_windowRefresh; - RGFW_windowRefreshCallback(win); - break; + XFree(vi); - case ClientMessage: { - /* if the client closed the window*/ - if (E.xclient.data.l[0] == (long)wm_delete_window) { - win->event.type = RGFW_quit; - RGFW_windowQuitCallback(win); - break; + i32 samp_buf, samples; + glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(win->src.display, fbc[i], GLX_SAMPLES, &samples); + + if ((!(flags & RGFW_windowTransparent) || vi->depth == 32) && + (best_fbc < 0 || samp_buf) && (samples == RGFW_SAMPLES || best_fbc == -1)) { + best_fbc = i; + } } - for (size_t i = 0; i < win->event.droppedFilesCount; i++) { - win->event.droppedFiles[i][0] = '\0'; + if (best_fbc == -1) { + #ifdef RGFW_DEBUG + fprintf(stderr, "Failed to get a valid GLX visual\n"); + #endif + return NULL; } - win->event.droppedFilesCount = 0; - if ((win->_flags & RGFW_windowAllowDND) == 0) - break; + GLXFBConfig bestFbc = fbc[best_fbc]; - reply.xclient.window = source; - reply.xclient.format = 32; - reply.xclient.data.l[0] = (long)win->src.window; - reply.xclient.data.l[1] = 0; - reply.xclient.data.l[2] = None; + /* Get a visual */ + XVisualInfo* vi = glXGetVisualFromFBConfig(win->src.display, bestFbc); - if (E.xclient.message_type == XdndEnter) { - if (version > 5) - break; + XFree(fbc); + #else + XVisualInfo viNorm; - unsigned long count; - Atom* formats; - Atom real_formats[6]; - Bool list = E.xclient.data.l[1] & 1; + viNorm.visual = DefaultVisual(win->src.display, DefaultScreen(win->src.display)); - source = E.xclient.data.l[0]; - version = E.xclient.data.l[1] >> 24; - format = None; + viNorm.depth = 0; + XVisualInfo* vi = &viNorm; - if (list) { - Atom actualType; - i32 actualFormat; - unsigned long bytesAfter; + XMatchVisualInfo(win->src.display, DefaultScreen(win->src.display), 32, TrueColor, vi); /*!< for RGBA backgrounds*/ + #endif + /* make X window attrubutes*/ + XSetWindowAttributes swa; + Colormap cmap; - XGetWindowProperty( - win->src.display, source, XdndTypeList, - 0, LONG_MAX, False, 4, - &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats - ); - } else { - count = 0; + swa.colormap = cmap = XCreateColormap(win->src.display, + DefaultRootWindow(win->src.display), + vi->visual, AllocNone); - for (size_t i = 2; i < 5; i++) { - Window format = E.xclient.data.l[i]; - if (format != None) { - real_formats[count] = format; - count += 1; - } - } + swa.background_pixmap = None; + swa.border_pixel = 0; + swa.event_mask = event_mask; - formats = real_formats; - } + swa.background_pixel = 0; - for (size_t i = 0; i < count; i++) { - if (formats[i] == XtextUriList || formats[i] == XtextPlain) { - format = (int)formats[i]; - break; - } - } + /* create the window*/ + win->src.window = XCreateWindow(win->src.display, DefaultRootWindow(win->src.display), win->r.x, win->r.y, win->r.w, win->r.h, + 0, vi->depth, InputOutput, vi->visual, + CWColormap | CWBorderPixel | CWBackPixel | CWEventMask, &swa); - if (list) { - XFree(formats); - } + XFreeColors(win->src.display, cmap, NULL, 0, 0); - break; - } + #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) + XFree(vi); + #endif + + // In your .desktop app, if you set the property + // StartupWMClass=RGFW that will assoicate the launcher icon + // with your application - robrohan - if (E.xclient.message_type == XdndPosition) { - const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff; - const i32 yabs = (E.xclient.data.l[2]) & 0xffff; - Window dummy; - i32 xpos, ypos; + if (RGFW_className == NULL) + RGFW_className = (char*)name; + + XClassHint hint; + hint.res_class = (char*)RGFW_className; + hint.res_name = (char*)name; // just use the window name as the app name + XSetClassHint(win->src.display, win->src.window, &hint); - if (version > 5) - break; + if ((flags & RGFW_windowNoInitAPI) == 0) { + #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) /* This is the second part of setting up opengl. This is where we ask OpenGL for a specific version. */ + i32 context_attribs[7] = { 0, 0, 0, 0, 0, 0, 0 }; + context_attribs[0] = GLX_CONTEXT_PROFILE_MASK_ARB; + if (RGFW_profile == RGFW_glCore) + context_attribs[1] = GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + else + context_attribs[1] = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - XTranslateCoordinates( - win->src.display, XDefaultRootWindow(win->src.display), win->src.window, - xabs, yabs, &xpos, &ypos, &dummy - ); + if (RGFW_majorVersion || RGFW_minorVersion) { + context_attribs[2] = GLX_CONTEXT_MAJOR_VERSION_ARB; + context_attribs[3] = RGFW_majorVersion; + context_attribs[4] = GLX_CONTEXT_MINOR_VERSION_ARB; + context_attribs[5] = RGFW_minorVersion; + } - win->event.point.x = xpos; - win->event.point.y = ypos; + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) + glXGetProcAddressARB((GLubyte*) "glXCreateContextAttribsARB"); - reply.xclient.window = source; - reply.xclient.message_type = XdndStatus; + GLXContext ctx = NULL; - if (format) { - reply.xclient.data.l[1] = 1; - if (version >= 2) - reply.xclient.data.l[4] = (long)XdndActionCopy; - } + if (RGFW_root != NULL) + ctx = RGFW_root->src.ctx; - XSendEvent(win->src.display, source, False, NoEventMask, &reply); - XFlush(win->src.display); - break; - } + win->src.ctx = glXCreateContextAttribsARB(win->src.display, bestFbc, ctx, True, context_attribs); + #endif - if (E.xclient.message_type != XdndDrop) - break; + RGFW_init_buffer(win, vi); + } - if (version > 5) - break; + #ifndef RGFW_NO_MONITOR + if (flags & RGFW_windowScaleToMonitor) + RGFW_window_scaleToMonitor(win); + #endif - win->event.type = RGFW_DNDInit; + if (flags & RGFW_windowCenter) { + RGFW_area screenR = RGFW_getScreenSize(); + RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); + } - if (format) { - Time time = (version >= 1) - ? (Time)E.xclient.data.l[2] - : CurrentTime; + if (flags & RGFW_windowNoResize) { /* make it so the user can't resize the window*/ + XSizeHints sh; + sh.flags = (1L << 4) | (1L << 5); + sh.min_width = sh.max_width = win->r.w; + sh.min_height = sh.max_height = win->r.h; - XConvertSelection( - win->src.display, XdndSelection, (Atom)format, - XdndSelection, win->src.window, time - ); - } else if (version >= 2) { - XEvent reply = { ClientMessage }; + XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); - XSendEvent(win->src.display, source, False, NoEventMask, &reply); - XFlush(win->src.display); - } + win->_flags |= RGFW_windowNoResize; + } - RGFW_dndInitCallback(win, win->event.point); - } break; - case SelectionNotify: { - /* this is only for checking for xdnd drops */ - if (E.xselection.property != XdndSelection || !(win->_flags & RGFW_windowAllowDND)) - break; + if (flags & RGFW_windowNoBorder) { + RGFW_window_setBorder(win, 0); + } - char* data; - unsigned long result; + XSelectInput(win->src.display, (Drawable) win->src.window, event_mask); /*!< tell X11 what events we want*/ - Atom actualType; - i32 actualFormat; - unsigned long bytesAfter; + /* make it so the user can't close the window until the program does*/ + if (wm_delete_window == 0) { + wm_delete_window = XInternAtom(win->src.display, "WM_DELETE_WINDOW", False); + RGFW_XUTF8_STRING = XInternAtom(win->src.display, "UTF8_STRING", False); + RGFW_XCLIPBOARD = XInternAtom(win->src.display, "CLIPBOARD", False); + } - XGetWindowProperty(win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); + XSetWMProtocols(win->src.display, (Drawable) win->src.window, &wm_delete_window, 1); - if (result == 0) - break; - - const char* prefix = (const char*)"file://"; + /* connect the context to the window*/ + #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) + if ((flags & RGFW_windowNoInitAPI) == 0) + glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); + #endif - char* line; + /* set the background*/ + RGFW_window_setName(win, name); - win->event.droppedFilesCount = 0; + XMapWindow(win->src.display, (Drawable) win->src.window); /* draw the window*/ + XMoveWindow(win->src.display, (Drawable) win->src.window, win->r.x, win->r.y); /*!< move the window to it's proper cords*/ - win->event.type = RGFW_DND; + if (flags & RGFW_windowAllowDND) { /* init drag and drop atoms and turn on drag and drop for this window */ + win->_flags |= RGFW_windowAllowDND; - while ((line = (char*)RGFW_strtok(data, "\r\n"))) { - char path[RGFW_MAX_PATH]; + XdndTypeList = XInternAtom(win->src.display, "XdndTypeList", False); + XdndSelection = XInternAtom(win->src.display, "XdndSelection", False); - data = NULL; + /* client messages */ + XdndEnter = XInternAtom(win->src.display, "XdndEnter", False); + XdndPosition = XInternAtom(win->src.display, "XdndPosition", False); + XdndStatus = XInternAtom(win->src.display, "XdndStatus", False); + XdndLeave = XInternAtom(win->src.display, "XdndLeave", False); + XdndDrop = XInternAtom(win->src.display, "XdndDrop", False); + XdndFinished = XInternAtom(win->src.display, "XdndFinished", False); - if (line[0] == '#') - continue; + /* actions */ + XdndActionCopy = XInternAtom(win->src.display, "XdndActionCopy", False); - char* l; - for (l = line; 1; l++) { - if ((l - line) > 7) - break; - else if (*l != prefix[(l - line)]) - break; - else if (*l == '\0' && prefix[(l - line)] == '\0') { - line += 7; - while (*line != '/') - line++; - break; - } else if (*l == '\0') - break; - } + XtextUriList = XInternAtom(win->src.display, "text/uri-list", False); + XtextPlain = XInternAtom(win->src.display, "text/plain", False); - win->event.droppedFilesCount++; + XdndAware = XInternAtom(win->src.display, "XdndAware", False); + const u8 version = 5; - size_t index = 0; - while (*line) { - if (line[0] == '%' && line[1] && line[2]) { - const char digits[3] = { line[1], line[2], '\0' }; - path[index] = (char) RGFW_STRTOL(digits, NULL, 16); - line += 2; - } else - path[index] = *line; + XChangeProperty(win->src.display, win->src.window, + XdndAware, 4, 32, + PropModeReplace, &version, 1); /*!< turns on drag and drop */ + } - index++; - line++; - } - path[index] = '\0'; - RGFW_MEMCPY(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1); - } + #ifdef RGFW_EGL + if ((flags & RGFW_windowNoInitAPI) == 0) + RGFW_createOpenGLContext(win); + #endif + #ifdef RGFW_DEBUG + printf("RGFW INFO: a window with a rect of {%i, %i, %i, %i} \n", win->r.x, win->r.y, win->r.w, win->r.h); + #endif + RGFW_window_setMouseDefault(win); + RGFW_windowsOpen++; + return win; /*return newly created window*/ +#endif +#ifdef RGFW_WAYLAND + wayland: - if (data) - XFree(data); + fprintf(stderr, "Warning: RGFW Wayland support is experimental\n"); - if (version >= 2) { - XEvent reply = { ClientMessage }; - reply.xclient.format = 32; - reply.xclient.message_type = XdndFinished; - reply.xclient.data.l[1] = result; - reply.xclient.data.l[2] = XdndActionCopy; + win->src.wl_display = wl_display_connect(NULL); + if (win->src.wl_display == NULL) { + #ifdef RGFW_DEBUG + fprintf(stderr, "Failed to load Wayland display\n"); + #endif + #ifdef RGFW_X11 + fprintf(stderr, "Falling back to X11\n"); + RGFW_useWayland(0); + return RGFW_createWindowPtr(name, rect, flags, win); + #endif + return NULL; + } - XSendEvent(win->src.display, source, False, NoEventMask, &reply); - XFlush(win->src.display); - } + struct wl_registry *registry = wl_display_get_registry(win->src.wl_display); + wl_registry_add_listener(registry, ®istry_listener, win); - RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); - break; - } - case FocusIn: - win->event.inFocus = 1; - win->event.type = RGFW_focusIn; - RGFW_focusCallback(win, 1); - break; - case FocusOut: - win->event.inFocus = 0; - win->event.type = RGFW_focusOut; - RGFW_focusCallback(win, 0); - break; - case EnterNotify: { - win->event.type = RGFW_mouseEnter; - win->event.point.x = E.xcrossing.x; - win->event.point.y = E.xcrossing.y; - RGFW_mouseNotifyCallBack(win, win->event.point, 1); - break; - } + wl_display_roundtrip(win->src.wl_display); + wl_display_dispatch(win->src.wl_display); - case LeaveNotify: { - win->event.type = RGFW_mouseLeave; - RGFW_mouseNotifyCallBack(win, win->event.point, 0); - break; + if (win->src.compositor == NULL) { + #ifdef RGFW_DEBUG + fprintf(stderr, "Can't find compositor.\n"); + #endif + + return NULL; } - case ConfigureNotify: { - /* detect resize */ - if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { - win->event.type = RGFW_windowResized; - win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); - RGFW_windowResizeCallback(win, win->r); - break; - } + if (RGFW_wl_cursor_theme == NULL) { + RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, win->src.shm); + RGFW_cursor_surface = wl_compositor_create_surface(win->src.compositor); - /* detect move */ - if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { - win->event.type = RGFW_windowMoved; - win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); - RGFW_windowMoveCallback(win, win->r); - break; - } + struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr"); + RGFW_cursor_image = cursor->images[0]; + struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); - break; - } - default: - XFlush(win->src.display); - return RGFW_window_checkEvent(win); + wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); + wl_surface_commit(RGFW_cursor_surface); } - XFlush(win->src.display); + xdg_wm_base_add_listener(win->src.xdg_wm_base, &xdg_wm_base_listener, NULL); - if (win->event.type) - return &win->event; - else - return NULL; -} + xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); -void RGFW_window_move(RGFW_window* win, RGFW_point v) { - RGFW_ASSERT(win != NULL); - win->r.x = v.x; - win->r.y = v.y; + win->src.surface = wl_compositor_create_surface(win->src.compositor); + wl_surface_set_user_data(win->src.surface, win); - XMoveWindow(win->src.display, win->src.window, v.x, v.y); -} + win->src.xdg_surface = xdg_wm_base_get_xdg_surface(win->src.xdg_wm_base, win->src.surface); + xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL); + xdg_wm_base_set_user_data(win->src.xdg_wm_base, win); -void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - win->r.w = a.w; - win->r.h = a.h; + win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface); + xdg_toplevel_set_user_data(win->src.xdg_toplevel, win); + xdg_toplevel_set_title(win->src.xdg_toplevel, name); + xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL); + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); - XResizeWindow(win->src.display, win->src.window, a.w, a.h); + if (!(flags & RGFW_windowNoBorder)) { + win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + decoration_manager, win->src.xdg_toplevel); + } - if (!(win->_flags & RGFW_windowNoResize)) - return; + if (flags & RGFW_windowCenter) { + RGFW_area screenR = RGFW_getScreenSize(); + RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); + } - XSizeHints sh; - sh.flags = (1L << 4) | (1L << 5); - sh.min_width = sh.max_width = a.w; - sh.min_height = sh.max_height = a.h; + if (flags & RGFW_windowOpenglSoftware) + setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); - XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); -} + wl_display_roundtrip(win->src.wl_display); -void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); + wl_surface_commit(win->src.surface); - if (a.w == 0 && a.h == 0) - return; + /* wait for the surface to be configured */ + while (wl_display_dispatch(win->src.wl_display) != -1 && !RGFW_wl_configured) { } + + #ifdef RGFW_OPENGL + if ((flags & RGFW_windowNoInitAPI) == 0) { + win->src.eglWindow = wl_egl_window_create(win->src.surface, win->r.w, win->r.h); + RGFW_createOpenGLContext(win); + } + #endif - XSizeHints hints; - long flags; + RGFW_init_buffer(win, NULL); - XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); + struct wl_callback* callback = wl_surface_frame(win->src.surface); + wl_callback_add_listener(callback, &wl_surface_frame_listener, win); + wl_surface_commit(win->src.surface); - hints.flags |= PMinSize; + if (flags & RGFW_windowHideMouse) { + RGFW_window_showMouse(win, 0); + } - hints.min_width = a.w; - hints.min_height = a.h; + win->src.eventIndex = 0; + win->src.eventLen = 0; - XSetWMNormalHints(win->src.display, win->src.window, &hints); + #ifdef RGFW_DEBUG + printf("RGFW INFO: a window with a rect of {%i, %i, %i, %i} \n", win->r.x, win->r.y, win->r.w, win->r.h); + #endif + RGFW_window_setMouseDefault(win); + RGFW_windowsOpen++; + return win; /*return newly created window*/ +#endif } -void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - RGFW_ASSERT(win != NULL); - - if (a.w == 0 && a.h == 0) - return; +RGFW_area RGFW_getScreenSize(void) { + RGFW_GOTO_WAYLAND(); + RGFW_ASSERT(RGFW_root != NULL); - XSizeHints hints; - long flags; + #ifdef RGFW_X11 + Screen* scrn = DefaultScreenOfDisplay(RGFW_root->src.display); + return RGFW_AREA(scrn->width, scrn->height); + #endif + #ifdef RGFW_WAYLAND + wayland: return RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h); // TODO + #endif +} - XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); +RGFW_point RGFW_getGlobalMousePoint(void) { + RGFW_ASSERT(RGFW_root != NULL); - hints.flags |= PMaxSize; + RGFW_point RGFWMouse; - hints.max_width = a.w; - hints.max_height = a.h; + i32 x, y; + u32 z; + Window window1, window2; + XQueryPointer(RGFW_root->src.display, XDefaultRootWindow(RGFW_root->src.display), &window1, &window2, &RGFWMouse.x, &RGFWMouse.y, &x, &y, &z); - XSetWMNormalHints(win->src.display, win->src.window, &hints); + return RGFWMouse; } - -void RGFW_window_minimize(RGFW_window* win) { +RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { RGFW_ASSERT(win != NULL); - XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display)); - XFlush(win->src.display); -} + RGFW_point RGFWMouse; -void RGFW_window_restore(RGFW_window* win) { - RGFW_ASSERT(win != NULL); + i32 x, y; + u32 z; + Window window1, window2; + XQueryPointer(win->src.display, win->src.window, &window1, &window2, &x, &y, &RGFWMouse.x, &RGFWMouse.y, &z); - XMapWindow(win->src.display, win->src.window); - XFlush(win->src.display); + return RGFWMouse; } -void RGFW_window_setName(RGFW_window* win, const char* name) { - RGFW_ASSERT(win); - - XStoreName(win->src.display, win->src.window, name); +char* RGFW_strtok(char* str, const char* delimStr) { + static char* static_str = NULL; - RGFW_LOAD_ATOM(_NET_WM_NAME); - XChangeProperty( - win->src.display, win->src.window, _NET_WM_NAME, RGFW_XUTF8_STRING, - 8, PropModeReplace, (u8*)name, 256 - ); -} + if (str != NULL) + static_str = str; -void* RGFW_libxshape = NULL; + if (static_str == NULL) { + return NULL; + } -#ifndef RGFW_NO_PASSTHROUGH + while (*static_str != '\0') { + b8 delim = 0; + for (const char* d = delimStr; *d != '\0'; d++) { + if (*static_str == *d) { + delim = 1; + break; + } + } + if (!delim) + break; + static_str++; + } -void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { - RGFW_ASSERT(win != NULL); + if (*static_str == '\0') + return NULL; - #if defined(__CYGWIN__) - RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext-6.so"); - #elif defined(__OpenBSD__) || defined(__NetBSD__) - RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext.so"); - #else - RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext.so.6"); - #endif + char* token_start = static_str; + while (*static_str != '\0') { + int delim = 0; + for (const char* d = delimStr; *d != '\0'; d++) { + if (*static_str == *d) { + delim = 1; + break; + } + } - typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); - static PFN_XShapeCombineMask XShapeCombineMaskSRC; + if (delim) { + *static_str = '\0'; + static_str++; + break; + } + static_str++; + } - typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); - static PFN_XShapeCombineRegion XShapeCombineRegionSRC; + return token_start; +} +int xAxis = 0, yAxis = 0; - RGFW_PROC_DEF(RGFW_libxshape, XShapeCombineRegion); - RGFW_PROC_DEF(RGFW_libxshape, XShapeCombineMask); +RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { + RGFW_ASSERT(win != NULL); - if (passthrough) { - Region region = XCreateRegion(); - XShapeCombineRegionSRC(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); - XDestroyRegion(region); + if (win->event.type == 0) + RGFW_resetKey(); - return; + if (win->event.type == RGFW_quit) { + return NULL; } - XShapeCombineMaskSRC(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); -} - -#endif /* RGFW_NO_PASSTHROUGH */ + win->event.type = 0; -b32 RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { - RGFW_ASSERT(win != NULL); RGFW_ASSERT(icon != NULL); - RGFW_ASSERT(channels == 3 || channels == 4); + #if defined(__linux__) && !defined(RGFW_NO_LINUX) + if (RGFW_linux_updateGamepad(win)) return &win->event; + #endif + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + XPending(win->src.display); - i32 count = 2 + (a.w * a.h); + XEvent E; /*!< raw X11 event */ - unsigned long* data = (unsigned long*) RGFW_alloc(count * sizeof(unsigned long)); - data[0] = (unsigned long)a.w; - data[1] = (unsigned long)a.h; + /* if there is no unread qued events, get a new one */ + if ((QLength(win->src.display) || XEventsQueued(win->src.display, QueuedAlready) + XEventsQueued(win->src.display, QueuedAfterReading)) + && win->event.type != RGFW_quit + ) + XNextEvent(win->src.display, &E); + else { + return NULL; + } - unsigned long* target = &data[2]; + win->event.type = 0; - u32 x, y; + /* xdnd data */ + Window source = 0; + long version = 0; + i32 format = 0; + + XEvent reply = { ClientMessage }; - for (x = 0; x < a.w; x++) { - for (y = 0; y < a.h; y++) { - size_t i = y * a.w + x; - u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; + switch (E.type) { + case KeyPress: + case KeyRelease: { + win->event.repeat = RGFW_FALSE; + /* check if it's a real key release */ + if (E.type == KeyRelease && XEventsQueued(win->src.display, QueuedAfterReading)) { /* get next event if there is one*/ + XEvent NE; + XPeekEvent(win->src.display, &NE); - target[i] = (unsigned long)((icon[i * 4 + 0]) << 16) | - (unsigned long)((icon[i * 4 + 1]) << 8) | - (unsigned long)((icon[i * 4 + 2]) << 0) | - (unsigned long)(alpha << 24); + if (E.xkey.time == NE.xkey.time && E.xkey.keycode == NE.xkey.keycode) /* check if the current and next are both the same*/ + win->event.repeat = RGFW_TRUE; } - } - RGFW_LOAD_ATOM(_NET_WM_ICON); + /* set event key data */ + win->event.key = RGFW_apiKeyToRGFW(E.xkey.keycode); - b32 res = (b32)XChangeProperty( - win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, - PropModeReplace, (u8*)data, count - ); + KeySym sym = (KeySym)XkbKeycodeToKeysym(win->src.display, E.xkey.keycode, 0, E.xkey.state & ShiftMask ? 1 : 0); - RGFW_free(data); + if ((E.xkey.state & LockMask) && sym >= XK_a && sym <= XK_z) + sym = (E.xkey.state & ShiftMask) ? sym + 32 : sym - 32; + if ((u8)sym != (u32)sym) + sym = 0; - XFlush(win->src.display); + win->event.keyChar = (u8)sym; - return res; -} + RGFW_keyboard[win->event.key].prev = RGFW_isPressed(win, win->event.key); -RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { - RGFW_ASSERT(icon); - RGFW_ASSERT(channels == 3 || channels == 4); - -#ifndef RGFW_NO_X11_CURSOR - XcursorImage* native = XcursorImageCreate(a.w, a.h); - native->xhot = 0; - native->yhot = 0; + /* get keystate data */ + win->event.type = (E.type == KeyPress) ? RGFW_keyPressed : RGFW_keyReleased; - XcursorPixel* target = native->pixels; - for (size_t x = 0; x < a.w; x++) { - for (size_t y = 0; y < a.h; y++) { - size_t i = y * a.w + x; - u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; + XKeyboardState keystate; + XGetKeyboardControl(win->src.display, &keystate); - target[i] = (u32)((icon[i * 4 + 0]) << 16) - | (u32)((icon[i * 4 + 1]) << 8) - | (u32)((icon[i * 4 + 2]) << 0) - | (u32)(alpha << 24); - } + RGFW_keyboard[win->event.key].current = (E.type == KeyPress); + RGFW_updateKeyMods(win, (keystate.led_mask & 1), (keystate.led_mask & 2)); + RGFW_keyCallback(win, win->event.key, win->event.keyChar, win->event.keyMod, (E.type == KeyPress)); + break; } + case ButtonPress: + case ButtonRelease: + win->event.type = RGFW_mouseButtonPressed + (E.type == ButtonRelease); // the events match - Cursor cursor = XcursorImageLoadCursor(RGFW_root->src.display, native); - XcursorImageDestroy(native); + win->event.button = E.xbutton.button; + switch(win->event.button) { + case RGFW_mouseScrollUp: + win->event.scroll = 1; + break; + case RGFW_mouseScrollDown: + win->event.scroll = -1; + break; + default: break; + } - return (void*)cursor; -#else - RGFW_UNUSED(image); RGFW_UNUSED(a.w); RGFW_UNUSED(channels); - return NULL; -#endif -} + RGFW_mouseButtons[win->event.button].prev = RGFW_mouseButtons[win->event.button].current; -void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { - RGFW_ASSERT(win && mouse); - XDefineCursor(win->src.display, win->src.window, (Cursor)mouse); -} + if (win->event.repeat == RGFW_FALSE) + win->event.repeat = RGFW_isPressed(win, win->event.key); -void RGFW_freeMouse(RGFW_mouse* mouse) { - RGFW_ASSERT(mouse); - XFreeCursor(RGFW_root->src.display, (Cursor)mouse); -} + RGFW_mouseButtons[win->event.button].current = (E.type == ButtonPress); + RGFW_mouseButtonCallback(win, win->event.button, win->event.scroll, (E.type == ButtonPress)); + break; -void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { - RGFW_ASSERT(win != NULL); + case MotionNotify: + win->event.point.x = E.xmotion.x; + win->event.point.y = E.xmotion.y; - XEvent event; - XQueryPointer(win->src.display, DefaultRootWindow(win->src.display), - &event.xbutton.root, &event.xbutton.window, - &event.xbutton.x_root, &event.xbutton.y_root, - &event.xbutton.x, &event.xbutton.y, - &event.xbutton.state); + if ((win->_flags & RGFW_HOLD_MOUSE)) { + win->event.point.y = E.xmotion.y; - win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y); - if (event.xbutton.x == p.x && event.xbutton.y == p.y) - return; + win->event.point.x = win->event.point.x - win->_lastMousePoint.x; + win->event.point.y = win->event.point.y - win->_lastMousePoint.y; + } - XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y); -} + win->_lastMousePoint = RGFW_POINT(E.xmotion.x, E.xmotion.y); -b32 RGFW_window_setMouseDefault(RGFW_window* win) { - return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); -} + win->event.type = RGFW_mousePosChanged; + RGFW_mousePosCallback(win, win->event.point); + break; -b32 RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - RGFW_ASSERT(win != NULL); + case GenericEvent: { + /* MotionNotify is used for mouse events if the mouse isn't held */ + if (!(win->_flags & RGFW_HOLD_MOUSE)) { + XFreeEventData(win->src.display, &E.xcookie); + break; + } - if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8))) - return 0; - - mouse = RGFW_mouseIconSrc[mouse]; + XGetEventData(win->src.display, &E.xcookie); + if (E.xcookie.evtype == XI_RawMotion) { + XIRawEvent *raw = (XIRawEvent *)E.xcookie.data; + if (raw->valuators.mask_len == 0) { + XFreeEventData(win->src.display, &E.xcookie); + break; + } - Cursor cursor = XCreateFontCursor(win->src.display, mouse); - XDefineCursor(win->src.display, win->src.window, (Cursor) cursor); + double deltaX = 0.0f; + double deltaY = 0.0f; - XFreeCursor(win->src.display, (Cursor) cursor); - return 1; -} + /* check if relative motion data exists where we think it does */ + if (XIMaskIsSet(raw->valuators.mask, 0) != 0) + deltaX += raw->raw_values[0]; + if (XIMaskIsSet(raw->valuators.mask, 1) != 0) + deltaY += raw->raw_values[1]; -void RGFW_window_hide(RGFW_window* win) { - XMapWindow(win->src.display, win->src.window); -} + win->event.point = RGFW_POINT((i32)deltaX, (i32)deltaY); -void RGFW_window_show(RGFW_window* win) { - XUnmapWindow(win->src.display, win->src.window); -} + RGFW_window_moveMouse(win, RGFW_POINT(win->r.x + (win->r.w / 2), win->r.y + (win->r.h / 2))); -RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { - #ifdef RGFW_X11 - XEvent event; - int format; - unsigned long N, sizeN; - char* data; - Atom target; + win->event.type = RGFW_mousePosChanged; + RGFW_mousePosCallback(win, win->event.point); + } - RGFW_LOAD_ATOM(XSEL_DATA); + XFreeEventData(win->src.display, &E.xcookie); + break; + } - XConvertSelection(RGFW_root->src.display, RGFW_XCLIPBOARD, RGFW_XUTF8_STRING, XSEL_DATA, RGFW_root->src.window, CurrentTime); - XSync(RGFW_root->src.display, 0); - XNextEvent(RGFW_root->src.display, &event); + case Expose: + win->event.type = RGFW_windowRefresh; + RGFW_windowRefreshCallback(win); + break; - if (event.type != SelectionNotify || event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0) - return -1; + case ClientMessage: { + /* if the client closed the window*/ + if (E.xclient.data.l[0] == (long)wm_delete_window) { + win->event.type = RGFW_quit; + RGFW_windowQuitCallback(win); + break; + } - XGetWindowProperty(event.xselection.display, event.xselection.requestor, - event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, - &format, &sizeN, &N, (unsigned char**) &data); + for (size_t i = 0; i < win->event.droppedFilesCount; i++) { + win->event.droppedFiles[i][0] = '\0'; + } + win->event.droppedFilesCount = 0; - RGFW_ssize_t size; - if (sizeN > strCapacity && str != NULL) - size = -1; + if ((win->_flags & RGFW_windowAllowDND) == 0) + break; - if ((target == RGFW_XUTF8_STRING || target == XA_STRING) && str != NULL) { - RGFW_MEMCPY(str, data, sizeN); - str[sizeN] = '\0'; - XFree(data); - } else if (str != NULL) size = -1; + reply.xclient.window = source; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)win->src.window; + reply.xclient.data.l[1] = 0; + reply.xclient.data.l[2] = None; - XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); - size = sizeN; - return size; - #endif - #if defined(RGFW_WAYLAND) - return 0; - #endif -} + if (E.xclient.message_type == XdndEnter) { + if (version > 5) + break; -void RGFW_writeClipboard(const char* text, u32 textLen) { - #ifdef RGFW_X11 - RGFW_LOAD_ATOM(SAVE_TARGETS); - RGFW_LOAD_ATOM(TARGETS); - RGFW_LOAD_ATOM(MULTIPLE); - RGFW_LOAD_ATOM(ATOM_PAIR); - RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); + unsigned long count; + Atom* formats; + Atom real_formats[6]; + Bool list = E.xclient.data.l[1] & 1; - XSetSelectionOwner(RGFW_root->src.display, RGFW_XCLIPBOARD, RGFW_root->src.window, CurrentTime); + source = E.xclient.data.l[0]; + version = E.xclient.data.l[1] >> 24; + format = None; - XConvertSelection(RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, RGFW_root->src.window, CurrentTime); - for (;;) { - XEvent event; + if (list) { + Atom actualType; + i32 actualFormat; + unsigned long bytesAfter; - XNextEvent(RGFW_root->src.display, &event); - if (event.type != SelectionRequest) { - break; - } + XGetWindowProperty( + win->src.display, source, XdndTypeList, + 0, LONG_MAX, False, 4, + &actualType, &actualFormat, &count, &bytesAfter, (u8**)&formats + ); + } else { + count = 0; - const XSelectionRequestEvent* request = &event.xselectionrequest; + for (size_t i = 2; i < 5; i++) { + Window format = E.xclient.data.l[i]; + if (format != None) { + real_formats[count] = format; + count += 1; + } + } - XEvent reply = { SelectionNotify }; - reply.xselection.property = 0; + formats = real_formats; + } - if (request->target == TARGETS) { - const Atom targets[] = { TARGETS, - MULTIPLE, - RGFW_XUTF8_STRING, - XA_STRING }; + for (size_t i = 0; i < count; i++) { + if (formats[i] == XtextUriList || formats[i] == XtextPlain) { + format = (int)formats[i]; + break; + } + } - XChangeProperty(RGFW_root->src.display, - request->requestor, - request->property, - 4, - 32, - PropModeReplace, - (u8*) targets, - sizeof(targets) / sizeof(targets[0])); + if (list) { + XFree(formats); + } - reply.xselection.property = request->property; + break; } - if (request->target == MULTIPLE) { - Atom* targets = NULL; + if (E.xclient.message_type == XdndPosition) { + const i32 xabs = (E.xclient.data.l[2] >> 16) & 0xffff; + const i32 yabs = (E.xclient.data.l[2]) & 0xffff; + Window dummy; + i32 xpos, ypos; - Atom actualType = 0; - int actualFormat = 0; - unsigned long count = 0, bytesAfter = 0; + if (version > 5) + break; - XGetWindowProperty(RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); + XTranslateCoordinates( + win->src.display, XDefaultRootWindow(win->src.display), win->src.window, + xabs, yabs, &xpos, &ypos, &dummy + ); - unsigned long i; - for (i = 0; i < (u32)count; i += 2) { - if (targets[i] == RGFW_XUTF8_STRING || targets[i] == XA_STRING) { - XChangeProperty(RGFW_root->src.display, - request->requestor, - targets[i + 1], - targets[i], - 8, - PropModeReplace, - (u8*) text, - textLen); - XFlush(RGFW_root->src.display); - } else { - targets[i + 1] = None; - } - } + win->event.point.x = xpos; + win->event.point.y = ypos; - XChangeProperty(RGFW_root->src.display, - request->requestor, - request->property, - ATOM_PAIR, - 32, - PropModeReplace, - (u8*) targets, - count); + reply.xclient.window = source; + reply.xclient.message_type = XdndStatus; - XFlush(RGFW_root->src.display); - XFree(targets); + if (format) { + reply.xclient.data.l[1] = 1; + if (version >= 2) + reply.xclient.data.l[4] = (long)XdndActionCopy; + } - reply.xselection.property = request->property; + XSendEvent(win->src.display, source, False, NoEventMask, &reply); + XFlush(win->src.display); + break; } - reply.xselection.display = request->display; - reply.xselection.requestor = request->requestor; - reply.xselection.selection = request->selection; - reply.xselection.target = request->target; - reply.xselection.time = request->time; + if (E.xclient.message_type != XdndDrop) + break; - XSendEvent(RGFW_root->src.display, request->requestor, False, 0, &reply); - XFlush(RGFW_root->src.display); - } - #endif - #if defined(RGFW_WAYLAND) + if (version > 5) + break; - #endif -} + win->event.type = RGFW_DNDInit; -u8 RGFW_window_isFullscreen(RGFW_window* win) { - RGFW_ASSERT(win != NULL); + if (format) { + Time time = (version >= 1) + ? (Time)E.xclient.data.l[2] + : CurrentTime; - XWindowAttributes windowAttributes; - XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); + XConvertSelection( + win->src.display, XdndSelection, (Atom)format, + XdndSelection, win->src.window, time + ); + } else if (version >= 2) { + XEvent reply = { ClientMessage }; - /* check if the window is visable */ - if (windowAttributes.map_state != IsViewable) - return 0; + XSendEvent(win->src.display, source, False, NoEventMask, &reply); + XFlush(win->src.display); + } - /* check if the window covers the full screen */ - return (windowAttributes.x == 0 && windowAttributes.y == 0 && - windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) && - windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display))); -} - -u8 RGFW_window_isHidden(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - XWindowAttributes windowAttributes; - XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); - - return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); -} - -u8 RGFW_window_isMinimized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - RGFW_LOAD_ATOM(WM_STATE); - - Atom actual_type; - i32 actual_format; - unsigned long nitems, bytes_after; - unsigned char* prop_data; - - i16 status = XGetWindowProperty(win->src.display, win->src.window, WM_STATE, 0, 2, False, - AnyPropertyType, &actual_type, &actual_format, - &nitems, &bytes_after, &prop_data); + RGFW_dndInitCallback(win, win->event.point); + } break; + case SelectionNotify: { + /* this is only for checking for xdnd drops */ + if (E.xselection.property != XdndSelection || !(win->_flags & RGFW_windowAllowDND)) + break; - if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) { - XFree(prop_data); - return 1; - } + char* data; + unsigned long result; - if (prop_data != NULL) - XFree(prop_data); + Atom actualType; + i32 actualFormat; + unsigned long bytesAfter; - return 0; -} + XGetWindowProperty(win->src.display, E.xselection.requestor, E.xselection.property, 0, LONG_MAX, False, E.xselection.target, &actualType, &actualFormat, &result, &bytesAfter, (u8**) &data); -u8 RGFW_window_isMaximized(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - RGFW_LOAD_ATOM(_NET_WM_STATE); - RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); - RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); + if (result == 0) + break; + + const char* prefix = (const char*)"file://"; - Atom actual_type; - i32 actual_format; - unsigned long nitems, bytes_after; - unsigned char* prop_data; + char* line; - i16 status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, 1024, False, - XA_ATOM, &actual_type, &actual_format, - &nitems, &bytes_after, &prop_data); + win->event.droppedFilesCount = 0; - if (status != Success) { - if (prop_data != NULL) - XFree(prop_data); + win->event.type = RGFW_DND; - return 0; - } + while ((line = (char*)RGFW_strtok(data, "\r\n"))) { + char path[RGFW_MAX_PATH]; - Atom* atoms = (Atom*) prop_data; - u64 i; - for (i = 0; i < nitems; ++i) { - if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT || - atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { - XFree(prop_data); - return 1; - } - } + data = NULL; - return 0; -} + if (line[0] == '#') + continue; -static float XGetSystemContentDPI(Display* display, i32 screen) { - float dpi = 96.0f; + char* l; + for (l = line; 1; l++) { + if ((l - line) > 7) + break; + else if (*l != prefix[(l - line)]) + break; + else if (*l == '\0' && prefix[(l - line)] == '\0') { + line += 7; + while (*line != '/') + line++; + break; + } else if (*l == '\0') + break; + } - #ifndef RGFW_NO_DPI - RGFW_UNUSED(screen); - char* rms = XResourceManagerString(display); - XrmDatabase db = NULL; + win->event.droppedFilesCount++; - if (rms && db) { - db = XrmGetStringDatabase(rms); - XrmValue value; - char* type = NULL; + size_t index = 0; + while (*line) { + if (line[0] == '%' && line[1] && line[2]) { + const char digits[3] = { line[1], line[2], '\0' }; + path[index] = (char) RGFW_STRTOL(digits, NULL, 16); + line += 2; + } else + path[index] = *line; - if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0) { - dpi = (float)atof(value.addr); + index++; + line++; } - XrmDestroyDatabase(db); + path[index] = '\0'; + RGFW_MEMCPY(win->event.droppedFiles[win->event.droppedFilesCount - 1], path, index + 1); } - #else - dpi = RGFW_ROUND(DisplayWidth(display, screen) / (DisplayWidthMM(display, screen) / 25.4)); - #endif - - return dpi; -} - -RGFW_monitor RGFW_XCreateMonitor(i32 screen) { - RGFW_monitor monitor; - - Display* display = XOpenDisplay(NULL); - - RGFW_area size = RGFW_getScreenSize(); - - monitor.rect = RGFW_RECT(0, 0, size.w, size.h); - monitor.physW = DisplayWidthMM(display, screen) / 25.4; - monitor.physH = DisplayHeightMM(display, screen) / 25.4; - char* name = XDisplayName((const char*)display); - RGFW_MEMCPY(monitor.name, name, 128); - - float dpi = XGetSystemContentDPI(display, screen); - monitor.pixelRatio = dpi / 96.0f; - - #ifndef RGFW_NO_DPI - XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); + if (data) + XFree(data); - XRRCrtcInfo* ci = NULL; - int crtc = screen; + if (version >= 2) { + XEvent reply = { ClientMessage }; + reply.xclient.format = 32; + reply.xclient.message_type = XdndFinished; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = XdndActionCopy; - if (sr->ncrtc > crtc) { - ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); + XSendEvent(win->src.display, source, False, NoEventMask, &reply); + XFlush(win->src.display); } - #endif - - float ppi_width = RGFW_ROUND((float)monitor.rect.w/(float)monitor.physW); - float ppi_height = RGFW_ROUND((float)monitor.rect.h/(float)monitor.physH); - - monitor.scaleX = (float) (ppi_width) / dpi; - monitor.scaleY = (float) (ppi_height) / dpi; - #ifndef RGFW_NO_DPI - XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]); + RGFW_dndCallback(win, win->event.droppedFiles, win->event.droppedFilesCount); + break; + } + case FocusIn: + win->event.inFocus = 1; + win->event.type = RGFW_focusIn; + RGFW_focusCallback(win, 1); + break; + case FocusOut: + win->event.inFocus = 0; + win->event.type = RGFW_focusOut; + RGFW_focusCallback(win, 0); + break; + case EnterNotify: { + win->event.type = RGFW_mouseEnter; + win->event.point.x = E.xcrossing.x; + win->event.point.y = E.xcrossing.y; + RGFW_mouseNotifyCallBack(win, win->event.point, 1); + break; + } - if (info == NULL || ci == NULL) { - XRRFreeScreenResources(sr); - XCloseDisplay(display); + case LeaveNotify: { + win->event.type = RGFW_mouseLeave; + RGFW_mouseNotifyCallBack(win, win->event.point, 0); + break; + } - #ifdef RGFW_DEBUG - printf("RGFW INFO: monitor found: scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n", monitor.name, monitor.rect.x, monitor.rect.y, monitor.rect.w, monitor.rect.h, monitor.physW, monitor.physH, monitor.scaleX, monitor.scaleY, monitor.pixelRatio); - #endif - return monitor; + case ConfigureNotify: { + /* detect resize */ + if (E.xconfigure.width != win->r.w || E.xconfigure.height != win->r.h) { + win->event.type = RGFW_windowResized; + win->r = RGFW_RECT(win->r.x, win->r.y, E.xconfigure.width, E.xconfigure.height); + RGFW_windowResizeCallback(win, win->r); + break; } + /* detect move */ + if (E.xconfigure.x != win->r.x || E.xconfigure.y != win->r.y) { + win->event.type = RGFW_windowMoved; + win->r = RGFW_RECT(E.xconfigure.x, E.xconfigure.y, win->r.w, win->r.h); + RGFW_windowMoveCallback(win, win->r); + break; + } - float physW = info->mm_width / 25.4; - float physH = info->mm_height / 25.4; + break; + } + default: + XFlush(win->src.display); + return RGFW_window_checkEvent(win); + } - RGFW_MEMCPY(monitor.name, info->name, 128); + XFlush(win->src.display); + if (win->event.type) return &win->event; + else return NULL; +#endif +#ifdef RGFW_WAYLAND + wayland: + if (win->_flags & RGFW_windowHide) + return NULL; - if (physW && physH) { - monitor.physW = physW; - monitor.physH = physH; + if (win->src.eventIndex == 0) { + if (wl_display_roundtrip(win->src.wl_display) == -1) { + return NULL; + } } - monitor.rect.x = ci->x; - monitor.rect.y = ci->y; + if (win->src.eventLen == 0) { + return NULL; + } - float w = ci->width; - float h = ci->height; + RGFW_event ev = RGFW_eventPipe_pop(win); - if (w && h) { - monitor.rect.w = w; - monitor.rect.h = h; + if (ev.type == 0 || win->event.type == RGFW_quit) { + return NULL; } - #endif - if (monitor.physW == 0 || monitor.physH == 0) { - monitor.scaleX = 0; - monitor.scaleY = 0; - } else { - float ppi_width = RGFW_ROUND((float)monitor.rect.w/(float)monitor.physW); - float ppi_height = RGFW_ROUND((float)monitor.rect.h/(float)monitor.physH); - - monitor.scaleX = (float) (ppi_width) / (float) dpi; - monitor.scaleY = (float) (ppi_height) / (float) dpi; - - if ((monitor.scaleX > 1 && monitor.scaleX < 1.1)) - monitor.scaleX = 1; + ev.frameTime = win->event.frameTime; + ev.frameTime2 = win->event.frameTime2; + ev.inFocus = win->event.inFocus; + win->event = ev; + if (win->event.type) return &win->event; + else return NULL; +#endif +} - if ((monitor.scaleY > 1 && monitor.scaleY < 1.1)) - monitor.scaleY = 1; +void RGFW_window_move(RGFW_window* win, RGFW_point v) { + RGFW_ASSERT(win != NULL); + win->r.x = v.x; + win->r.y = v.y; + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + XMoveWindow(win->src.display, win->src.window, v.x, v.y); +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_ASSERT(win != NULL); + + if (win->src.compositor) { + struct wl_pointer *pointer = wl_seat_get_pointer(win->src.seat); + if (!pointer) { + return; + } + + wl_display_flush(win->src.wl_display); } - - #ifndef RGFW_NO_DPI - XRRFreeCrtcInfo(ci); - XRRFreeScreenResources(sr); - #endif - - XCloseDisplay(display); - - #ifdef RGFW_DEBUG - printf("RGFW INFO: monitor found: scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n", monitor.name, monitor.rect.x, monitor.rect.y, monitor.rect.w, monitor.rect.h, monitor.physW, monitor.physH, monitor.scaleX, monitor.scaleY, monitor.pixelRatio); - #endif - - return monitor; +#endif } -RGFW_monitor RGFW_monitors[6]; -RGFW_monitor* RGFW_getMonitors(void) { - #ifdef RGFW_X11 - size_t i; - for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++) - RGFW_monitors[i] = RGFW_XCreateMonitor(i); - - return RGFW_monitors; - #endif - #ifdef RGFW_WAYLAND - #endif -} +void RGFW_window_resize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + win->r.w = a.w; + win->r.h = a.h; + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + XResizeWindow(win->src.display, win->src.window, a.w, a.h); -RGFW_monitor RGFW_getPrimaryMonitor(void) { - #ifdef RGFW_X11 - RGFW_ASSERT(RGFW_root != NULL); - return RGFW_XCreateMonitor(DefaultScreen(RGFW_root->src.display)); - #endif - #ifdef RGFW_WAYLAND + if (!(win->_flags & RGFW_windowNoResize)) + return; - #endif + XSizeHints sh; + sh.flags = (1L << 4) | (1L << 5); + sh.min_width = sh.max_width = a.w; + sh.min_height = sh.max_height = a.h; + + XSetWMSizeHints(win->src.display, (Drawable) win->src.window, &sh, XA_WM_NORMAL_HINTS); +#endif +#ifdef RGFW_WAYLAND + wayland: + if (win->src.compositor) { + xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); + #ifdef RGFW_OPENGL + wl_egl_window_resize(win->src.eglWindow, a.w, a.h, 0, 0); + #endif + } +#endif } -RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { +void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { RGFW_ASSERT(win != NULL); - XWindowAttributes attrs; - if (!XGetWindowAttributes(win->src.display, win->src.window, &attrs)) { - return (RGFW_monitor){}; - } - - #ifndef RGFW_NO_DPI - XRRScreenResources* screenRes = XRRGetScreenResources(win->src.display, DefaultRootWindow(win->src.display)); - if (screenRes == NULL) { - return (RGFW_monitor){}; - } + if (a.w == 0 && a.h == 0) + return; - for (int i = 0; i < screenRes->ncrtc; i++) { - XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(win->src.display, screenRes, screenRes->crtcs[i]); - if (!crtcInfo) continue; + XSizeHints hints; + long flags; - int monitorX = crtcInfo->x; - int monitorY = crtcInfo->y; - int monitorWidth = crtcInfo->width; - int monitorHeight = crtcInfo->height; + XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); - if (attrs.x >= monitorX && - attrs.x < monitorX + monitorWidth && - attrs.y >= monitorY && - attrs.y < monitorY + monitorHeight) { - XRRFreeCrtcInfo(crtcInfo); - XRRFreeScreenResources(screenRes); - return RGFW_XCreateMonitor(i); - } + hints.flags |= PMinSize; - XRRFreeCrtcInfo(crtcInfo); - } + hints.min_width = a.w; + hints.min_height = a.h; - XRRFreeScreenResources(screenRes); - #else - size_t i; - for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++) { - Screen* screen = ScreenOfDisplay(RGFW_root->src.display, i); - if (attrs.x >= 0 && attrs.x < 0 + XWidthOfScreen(screen) && - attrs.y >= 0 && attrs.y < 0 + XHeightOfScreen(screen)) - return RGFW_XCreateMonitor(i); - } - #endif - return (RGFW_monitor){}; + XSetWMNormalHints(win->src.display, win->src.window, &hints); } -#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - -void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { - if (win == NULL) - glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL); - else - glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); -} -#endif +void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { + RGFW_ASSERT(win != NULL); + if (a.w == 0 && a.h == 0) + return; -void RGFW_window_swapBuffers(RGFW_window* win) { - RGFW_ASSERT(win != NULL); + XSizeHints hints; + long flags; - /* clear the window*/ - if (!(win->_flags & RGFW_NO_CPU_RENDER)) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - RGFW_area area = RGFW_bufferSize; - win->src.bitmap->data = (char*) win->buffer; - #if !defined(RGFW_X11_DONT_CONVERT_BGR) && !defined(RGFW_OSMESA) - u32 x, y; - for (y = 0; y < (u32)win->r.h; y++) { - for (x = 0; x < (u32)win->r.w; x++) { - u32 index = (y * 4 * area.w) + x * 4; + XGetWMNormalHints(win->src.display, win->src.window, &hints, &flags); - u8 red = win->src.bitmap->data[index]; - win->src.bitmap->data[index] = win->buffer[index + 2]; - win->src.bitmap->data[index + 2] = red; + hints.flags |= PMaxSize; - } - } - #endif - XPutImage(win->src.display, win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h); - win->src.bitmap->data = NULL; - #endif - } + hints.max_width = a.w; + hints.max_height = a.h; - if (!(win->_flags & RGFW_NO_GPU_RENDER)) { - #ifdef RGFW_EGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); - #elif defined(RGFW_OPENGL) - glXSwapBuffers(win->src.display, win->src.window); - #endif - } + XSetWMNormalHints(win->src.display, win->src.window, &hints); } -#if !defined(RGFW_EGL) -void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { +void RGFW_window_minimize(RGFW_window* win) { RGFW_ASSERT(win != NULL); - #if defined(RGFW_OPENGL) - ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))(win->src.display, win->src.window, swapInterval); - #else - RGFW_UNUSED(swapInterval); - #endif + XIconifyWindow(win->src.display, win->src.window, DefaultScreen(win->src.display)); + XFlush(win->src.display); } -#endif - -void RGFW_window_close(RGFW_window* win) { +void RGFW_window_restore(RGFW_window* win) { RGFW_ASSERT(win != NULL); - #ifdef RGFW_X11 - /* ungrab pointer if it was grabbed */ - if (win->_flags & RGFW_HOLD_MOUSE) - XUngrabPointer(win->src.display, CurrentTime); - #ifdef RGFW_EGL - RGFW_closeEGL(win); - #endif + XMapWindow(win->src.display, win->src.window); + XFlush(win->src.display); +} - if (RGFW_hiddenMouse != NULL && (RGFW_windowsOpen - 1) <= 0) { - RGFW_freeMouse(RGFW_hiddenMouse); - RGFW_hiddenMouse = 0; - } +void RGFW_window_setName(RGFW_window* win, const char* name) { + RGFW_ASSERT(win); + RGFW_GOTO_WAYLAND(); + #ifdef RGFW_X11 + XStoreName(win->src.display, win->src.window, name); - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - if (win->buffer != NULL) { - if ((win->_flags & RGFW_BUFFER_ALLOC)) - win->_mem.free(win->_mem.userdata, win->buffer); - XDestroyImage((XImage*) win->src.bitmap); - XFreeGC(win->src.display, win->src.gc); - } + RGFW_LOAD_ATOM(_NET_WM_NAME); + XChangeProperty( + win->src.display, win->src.window, _NET_WM_NAME, RGFW_XUTF8_STRING, + 8, PropModeReplace, (u8*)name, 256 + ); #endif - - if (win->src.display) { - #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - glXDestroyContext(win->src.display, win->src.ctx); + #ifdef RGFW_WAYLAND + wayland: + if (win->src.compositor) + xdg_toplevel_set_title(win->src.xdg_toplevel, name); #endif +} - if (win == RGFW_root) - RGFW_root = NULL; +void* RGFW_libxshape = NULL; - if ((Drawable) win->src.window) - XDestroyWindow(win->src.display, (Drawable) win->src.window); /*!< close the window*/ +#ifndef RGFW_NO_PASSTHROUGH - XCloseDisplay(win->src.display); /*!< kill the display*/ - } +void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { + RGFW_ASSERT(win != NULL); - #ifdef RGFW_ALLOC_DROPFILES - { - u32 i; - for (i = 0; i < RGFW_MAX_DROPS; i++) - win->_mem.free(win->_mem.userdata, win->event.droppedFiles[i]); + #if defined(__CYGWIN__) + RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext-6.so"); + #elif defined(__OpenBSD__) || defined(__NetBSD__) + RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext.so"); + #else + RGFW_LOAD_LIBRARY(RGFW_libxshape, "libXext.so.6"); + #endif + typedef void (* PFN_XShapeCombineMask)(Display*,Window,int,int,int,Pixmap,int); + static PFN_XShapeCombineMask XShapeCombineMaskSRC; - win->_mem.free(win->_mem.userdata, win->event.droppedFiles); - } - #endif - - /* set cleared display / window to NULL for error checking */ - win->src.display = 0; - win->src.window = 0; - - RGFW_windowsOpen--; - - #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL; - if (RGFW_windowsOpen <= 0) { - #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) - RGFW_FREE_LIBRARY(X11Cursorhandle); - #endif - #if !defined(RGFW_NO_X11_XI_PRELOAD) - RGFW_FREE_LIBRARY(X11Xihandle); - #endif - - #ifdef RGFW_USE_XDL - XDL_close(); - #endif - - #ifndef RGFW_NO_PASSTHROUGH - RGFW_FREE_LIBRARY(RGFW_libxshape); - #endif - - #ifndef RGFW_NO_LINUX - if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){ - close(RGFW_eventWait_forceStop[0]); - close(RGFW_eventWait_forceStop[1]); - } + typedef void (* PFN_XShapeCombineRegion)(Display*,Window,int,int,int,Region,int); + static PFN_XShapeCombineRegion XShapeCombineRegionSRC; - u8 i; - for (i = 0; i < RGFW_gamepadCount; i++) { - if(RGFW_gamepads[i]) - close(RGFW_gamepads[i]); - } - #endif - } - #endif + RGFW_PROC_DEF(RGFW_libxshape, XShapeCombineRegion); + RGFW_PROC_DEF(RGFW_libxshape, XShapeCombineMask); - #ifdef RGFW_WAYLAND - #ifdef RGFW_EGL - RGFW_closeEGL(win); - #endif + if (passthrough) { + Region region = XCreateRegion(); + XShapeCombineRegionSRC(win->src.display, win->src.window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); - if (RGFW_root == win) { - RGFW_root = NULL; + return; } - xdg_toplevel_destroy(win->src.xdg_toplevel); - xdg_surface_destroy(win->src.xdg_surface); - wl_surface_destroy(win->src.surface); - - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - wl_buffer_destroy(win->src.wl_buffer); - if ((win->_flags & RGFW_BUFFER_ALLOC)) - win->_mem.free(win->_mem.userdata, win->buffer); - - munmap(win->src.buffer, win->r.w * win->r.h * 4); - #endif - - wl_display_disconnect(win->src.display); - #endif - - RGFW_clipboard_switch(NULL); - - if ((win->_flags & RGFW_WINDOW_ALLOC)) - win->_mem.free(win->_mem.userdata, win); + XShapeCombineMaskSRC(win->src.display, win->src.window, ShapeInput, 0, 0, None, ShapeSet); } +#endif /* RGFW_NO_PASSTHROUGH */ -/* - End of X11 linux / unix defines -*/ - -#endif /* RGFW_X11 */ - +b32 RGFW_window_setIcon(RGFW_window* win, u8* icon, RGFW_area a, i32 channels) { + RGFW_ASSERT(win != NULL); RGFW_ASSERT(icon != NULL); + RGFW_ASSERT(channels == 3 || channels == 4); + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + i32 count = 2 + (a.w * a.h); -/* wayland or X11 defines*/ -#if defined(RGFW_WAYLAND) || defined(RGFW_X11) && !defined(RGFW_NO_LINUX) -#include -#include -#include + unsigned long* data = (unsigned long*) RGFW_alloc(count * sizeof(unsigned long)); + data[0] = (unsigned long)a.w; + data[1] = (unsigned long)a.h; -void RGFW_stopCheckEvents(void) { + unsigned long* target = &data[2]; - RGFW_eventWait_forceStop[2] = 1; - while (1) { - const char byte = 0; - const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1); - if (result == 1 || result == -1) - break; - } -} + u32 x, y; -void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { - if (waitMS == 0) - return; + for (x = 0; x < a.w; x++) { + for (y = 0; y < a.h; y++) { + size_t i = y * a.w + x; + u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; - u8 i; - if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) { - if (pipe(RGFW_eventWait_forceStop) != -1) { - fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0); - fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0); - fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0); - fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0); + target[i] = (unsigned long)((icon[i * 4 + 0]) << 16) | + (unsigned long)((icon[i * 4 + 1]) << 8) | + (unsigned long)((icon[i * 4 + 2]) << 0) | + (unsigned long)(alpha << 24); } } - struct pollfd fds[] = { - #ifdef RGFW_WAYLAND - { wl_display_get_fd(win->src.display), POLLIN, 0 }, - #else - { ConnectionNumber(win->src.display), POLLIN, 0 }, - #endif - { RGFW_eventWait_forceStop[0], POLLIN, 0 }, - #if defined(__linux__) - { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} - #endif - }; - - u8 index = 2; - - #if defined(__linux__) - for (i = 0; i < RGFW_gamepadCount; i++) { - if (RGFW_gamepads[i] == 0) - continue; + RGFW_LOAD_ATOM(_NET_WM_ICON); - fds[index].fd = RGFW_gamepads[i]; - index++; - } - #endif + b32 res = (b32)XChangeProperty( + win->src.display, win->src.window, _NET_WM_ICON, XA_CARDINAL, 32, + PropModeReplace, (u8*)data, count + ); + RGFW_free(data); - u64 start = RGFW_getTimeNS(); + XFlush(win->src.display); + return res; +#endif +#ifdef RGFW_WAYLAND + wayland: + return 0; +#endif +} +RGFW_mouse* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { + RGFW_ASSERT(icon); + RGFW_ASSERT(channels == 3 || channels == 4); + RGFW_GOTO_WAYLAND(); + +#ifdef RGFW_X11 +#ifndef RGFW_NO_X11_CURSOR + XcursorImage* native = XcursorImageCreate(a.w, a.h); + native->xhot = 0; + native->yhot = 0; - #ifdef RGFW_WAYLAND - while (wl_display_dispatch(win->src.display) <= 0 && waitMS >= -1) { - #else - while (XPending(win->src.display) == 0 && waitMS >= -1) { - #endif - if (poll(fds, index, waitMS) <= 0) - break; + XcursorPixel* target = native->pixels; + for (size_t x = 0; x < a.w; x++) { + for (size_t y = 0; y < a.h; y++) { + size_t i = y * a.w + x; + u32 alpha = (channels == 4) ? icon[i * 4 + 3] : 0xFF; - if (waitMS > 0) { - waitMS -= (RGFW_getTimeNS() - start) / 1e+6; + target[i] = (u32)((icon[i * 4 + 0]) << 16) + | (u32)((icon[i * 4 + 1]) << 8) + | (u32)((icon[i * 4 + 2]) << 0) + | (u32)(alpha << 24); } } - /* drain any data in the stop request */ - if (RGFW_eventWait_forceStop[2]) { - char data[64]; - (void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data)); + Cursor cursor = XcursorImageLoadCursor(RGFW_root->src.display, native); + XcursorImageDestroy(native); - RGFW_eventWait_forceStop[2] = 0; - } + return (void*)cursor; +#else + RGFW_UNUSED(image); RGFW_UNUSED(a.w); RGFW_UNUSED(channels); + return NULL; +#endif +#endif +#ifdef RGFW_WAYLAND + wayland: + RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); + return NULL; // TODO +#endif } -u64 RGFW_getTimeNS(void) { - struct timespec ts = { 0, 0 }; - #ifndef RGFW_NO_UNIX_CLOCK - clock_gettime(1, &ts); - #endif - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - - return nanoSeconds; +void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { +RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + RGFW_ASSERT(win && mouse); + XDefineCursor(win->src.display, win->src.window, (Cursor)mouse); +#endif +#ifdef RGFW_WAYLAND + wayland: +#endif } -u64 RGFW_getTime(void) { - struct timespec ts = { 0, 0 }; - #ifndef RGFW_NO_UNIX_CLOCK - clock_gettime(1, &ts); - #endif - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - - return (double)(nanoSeconds) * 1e-9; +void RGFW_freeMouse(RGFW_mouse* mouse) { +RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + RGFW_ASSERT(mouse); + XFreeCursor(RGFW_root->src.display, (Cursor)mouse); +#endif +#ifdef RGFW_WAYLAND + wayland: +#endif } -#endif /* end of wayland or X11 time defines*/ +void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { + RGFW_ASSERT(win != NULL); -/* + XEvent event; + XQueryPointer(win->src.display, DefaultRootWindow(win->src.display), + &event.xbutton.root, &event.xbutton.window, + &event.xbutton.x_root, &event.xbutton.y_root, + &event.xbutton.x, &event.xbutton.y, + &event.xbutton.state); - Start of Wayland defines + win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y); + if (event.xbutton.x == p.x && event.xbutton.y == p.y) + return; + XWarpPointer(win->src.display, None, win->src.window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y); +} -*/ +b32 RGFW_window_setMouseDefault(RGFW_window* win) { + return RGFW_window_setMouseStandard(win, RGFW_mouseArrow); +} -#ifdef RGFW_WAYLAND -/* -Wayland TODO: -- fix RGFW_keyPressed lock state +b32 RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { + RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + if (mouse > (sizeof(RGFW_mouseIconSrc) / sizeof(u8))) + return 0; - RGFW_windowMoved, the window was moved (by the user) - RGFW_windowResized the window was resized (by the user), [on webASM this means the browser was resized] - RGFW_windowRefresh The window content needs to be refreshed - - RGFW_DND a file has been dropped into the window - RGFW_DNDInit - -- window args: - #define RGFW_windowNoResize the window cannot be resized by the user - #define RGFW_windowAllowDND the window supports drag and drop - #define RGFW_scaleToMonitor scale the window to the screen - -- other missing functions functions ("TODO wayland") (~30 functions) -- fix buffer rendering weird behavior -*/ -#include -#include -#include -#include -#include -#include -#include -#include + mouse = RGFW_mouseIconSrc[mouse]; + Cursor cursor = XCreateFontCursor(win->src.display, mouse); + XDefineCursor(win->src.display, win->src.window, (Cursor) cursor); -#ifndef RGFW_NO_WAYLAND_X11 -#include -Display* x11_display = NULL; -Window x11_window; + XFreeCursor(win->src.display, (Cursor) cursor); + return 1; #endif +#ifdef RGFW_WAYLAND + wayland: + static const char* iconStrings[] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" }; -RGFW_window* RGFW_key_win = NULL; - -void RGFW_eventPipe_push(RGFW_window* win, RGFW_event event) { - if (win == NULL) { - win = RGFW_key_win; - - if (win == NULL) return; - } - - if (win->src.eventLen >= (i32)(sizeof(win->src.events) / sizeof(win->src.events[0]))) - return; + struct wl_cursor* wlcursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); + RGFW_cursor_image = wlcursor->images[0]; + struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); - win->src.events[win->src.eventLen] = event; - win->src.eventLen += 1; + wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); + wl_surface_commit(RGFW_cursor_surface); + return 1; +#endif } -RGFW_event RGFW_eventPipe_pop(RGFW_window* win) { - RGFW_event ev; - ev.type = 0; - - if (win->src.eventLen > -1) - win->src.eventLen -= 1; - - if (win->src.eventLen >= 0) - ev = win->src.events[win->src.eventLen]; - - return ev; +void RGFW_window_hide(RGFW_window* win) { + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + XMapWindow(win->src.display, win->src.window); +#endif +#ifdef RGFW_WAYLAND + wayland: + wl_surface_attach(win->src.surface, NULL, 0, 0); + wl_surface_commit(win->src.surface); + win->_flags |= RGFW_windowHide; +#endif } -/* wayland global garbage (wayland bad, X11 is fine (ish) (not really)) */ -#include "xdg-shell.h" -#include "xdg-decoration-unstable-v1.h" - -static struct xkb_context *xkb_context; -static struct xkb_keymap *keymap = NULL; -static struct xkb_state *xkb_state = NULL; -enum zxdg_toplevel_decoration_v1_mode client_preferred_mode, RGFW_current_mode; -static struct zxdg_decoration_manager_v1 *decoration_manager = NULL; - -struct wl_cursor_theme* RGFW_wl_cursor_theme = NULL; -struct wl_surface* RGFW_cursor_surface = NULL; -struct wl_cursor_image* RGFW_cursor_image = NULL; - -static void xdg_wm_base_ping_handler(void *data, - struct xdg_wm_base *wm_base, uint32_t serial) -{ - RGFW_UNUSED(data); - xdg_wm_base_pong(wm_base, serial); +void RGFW_window_show(RGFW_window* win) { + if (win->_flags & RGFW_windowHide) + win->_flags ^= RGFW_windowHide; + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + XUnmapWindow(win->src.display, win->src.window); +#endif +#ifdef RGFW_WAYLAND + wayland: + //wl_surface_attach(win->src.surface, win->rc., 0, 0); + wl_surface_commit(win->src.surface); +#endif } -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = xdg_wm_base_ping_handler, -}; - -b8 RGFW_wl_configured = 0; - -static void xdg_surface_configure_handler(void *data, - struct xdg_surface *xdg_surface, uint32_t serial) -{ - RGFW_UNUSED(data); - xdg_surface_ack_configure(xdg_surface, serial); - #ifdef RGFW_DEBUG - printf("Surface configured\n"); - #endif - RGFW_wl_configured = 1; -} +RGFW_ssize_t RGFW_readClipboardPtr(char* str, size_t strCapacity) { + RGFW_GOTO_WAYLAND(); + #ifdef RGFW_X11 + XEvent event; + int format; + unsigned long N, sizeN; + char* data; + Atom target; -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_configure_handler, -}; + RGFW_LOAD_ATOM(XSEL_DATA); -static void xdg_toplevel_configure_handler(void *data, - struct xdg_toplevel *toplevel, int32_t width, int32_t height, - struct wl_array *states) -{ - RGFW_UNUSED(data); RGFW_UNUSED(toplevel); RGFW_UNUSED(states); - #ifdef RGFW_DEBUG - fprintf(stderr, "XDG toplevel configure: %dx%d\n", width, height); - #endif -} + XConvertSelection(RGFW_root->src.display, RGFW_XCLIPBOARD, RGFW_XUTF8_STRING, XSEL_DATA, RGFW_root->src.window, CurrentTime); + XSync(RGFW_root->src.display, 0); + XNextEvent(RGFW_root->src.display, &event); -static void xdg_toplevel_close_handler(void *data, - struct xdg_toplevel *toplevel) -{ - RGFW_UNUSED(data); - RGFW_window* win = (RGFW_window*)xdg_toplevel_get_user_data(toplevel); - if (win == NULL) - win = RGFW_key_win; + if (event.type != SelectionNotify || event.xselection.selection != RGFW_XCLIPBOARD || event.xselection.property == 0) + return -1; - RGFW_event ev; - ev.type = RGFW_quit; + XGetWindowProperty(event.xselection.display, event.xselection.requestor, + event.xselection.property, 0L, (~0L), 0, AnyPropertyType, &target, + &format, &sizeN, &N, (unsigned char**) &data); - RGFW_eventPipe_push(win, ev); + RGFW_ssize_t size; + if (sizeN > strCapacity && str != NULL) + size = -1; - RGFW_windowQuitCallback(win); -} + if ((target == RGFW_XUTF8_STRING || target == XA_STRING) && str != NULL) { + RGFW_MEMCPY(str, data, sizeN); + str[sizeN] = '\0'; + XFree(data); + } else if (str != NULL) size = -1; -static void shm_format_handler(void *data, - struct wl_shm *shm, uint32_t format) -{ - RGFW_UNUSED(data); RGFW_UNUSED(shm); - #ifdef RGFW_DEBUG - fprintf(stderr, "Format %d\n", format); + XDeleteProperty(event.xselection.display, event.xselection.requestor, event.xselection.property); + size = sizeN; + return size; + #endif + #if defined(RGFW_WAYLAND) + wayland: return 0; #endif } -static const struct wl_shm_listener shm_listener = { - .format = shm_format_handler, -}; - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure_handler, - .close = xdg_toplevel_close_handler, -}; - -RGFW_window* RGFW_mouse_win = NULL; - -static void pointer_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface_x); RGFW_UNUSED(surface_y); - RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); - RGFW_mouse_win = win; - - RGFW_event ev; - ev.type = RGFW_mouseEnter; - ev.point = win->event.point; - - RGFW_eventPipe_push(win, ev); - - RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_TRUE); -} -static void pointer_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { - RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(serial); RGFW_UNUSED(surface); - RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); - if (RGFW_mouse_win == win) - RGFW_mouse_win = NULL; - - RGFW_event ev; - ev.type = RGFW_mouseLeave; - ev.point = win->event.point; - RGFW_eventPipe_push(win, ev); - - RGFW_mouseNotifyCallBack(win, win->event.point, RGFW_FALSE); -} -static void pointer_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) { - RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(x); RGFW_UNUSED(y); - - RGFW_ASSERT(RGFW_mouse_win != NULL); - - RGFW_event ev; - ev.type = RGFW_mousePosChanged; - ev.point = RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y)); - RGFW_eventPipe_push(RGFW_mouse_win, ev); - - RGFW_mousePosCallback(RGFW_mouse_win, RGFW_POINT(wl_fixed_to_double(x), wl_fixed_to_double(y))); -} -static void pointer_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(serial); - RGFW_ASSERT(RGFW_mouse_win != NULL); - - u32 b = (button - 0x110) + 1; - - /* flip right and middle button codes */ - if (b == 2) b = 3; - else if (b == 3) b = 2; - - RGFW_mouseButtons[b].prev = RGFW_mouseButtons[b].current; - RGFW_mouseButtons[b].current = state; - - RGFW_event ev; - ev.type = RGFW_mouseButtonPressed + state; - ev.button = b; - RGFW_eventPipe_push(RGFW_mouse_win, ev); - - RGFW_mouseButtonCallback(RGFW_mouse_win, b, 0, state); -} -static void pointer_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { - RGFW_UNUSED(data); RGFW_UNUSED(pointer); RGFW_UNUSED(time); RGFW_UNUSED(axis); - RGFW_ASSERT(RGFW_mouse_win != NULL); - - double scroll = wl_fixed_to_double(value); - - RGFW_event ev; - ev.type = RGFW_mouseButtonPressed; - ev.button = RGFW_mouseScrollUp + (scroll < 0); - RGFW_eventPipe_push(RGFW_mouse_win, ev); - - RGFW_mouseButtonCallback(RGFW_mouse_win, RGFW_mouseScrollUp + (scroll < 0), scroll, 1); -} - -void RGFW_doNothing(void) { } -static struct wl_pointer_listener pointer_listener = (struct wl_pointer_listener){&pointer_enter, &pointer_leave, &pointer_motion, &pointer_button, &pointer_axis, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing, (void*)&RGFW_doNothing}; - -static void keyboard_keymap (void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size) { - RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(format); - - char *keymap_string = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); - xkb_keymap_unref (keymap); - keymap = xkb_keymap_new_from_string (xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); - - munmap (keymap_string, size); - close (fd); - xkb_state_unref (xkb_state); - xkb_state = xkb_state_new (keymap); -} -static void keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(keys); - - RGFW_key_win = (RGFW_window*)wl_surface_get_user_data(surface); - - RGFW_event ev; - ev.type = RGFW_focusIn; - ev.inFocus = RGFW_TRUE; - RGFW_key_win->event.inFocus = RGFW_TRUE; - - RGFW_eventPipe_push((RGFW_window*)RGFW_mouse_win, ev); - - RGFW_focusCallback(RGFW_key_win, RGFW_TRUE); -} -static void keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface) { - RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); - - RGFW_window* win = (RGFW_window*)wl_surface_get_user_data(surface); - if (RGFW_key_win == win) - RGFW_key_win = NULL; - - RGFW_event ev; - ev.type = RGFW_focusOut; - ev.inFocus = RGFW_FALSE; - win->event.inFocus = RGFW_FALSE; - RGFW_eventPipe_push(win, ev); - - RGFW_focusCallback(win, RGFW_FALSE); -} -static void keyboard_key (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); - - RGFW_ASSERT(RGFW_key_win != NULL); - - xkb_keysym_t keysym = xkb_state_key_get_one_sym (xkb_state, key+8); - - u32 RGFW_key = RGFW_apiKeyToRGFW(key); - RGFW_keyboard[RGFW_key].prev = RGFW_keyboard[RGFW_key].current; - RGFW_keyboard[RGFW_key].current = state; - RGFW_event ev; - ev.type = RGFW_keyPressed + state; - ev.key = RGFW_key; - ev.keyChar = (u8)keysym; - - ev.repeat = RGFW_isHeld(RGFW_key_win, RGFW_key); - RGFW_eventPipe_push(RGFW_key_win, ev); - - RGFW_updateKeyMods(RGFW_key_win, xkb_keymap_mod_get_index(keymap, "Lock"), xkb_keymap_mod_get_index(keymap, "Mod2")); - - RGFW_keyCallback(RGFW_key_win, RGFW_key, (u8)keysym, RGFW_key_win->event.keyMod, state); -} -static void keyboard_modifiers (void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - RGFW_UNUSED(data); RGFW_UNUSED(keyboard); RGFW_UNUSED(serial); RGFW_UNUSED(time); - xkb_state_update_mask (xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); -} -static struct wl_keyboard_listener keyboard_listener = {&keyboard_keymap, &keyboard_enter, &keyboard_leave, &keyboard_key, &keyboard_modifiers, (void*)&RGFW_doNothing}; +void RGFW_writeClipboard(const char* text, u32 textLen) { + RGFW_GOTO_WAYLAND(); + #ifdef RGFW_X11 + RGFW_LOAD_ATOM(SAVE_TARGETS); + RGFW_LOAD_ATOM(TARGETS); + RGFW_LOAD_ATOM(MULTIPLE); + RGFW_LOAD_ATOM(ATOM_PAIR); + RGFW_LOAD_ATOM(CLIPBOARD_MANAGER); -static void seat_capabilities (void *data, struct wl_seat *seat, uint32_t capabilities) { - RGFW_UNUSED(data); + XSetSelectionOwner(RGFW_root->src.display, RGFW_XCLIPBOARD, RGFW_root->src.window, CurrentTime); - if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - struct wl_pointer *pointer = wl_seat_get_pointer (seat); - wl_pointer_add_listener (pointer, &pointer_listener, NULL); - } - if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - struct wl_keyboard *keyboard = wl_seat_get_keyboard (seat); - wl_keyboard_add_listener (keyboard, &keyboard_listener, NULL); - } -} -static struct wl_seat_listener seat_listener = {&seat_capabilities, (void*)&RGFW_doNothing}; + XConvertSelection(RGFW_root->src.display, CLIPBOARD_MANAGER, SAVE_TARGETS, None, RGFW_root->src.window, CurrentTime); + for (;;) { + XEvent event; -static void wl_global_registry_handler(void *data, - struct wl_registry *registry, uint32_t id, const char *interface, - uint32_t version) -{ - RGFW_window* win = (RGFW_window*)data; - RGFW_UNUSED(version); - if (RGFW_STRNCMP(interface, "wl_compositor", 16) == 0) { - win->src.compositor = wl_registry_bind(registry, - id, &wl_compositor_interface, 4); - } else if (RGFW_STRNCMP(interface, "xdg_wm_base", 12) == 0) { - win->src.xdg_wm_base = wl_registry_bind(registry, - id, &xdg_wm_base_interface, 1); - } else if (RGFW_STRNCMP(interface, zxdg_decoration_manager_v1_interface.name, 255) == 0) { - decoration_manager = wl_registry_bind(registry, id, &zxdg_decoration_manager_v1_interface, 1); - } else if (RGFW_STRNCMP(interface, "wl_shm", 7) == 0) { - win->src.shm = wl_registry_bind(registry, - id, &wl_shm_interface, 1); - wl_shm_add_listener(win->src.shm, &shm_listener, NULL); - } else if (RGFW_STRNCMP(interface,"wl_seat", 8) == 0) { - win->src.seat = wl_registry_bind(registry, id, &wl_seat_interface, 1); - wl_seat_add_listener(win->src.seat, &seat_listener, NULL); - } + XNextEvent(RGFW_root->src.display, &event); + if (event.type != SelectionRequest) { + break; + } - else { - #ifdef RGFW_DEBUG - printf("did not register %s\n", interface); - return; - #endif - } + const XSelectionRequestEvent* request = &event.xselectionrequest; - #ifdef RGFW_DEBUG - printf("registered %s\n", interface); - #endif -} + XEvent reply = { SelectionNotify }; + reply.xselection.property = 0; -static void wl_global_registry_remove(void *data, struct wl_registry *registry, uint32_t name) { RGFW_UNUSED(data); RGFW_UNUSED(registry); RGFW_UNUSED(name); } -static const struct wl_registry_listener registry_listener = { - .global = wl_global_registry_handler, - .global_remove = wl_global_registry_remove, -}; + if (request->target == TARGETS) { + const Atom targets[] = { TARGETS, + MULTIPLE, + RGFW_XUTF8_STRING, + XA_STRING }; -static const char *get_mode_name(enum zxdg_toplevel_decoration_v1_mode mode) { - switch (mode) { - case ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE: - return "client-side decorations"; - case ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE: - return "server-side decorations"; - } - abort(); -} + XChangeProperty(RGFW_root->src.display, + request->requestor, + request->property, + 4, + 32, + PropModeReplace, + (u8*) targets, + sizeof(targets) / sizeof(targets[0])); + reply.xselection.property = request->property; + } -static void decoration_handle_configure(void *data, - struct zxdg_toplevel_decoration_v1 *decoration, - enum zxdg_toplevel_decoration_v1_mode mode) { - RGFW_UNUSED(data); RGFW_UNUSED(decoration); - #ifdef RGFW_DEBUG - printf("Using %s\n", get_mode_name(mode)); - #endif - RGFW_current_mode = mode; -} + if (request->target == MULTIPLE) { + Atom* targets = NULL; -static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { - .configure = decoration_handle_configure, -}; + Atom actualType = 0; + int actualFormat = 0; + unsigned long count = 0, bytesAfter = 0; -static void randname(char *buf) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; - for (int i = 0; i < 6; ++i) { - buf[i] = 'A'+(r&15)+(r&16)*2; - r >>= 5; - } -} + XGetWindowProperty(RGFW_root->src.display, request->requestor, request->property, 0, LONG_MAX, False, ATOM_PAIR, &actualType, &actualFormat, &count, &bytesAfter, (u8**) &targets); -size_t wl_stringlen(char* name) { - size_t i = 0; - for (i; name[i]; i++); - return i; -} + unsigned long i; + for (i = 0; i < (u32)count; i += 2) { + if (targets[i] == RGFW_XUTF8_STRING || targets[i] == XA_STRING) { + XChangeProperty(RGFW_root->src.display, + request->requestor, + targets[i + 1], + targets[i], + 8, + PropModeReplace, + (u8*) text, + textLen); + XFlush(RGFW_root->src.display); + } else { + targets[i + 1] = None; + } + } -static int anonymous_shm_open(void) { - char name[] = "/RGFW-wayland-XXXXXX"; - int retries = 100; + XChangeProperty(RGFW_root->src.display, + request->requestor, + request->property, + ATOM_PAIR, + 32, + PropModeReplace, + (u8*) targets, + count); - do { - randname(name + wl_stringlen(name) - 6); + XFlush(RGFW_root->src.display); + XFree(targets); - --retries; - // shm_open guarantees that O_CLOEXEC is set - int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd >= 0) { - shm_unlink(name); - return fd; + reply.xselection.property = request->property; } - } while (retries > 0 && errno == EEXIST); - return -1; -} + reply.xselection.display = request->display; + reply.xselection.requestor = request->requestor; + reply.xselection.selection = request->selection; + reply.xselection.target = request->target; + reply.xselection.time = request->time; -int create_shm_file(off_t size) { - int fd = anonymous_shm_open(); - if (fd < 0) { - return fd; + XSendEvent(RGFW_root->src.display, request->requestor, False, 0, &reply); + XFlush(RGFW_root->src.display); } + #endif + #if defined(RGFW_WAYLAND) + wayland: + #endif +} - if (ftruncate(fd, size) < 0) { - close(fd); - return -1; - } +u8 RGFW_window_isFullscreen(RGFW_window* win) { + RGFW_ASSERT(win != NULL); - return fd; -} + XWindowAttributes windowAttributes; + XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); -static void wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - RGFW_UNUSED(data); RGFW_UNUSED(cb); RGFW_UNUSED(time); + /* check if the window is visable */ + if (windowAttributes.map_state != IsViewable) + return 0; - #ifdef RGFW_BUFFER - RGFW_window* win = (RGFW_window*)data; - if ((win->_flags & RGFW_NO_CPU_RENDER)) - return; - - wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); - wl_surface_damage_buffer(win->src.surface, 0, 0, win->r.w, win->r.h); - wl_surface_commit(win->src.surface); - #endif + /* check if the window covers the full screen */ + return (windowAttributes.x == 0 && windowAttributes.y == 0 && + windowAttributes.width == XDisplayWidth(win->src.display, DefaultScreen(win->src.display)) && + windowAttributes.height == XDisplayHeight(win->src.display, DefaultScreen(win->src.display))); } -static const struct wl_callback_listener wl_surface_frame_listener = { - .done = wl_surface_frame_done, -}; +u8 RGFW_window_isHidden(RGFW_window* win) { + RGFW_ASSERT(win != NULL); -/* normal wayland RGFW stuff */ + XWindowAttributes windowAttributes; + XGetWindowAttributes(win->src.display, win->src.window, &windowAttributes); -RGFW_area RGFW_getScreenSize(void) { - RGFW_area area = {}; + return (windowAttributes.map_state == IsUnmapped && !RGFW_window_isMinimized(win)); +} - if (RGFW_root != NULL) - /* this isn't right but it's here for buffers */ - area = RGFW_AREA(RGFW_root->r.w, RGFW_root->r.h); - - #ifndef RGFW_NO_WAYLAND_X11 - #ifndef RGFW_DEBUG - printf("Reverting to X11 layer for RGFW_getScreenSize\n"); - #endif +u8 RGFW_window_isMinimized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); - Screen* scrn = DefaultScreenOfDisplay(x11_display); - return RGFW_AREA(scrn->width, scrn->height); - #endif + RGFW_LOAD_ATOM(WM_STATE); + Atom actual_type; + i32 actual_format; + unsigned long nitems, bytes_after; + unsigned char* prop_data; - /* TODO wayland */ - return area; -} + i16 status = XGetWindowProperty(win->src.display, win->src.window, WM_STATE, 0, 2, False, + AnyPropertyType, &actual_type, &actual_format, + &nitems, &bytes_after, &prop_data); -void RGFW_releaseCursor(RGFW_window* win) { - RGFW_UNUSED(win); + if (status == Success && nitems >= 1 && *((int*) prop_data) == IconicState) { + XFree(prop_data); + return 1; + } + + if (prop_data != NULL) + XFree(prop_data); - /* TODO wayland */ + return 0; } -void RGFW_captureCursor(RGFW_window* win, RGFW_rect r) { - RGFW_UNUSED(win); RGFW_UNUSED(r); +u8 RGFW_window_isMaximized(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_LOAD_ATOM(_NET_WM_STATE); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); + RGFW_LOAD_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); - /* TODO wayland */ -} + Atom actual_type; + i32 actual_format; + unsigned long nitems, bytes_after; + unsigned char* prop_data; + i16 status = XGetWindowProperty(win->src.display, win->src.window, _NET_WM_STATE, 0, 1024, False, + XA_ATOM, &actual_type, &actual_format, + &nitems, &bytes_after, &prop_data); -RGFWDEF void RGFW_init_buffer(RGFW_window* win, void* vi); -void RGFW_init_buffer(RGFW_window* win, void* vi) { - #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) - size_t size = win->r.w * win->r.h * 4; - int fd = create_shm_file(size); - if (fd < 0) { - fprintf(stderr, "Failed to create a buffer. size: %ld\n", size); - exit(1); - } - - if (RGFW_bufferSize.w == 0 && RGFW_bufferSize.h == 0) - RGFW_bufferSize = RGFW_getScreenSize(); + if (status != Success) { + if (prop_data != NULL) + XFree(prop_data); - win->buffer = (u8*)RGFW_alloc(RGFW_bufferSize.w * RGFW_bufferSize.h * 4); - win->src.buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (win->src.buffer == MAP_FAILED) { - fprintf(stderr, "mmap failed!\n"); - close(fd); - exit(1); - } + return 0; + } - win->_flags |= RGFW_BUFFER_ALLOC; + Atom* atoms = (Atom*) prop_data; + u64 i; + for (i = 0; i < nitems; ++i) { + if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT || + atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { + XFree(prop_data); + return 1; + } + } - struct wl_shm_pool* pool = wl_shm_create_pool(win->src.shm, fd, size); - win->src.wl_buffer = wl_shm_pool_create_buffer(pool, 0, win->r.w, win->r.h, win->r.w * 4, - WL_SHM_FORMAT_ARGB8888); - wl_shm_pool_destroy(pool); + return 0; +} - close(fd); +static float XGetSystemContentDPI(Display* display, i32 screen) { + float dpi = 96.0f; - wl_surface_attach(win->src.surface, win->src.wl_buffer, 0, 0); - wl_surface_commit(win->src.surface); + #ifndef RGFW_NO_DPI + RGFW_UNUSED(screen); + char* rms = XResourceManagerString(display); + XrmDatabase db = NULL; - u8 color[] = {0x00, 0x00, 0x00, 0xFF}; + if (rms && db) { + db = XrmGetStringDatabase(rms); + XrmValue value; + char* type = NULL; - size_t i; - for (i = 0; i < RGFW_bufferSize.w * RGFW_bufferSize.h * 4; i += 4) { - RGFW_MEMCPY(&win->buffer[i], color, 4); + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value) && type && RGFW_STRNCMP(type, "String", 7) == 0) { + dpi = (float)atof(value.addr); + } + XrmDestroyDatabase(db); } - - RGFW_MEMCPY(win->src.buffer, win->buffer, win->r.w * win->r.h * 4); - - #if defined(RGFW_OSMESA) - win->src.ctx = OSMesaCreateContext(OSMESA_BGRA, NULL); - OSMesaMakeCurrent(win->src.ctx, win->buffer, GL_UNSIGNED_BYTE, RGFW_bufferSize.w, RGFW_bufferSize.h); - #endif #else - RGFW_UNUSED(win); + dpi = RGFW_ROUND(DisplayWidth(display, screen) / (DisplayWidthMM(display, screen) / 25.4)); #endif + + return dpi; } -RGFW_window* RGFW_createWindowPtr(const char* name, RGFW_rect rect, RGFW_windowFlags flags, RGFW_window* win) { - #ifndef RGFW_NO_WAYLAND_X11 - if (x11_display == NULL) { - x11_display = XOpenDisplay(NULL); +RGFW_monitor RGFW_XCreateMonitor(i32 screen) { + RGFW_monitor monitor; + + Display* display = XOpenDisplay(NULL); + + RGFW_area size = RGFW_getScreenSize(); - Window root = DefaultRootWindow(x11_display); + monitor.rect = RGFW_RECT(0, 0, size.w, size.h); + monitor.physW = DisplayWidthMM(display, screen) / 25.4; + monitor.physH = DisplayHeightMM(display, screen) / 25.4; - XSetWindowAttributes attributes; - attributes.background_pixel = 0; - attributes.override_redirect = True; + char* name = XDisplayName((const char*)display); + RGFW_MEMCPY(monitor.name, name, 128); - x11_window = XCreateWindow(x11_display, root, 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, - CWBackPixel | CWOverrideRedirect, &attributes); + float dpi = XGetSystemContentDPI(display, screen); + monitor.pixelRatio = dpi / 96.0f; - XMapWindow(x11_display, x11_window); - } - #endif + #ifndef RGFW_NO_DPI + XRRScreenResources* sr = XRRGetScreenResourcesCurrent(display, RootWindow(display, screen)); - RGFW_window_basic_init(win, rect, flags); + XRRCrtcInfo* ci = NULL; + int crtc = screen; - win->src.compositor = NULL; + if (sr->ncrtc > crtc) { + ci = XRRGetCrtcInfo(display, sr, sr->crtcs[crtc]); + } + #endif - fprintf(stderr, "Warning: RGFW Wayland support is experimental\n"); + float ppi_width = RGFW_ROUND((float)monitor.rect.w/(float)monitor.physW); + float ppi_height = RGFW_ROUND((float)monitor.rect.h/(float)monitor.physH); - win->src.display = wl_display_connect(NULL); - if (win->src.display == NULL) { - #ifdef RGFW_DEBUG - fprintf(stderr, "Failed to load Wayland display\n"); - #endif - return NULL; - } + monitor.scaleX = (float) (ppi_width) / dpi; + monitor.scaleY = (float) (ppi_height) / dpi; - struct wl_registry *registry = wl_display_get_registry(win->src.display); - wl_registry_add_listener(registry, ®istry_listener, win); + #ifndef RGFW_NO_DPI + XRROutputInfo* info = XRRGetOutputInfo (display, sr, sr->outputs[screen]); - wl_display_roundtrip(win->src.display); - wl_display_dispatch(win->src.display); + if (info == NULL || ci == NULL) { + XRRFreeScreenResources(sr); + XCloseDisplay(display); - if (win->src.compositor == NULL) { - #ifdef RGFW_DEBUG - fprintf(stderr, "Can't find compositor.\n"); - #endif + #ifdef RGFW_DEBUG + printf("RGFW INFO: monitor found: scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n", monitor.name, monitor.rect.x, monitor.rect.y, monitor.rect.w, monitor.rect.h, monitor.physW, monitor.physH, monitor.scaleX, monitor.scaleY, monitor.pixelRatio); + #endif + return monitor; + } - return NULL; - } - if (RGFW_wl_cursor_theme == NULL) { - RGFW_wl_cursor_theme = wl_cursor_theme_load(NULL, 24, win->src.shm); - RGFW_cursor_surface = wl_compositor_create_surface(win->src.compositor); + float physW = info->mm_width / 25.4; + float physH = info->mm_height / 25.4; - struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, "left_ptr"); - RGFW_cursor_image = cursor->images[0]; - struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); + RGFW_MEMCPY(monitor.name, info->name, 128); - wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); - wl_surface_commit(RGFW_cursor_surface); + if (physW && physH) { + monitor.physW = physW; + monitor.physH = physH; } - xdg_wm_base_add_listener(win->src.xdg_wm_base, &xdg_wm_base_listener, NULL); - - xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + monitor.rect.x = ci->x; + monitor.rect.y = ci->y; - win->src.surface = wl_compositor_create_surface(win->src.compositor); - wl_surface_set_user_data(win->src.surface, win); + float w = ci->width; + float h = ci->height; - win->src.xdg_surface = xdg_wm_base_get_xdg_surface(win->src.xdg_wm_base, win->src.surface); - xdg_surface_add_listener(win->src.xdg_surface, &xdg_surface_listener, NULL); + if (w && h) { + monitor.rect.w = w; + monitor.rect.h = h; + } + #endif - xdg_wm_base_set_user_data(win->src.xdg_wm_base, win); + if (monitor.physW == 0 || monitor.physH == 0) { + monitor.scaleX = 0; + monitor.scaleY = 0; + } else { + float ppi_width = RGFW_ROUND((float)monitor.rect.w/(float)monitor.physW); + float ppi_height = RGFW_ROUND((float)monitor.rect.h/(float)monitor.physH); - win->src.xdg_toplevel = xdg_surface_get_toplevel(win->src.xdg_surface); - xdg_toplevel_set_user_data(win->src.xdg_toplevel, win); - xdg_toplevel_set_title(win->src.xdg_toplevel, name); - xdg_toplevel_add_listener(win->src.xdg_toplevel, &xdg_toplevel_listener, NULL); + monitor.scaleX = (float) (ppi_width) / (float) dpi; + monitor.scaleY = (float) (ppi_height) / (float) dpi; - xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); + if ((monitor.scaleX > 1 && monitor.scaleX < 1.1)) + monitor.scaleX = 1; - if (!(flags & RGFW_windowNoBorder)) { - win->src.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( - decoration_manager, win->src.xdg_toplevel); + if ((monitor.scaleY > 1 && monitor.scaleY < 1.1)) + monitor.scaleY = 1; } - if (flags & RGFW_windowCenter) { - RGFW_area screenR = RGFW_getScreenSize(); - RGFW_window_move(win, RGFW_POINT((screenR.w - win->r.w) / 2, (screenR.h - win->r.h) / 2)); - } + #ifndef RGFW_NO_DPI + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + #endif - if (flags & RGFW_windowOpenglSoftware) - setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1); + XCloseDisplay(display); - wl_display_roundtrip(win->src.display); + #ifdef RGFW_DEBUG + printf("RGFW INFO: monitor found: scale (%s):\n rect: {%i, %i, %i, %i}\n physical size:%f %f\n scale: %f %f\n pixelRatio: %f\n", monitor.name, monitor.rect.x, monitor.rect.y, monitor.rect.w, monitor.rect.h, monitor.physW, monitor.physH, monitor.scaleX, monitor.scaleY, monitor.pixelRatio); + #endif - wl_surface_commit(win->src.surface); + return monitor; +} - /* wait for the surface to be configured */ - while (wl_display_dispatch(win->src.display) != -1 && !RGFW_wl_configured) { } - - #ifdef RGFW_OPENGL - if ((flags & RGFW_windowNoInitAPI) == 0) { - win->src.window = wl_egl_window_create(win->src.surface, win->r.w, win->r.h); - RGFW_createOpenGLContext(win); - } +RGFW_monitor RGFW_monitors[6]; +RGFW_monitor* RGFW_getMonitors(void) { + RGFW_GOTO_WAYLAND(); + #ifdef RGFW_X11 + size_t i; + for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++) + RGFW_monitors[i] = RGFW_XCreateMonitor(i); + + return RGFW_monitors; + #endif + #ifdef RGFW_WAYLAND + wayland: return RGFW_monitors; // TODO WAYLAND #endif +} - RGFW_init_buffer(win, NULL); +RGFW_monitor RGFW_getPrimaryMonitor(void) { + RGFW_GOTO_WAYLAND(); + #ifdef RGFW_X11 + RGFW_ASSERT(RGFW_root != NULL); + return RGFW_XCreateMonitor(DefaultScreen(RGFW_root->src.display)); + #endif + #ifdef RGFW_WAYLAND + wayland: return (RGFW_monitor){ }; // TODO WAYLAND + #endif +} - struct wl_callback* callback = wl_surface_frame(win->src.surface); - wl_callback_add_listener(callback, &wl_surface_frame_listener, win); - wl_surface_commit(win->src.surface); +RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { + RGFW_ASSERT(win != NULL); - if (flags & RGFW_windowHideMouse) { - RGFW_window_showMouse(win, 0); + XWindowAttributes attrs; + if (!XGetWindowAttributes(win->src.display, win->src.window, &attrs)) { + return (RGFW_monitor){}; + } + + #ifndef RGFW_NO_DPI + XRRScreenResources* screenRes = XRRGetScreenResources(win->src.display, DefaultRootWindow(win->src.display)); + if (screenRes == NULL) { + return (RGFW_monitor){}; } - win->src.eventIndex = 0; - win->src.eventLen = 0; + for (int i = 0; i < screenRes->ncrtc; i++) { + XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(win->src.display, screenRes, screenRes->crtcs[i]); + if (!crtcInfo) continue; - #ifdef RGFW_DEBUG - printf("RGFW INFO: a window with a rect of {%i, %i, %i, %i} \n", win->r.x, win->r.y, win->r.w, win->r.h); - #endif + int monitorX = crtcInfo->x; + int monitorY = crtcInfo->y; + int monitorWidth = crtcInfo->width; + int monitorHeight = crtcInfo->height; - return win; -} + if (attrs.x >= monitorX && + attrs.x < monitorX + monitorWidth && + attrs.y >= monitorY && + attrs.y < monitorY + monitorHeight) { + XRRFreeCrtcInfo(crtcInfo); + XRRFreeScreenResources(screenRes); + return RGFW_XCreateMonitor(i); + } -RGFW_event* RGFW_window_checkEvent(RGFW_window* win) { - if (win->_flags & RGFW_windowHide) - return NULL; + XRRFreeCrtcInfo(crtcInfo); + } - #ifndef RGFW_NO_WAYLAND_X11 - XEvent event; + XRRFreeScreenResources(screenRes); + #else + size_t i; + for (i = 0; i < (size_t)ScreenCount(RGFW_root->src.display) && i < 6; i++) { + Screen* screen = ScreenOfDisplay(RGFW_root->src.display, i); + if (attrs.x >= 0 && attrs.x < 0 + XWidthOfScreen(screen) && + attrs.y >= 0 && attrs.y < 0 + XHeightOfScreen(screen)) + return RGFW_XCreateMonitor(i); + } + #endif + return (RGFW_monitor){}; +} - XPending(x11_display); +#if defined(RGFW_OPENGL) && !defined(RGFW_EGL) - /* if there is no unread qued events, get a new one */ - if ((QLength(x11_display) || XEventsQueued(x11_display, QueuedAlready) + XEventsQueued(x11_display, QueuedAfterReading))) { - XNextEvent(x11_display, &event); - } - #endif +void RGFW_window_makeCurrent_OpenGL(RGFW_window* win) { + if (win == NULL) + glXMakeCurrent(NULL, (Drawable)NULL, (GLXContext) NULL); + else + glXMakeCurrent(win->src.display, (Drawable) win->src.window, (GLXContext) win->src.ctx); +} +#endif - #if defined(__linux__) - if (RGFW_linux_updateGamepad(win)) { return &win->event; } - #endif - if (win->src.eventIndex == 0) { - if (wl_display_roundtrip(win->src.display) == -1) { - return NULL; - } +void RGFW_window_swapBuffers(RGFW_window* win) { + RGFW_ASSERT(win != NULL); + RGFW_GOTO_WAYLAND(); +#ifdef RGFW_X11 + /* clear the window*/ + if (!(win->_flags & RGFW_NO_CPU_RENDER)) { + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + RGFW_area area = RGFW_bufferSize; + win->src.bitmap->data = (char*) win->buffer; + #if !defined(RGFW_X11_DONT_CONVERT_BGR) && !defined(RGFW_OSMESA) + u32 x, y; + for (y = 0; y < (u32)win->r.h; y++) { + for (x = 0; x < (u32)win->r.w; x++) { + u32 index = (y * 4 * area.w) + x * 4; - //wl_display_dispatch(win->src.display); - //wl_display_flush(win->src.display); + u8 red = win->src.bitmap->data[index]; + win->src.bitmap->data[index] = win->buffer[index + 2]; + win->src.bitmap->data[index + 2] = red; - RGFW_resetKey(); + } + } + #endif + XPutImage(win->src.display, win->src.window, win->src.gc, win->src.bitmap, 0, 0, 0, 0, RGFW_bufferSize.w, RGFW_bufferSize.h); + win->src.bitmap->data = NULL; + #endif } - if (win->src.eventLen == 0) { - return NULL; + if (!(win->_flags & RGFW_NO_GPU_RENDER)) { + #ifdef RGFW_EGL + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); + #elif defined(RGFW_OPENGL) + glXSwapBuffers(win->src.display, win->src.window); + #endif } + return; +#endif +#ifdef RGFW_WAYLAND + wayland: + #if defined(RGFW_BUFFER) || defined(RGFW_OSMESA) + #if !defined(RGFW_X11_DONT_CONVERT_BGR) && !defined(RGFW_OSMESA) + for (u32 y = 0; y < (u32)win->r.h; y++) { + for (u32 x = 0; x < (u32)win->r.w; x++) { + u32 index = (y * 4 * win->r.w) + x * 4; + u32 index2 = (y * 4 * RGFW_bufferSize.w) + x * 4; - RGFW_event ev = RGFW_eventPipe_pop(win); + u8 red = win->buffer[index2]; + win->src.buffer[index] = win->buffer[index2 + 2]; + win->src.buffer[index + 1] = win->buffer[index2 + 1]; + win->src.buffer[index + 2] = red; + } + } + #else + for (size_t y = 0; y < win->r.h; y++) { + u32 index = (y * 4 * win->r.w); + u32 index2 = (y * 4 * RGFW_bufferSize.w); + RGFW_MEMCPY(&win->src.buffer[index], &win->buffer[index2], win->r.w * 4); + } + #endif - if (ev.type == 0 || win->event.type == RGFW_quit) { - return NULL; + wl_surface_frame_done(win, NULL, 0); + if (!(win->_flags & RGFW_NO_GPU_RENDER)) + #endif + { + #ifdef RGFW_OPENGL + eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); + #endif } - ev.frameTime = win->event.frameTime; - ev.frameTime2 = win->event.frameTime2; - ev.inFocus = win->event.inFocus; - win->event = ev; - return &win->event; + wl_surface_commit(win->src.surface); +#endif } +#if !defined(RGFW_EGL) -void RGFW_window_resize(RGFW_window* win, RGFW_area a) { - RGFW_UNUSED(win); RGFW_UNUSED(a); - win->r.w = a.w; - win->r.h = a.h; +void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { + RGFW_ASSERT(win != NULL); - xdg_surface_set_window_geometry(win->src.xdg_surface, 0, 0, win->r.w, win->r.h); - #ifdef RGFW_OPENGL - wl_egl_window_resize(win->src.window, a.w, a.h, 0, 0); + #if defined(RGFW_OPENGL) + ((PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress((GLubyte*) "glXSwapIntervalEXT"))(win->src.display, win->src.window, swapInterval); + #else + RGFW_UNUSED(swapInterval); #endif } +#endif -void RGFW_window_move(RGFW_window* win, RGFW_point v) { - RGFW_UNUSED(win); RGFW_UNUSED(v); - /* TODO wayland */ +void RGFW_window_close(RGFW_window* win) { RGFW_ASSERT(win != NULL); - struct wl_pointer *pointer = wl_seat_get_pointer(win->src.seat); - if (!pointer) { - return; - } + RGFW_GOTO_WAYLAND(); + #ifdef RGFW_X11 + /* ungrab pointer if it was grabbed */ + if (win->_flags & RGFW_HOLD_MOUSE) + XUngrabPointer(win->src.display, CurrentTime); - // Initiate the move operation - //wl_shell_surface_move(win->src.surface, seat, 0); - win->r.x = v.x; - win->r.y = v.y; - - wl_display_flush(win->src.display); -} + #ifdef RGFW_EGL + RGFW_closeEGL(win); + #endif -b32 RGFW_window_setIcon(RGFW_window* win, u8* src, RGFW_area a, i32 channels) { - RGFW_UNUSED(win); RGFW_UNUSED(src); RGFW_UNUSED(a); RGFW_UNUSED(channels); - /* TODO wayland */ - return 0; -} + if (RGFW_hiddenMouse != NULL && (RGFW_windowsOpen - 1) <= 0) { + RGFW_freeMouse(RGFW_hiddenMouse); + RGFW_hiddenMouse = 0; + } -void RGFW_window_moveMouse(RGFW_window* win, RGFW_point p) { - #ifdef RGFW_DEBUG - printf("Wayland: The platform does not support moving the mouse\n"); + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + if (win->buffer != NULL) { + if ((win->_flags & RGFW_BUFFER_ALLOC)) + win->_mem.free(win->_mem.userdata, win->buffer); + XDestroyImage((XImage*) win->src.bitmap); + XFreeGC(win->src.display, win->src.gc); + } #endif - #ifndef RGFW_NO_WAYLAND_X11 - XEvent event; - XQueryPointer(x11_display, DefaultRootWindow(x11_display), - &event.xbutton.root, &event.xbutton.window, - &event.xbutton.x_root, &event.xbutton.y_root, - &event.xbutton.x, &event.xbutton.y, - &event.xbutton.state); - - win->_lastMousePoint = RGFW_POINT(p.x - win->r.x, p.y - win->r.y); - if (event.xbutton.x == p.x && event.xbutton.y == p.y) - return; - - XWarpPointer(x11_display, None, x11_window, 0, 0, 0, 0, (int) p.x - win->r.x, (int) p.y - win->r.y); - XFlush(x11_display); + if (win->src.display) { + #if defined(RGFW_OPENGL) && !defined(RGFW_EGL) + glXDestroyContext(win->src.display, win->src.ctx); #endif - /* TODO wayland */ -} - -void RGFW_window_showMouse(RGFW_window* win, i8 show) { - RGFW_UNUSED(win); - if (show) { + if (win == RGFW_root) + RGFW_root = NULL; - } - else { + if ((Drawable) win->src.window) + XDestroyWindow(win->src.display, (Drawable) win->src.window); /*!< close the window*/ + XCloseDisplay(win->src.display); /*!< kill the display*/ } - /* TODO wayland */ -} + #ifdef RGFW_ALLOC_DROPFILES + { + u32 i; + for (i = 0; i < RGFW_MAX_DROPS; i++) + win->_mem.free(win->_mem.userdata, win->event.droppedFiles[i]); -b8 RGFW_window_isMaximized(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; -} -b8 RGFW_window_isMinimized(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; -} + win->_mem.free(win->_mem.userdata, win->event.droppedFiles); + } + #endif -b8 RGFW_window_isHidden(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; -} + /* set cleared display / window to NULL for error checking */ + win->src.display = 0; + win->src.window = 0; -b8 RGFW_window_isFullscreen(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return 0; -} + RGFW_windowsOpen--; -RGFW_point RGFW_window_getMousePoint(RGFW_window* win) { - RGFW_UNUSED(win); - /* TODO wayland */ - return RGFW_POINT(0, 0); -} + #define RGFW_FREE_LIBRARY(x) if (x != NULL) dlclose(x); x = NULL; + if (RGFW_windowsOpen <= 0) { + #if !defined(RGFW_NO_X11_CURSOR_PRELOAD) && !defined(RGFW_NO_X11_CURSOR) + RGFW_FREE_LIBRARY(X11Cursorhandle); + #endif + #if !defined(RGFW_NO_X11_XI_PRELOAD) + RGFW_FREE_LIBRARY(X11Xihandle); + #endif -RGFW_point RGFW_getGlobalMousePoint(void) { - /* TODO wayland */ - return RGFW_POINT(0, 0); -} + #ifdef RGFW_USE_XDL + XDL_close(); + #endif -void RGFW_window_show(RGFW_window* win) { - //wl_surface_attach(win->src.surface, win->rc., 0, 0); - wl_surface_commit(win->src.surface); + #ifndef RGFW_NO_PASSTHROUGH + RGFW_FREE_LIBRARY(RGFW_libxshape); + #endif - if (win->_flags & RGFW_windowHide) - win->_flags ^= RGFW_windowHide; -} + #ifndef RGFW_NO_LINUX + if (RGFW_eventWait_forceStop[0] || RGFW_eventWait_forceStop[1]){ + close(RGFW_eventWait_forceStop[0]); + close(RGFW_eventWait_forceStop[1]); + } -void RGFW_window_hide(RGFW_window* win) { - wl_surface_attach(win->src.surface, NULL, 0, 0); - wl_surface_commit(win->src.surface); - win->_flags |= RGFW_windowHide; -} + u8 i; + for (i = 0; i < RGFW_gamepadCount; i++) { + if(RGFW_gamepads[i]) + close(RGFW_gamepads[i]); + } + #endif + } + RGFW_clipboard_switch(NULL); + if ((win->_flags & RGFW_WINDOW_ALLOC)) + win->_mem.free(win->_mem.userdata, win); + return; + #endif -b32 RGFW_window_setMouseDefault(RGFW_window* win) { - RGFW_UNUSED(win); + #ifdef RGFW_WAYLAND + wayland: + #ifdef RGFW_EGL + RGFW_closeEGL(win); + #endif - return RGFW_window_setMouseStandard(win, RGFW_mouseNormal); -} + if (RGFW_root == win) { + RGFW_root = NULL; + } -b32 RGFW_window_setMouseStandard(RGFW_window* win, u8 mouse) { - RGFW_UNUSED(win); + xdg_toplevel_destroy(win->src.xdg_toplevel); + xdg_surface_destroy(win->src.xdg_surface); + wl_surface_destroy(win->src.surface); - static const char* iconStrings[] = { "left_ptr", "left_ptr", "text", "cross", "pointer", "e-resize", "n-resize", "nw-resize", "ne-resize", "all-resize", "not-allowed" }; + #if defined(RGFW_OSMESA) || defined(RGFW_BUFFER) + wl_buffer_destroy(win->src.wl_buffer); + if ((win->_flags & RGFW_BUFFER_ALLOC)) + win->_mem.free(win->_mem.userdata, win->buffer); - struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); - RGFW_cursor_image = cursor->images[0]; - struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); + munmap(win->src.buffer, win->r.w * win->r.h * 4); + #endif - wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); - wl_surface_commit(RGFW_cursor_surface); - return 1; + wl_display_disconnect(win->src.wl_display); + RGFW_clipboard_switch(NULL); + if ((win->_flags & RGFW_WINDOW_ALLOC)) + win->_mem.free(win->_mem.userdata, win); + #endif } -void* RGFW_loadMouse(u8* icon, RGFW_area a, i32 channels) { - RGFW_UNUSED(icon); RGFW_UNUSED(a); RGFW_UNUSED(channels); - //struct wl_cursor* cursor = wl_cursor_theme_get_cursor(RGFW_wl_cursor_theme, iconStrings[mouse]); - //RGFW_cursor_image = icon; - struct wl_buffer* cursor_buffer = wl_cursor_image_get_buffer(RGFW_cursor_image); - wl_surface_attach(RGFW_cursor_surface, cursor_buffer, 0, 0); - wl_surface_commit(RGFW_cursor_surface); +/* + End of X11 linux / wayland / unix defines +*/ - return cursor_buffer; -} +#include +#include +#include -void RGFW_window_setMouse(RGFW_window* win, RGFW_mouse* mouse) { - RGFW_UNUSED(win); RGFW_UNUSED(mouse); -} +void RGFW_stopCheckEvents(void) { -void RGFW_freeMouse(RGFW_mouse* mouse) { - RGFW_UNUSED(mouse); + RGFW_eventWait_forceStop[2] = 1; + while (1) { + const char byte = 0; + const ssize_t result = write(RGFW_eventWait_forceStop[1], &byte, 1); + if (result == 1 || result == -1) + break; + } } -void RGFW_window_setName(RGFW_window* win, const char* name) { - xdg_toplevel_set_title(win->src.xdg_toplevel, name); -} +void RGFW_window_eventWait(RGFW_window* win, i32 waitMS) { + if (waitMS == 0) + return; -void RGFW_window_setMousePassthrough(RGFW_window* win, b8 passthrough) { - RGFW_UNUSED(win); RGFW_UNUSED(passthrough); + u8 i; + if (RGFW_eventWait_forceStop[0] == 0 || RGFW_eventWait_forceStop[1] == 0) { + if (pipe(RGFW_eventWait_forceStop) != -1) { + fcntl(RGFW_eventWait_forceStop[0], F_GETFL, 0); + fcntl(RGFW_eventWait_forceStop[0], F_GETFD, 0); + fcntl(RGFW_eventWait_forceStop[1], F_GETFL, 0); + fcntl(RGFW_eventWait_forceStop[1], F_GETFD, 0); + } + } -/* TODO wayland */ -} + struct pollfd fds[] = { + #ifdef RGFW_WAYLAND + { wl_display_get_fd(win->src.wl_display), POLLIN, 0 }, + #else + { ConnectionNumber(win->src.display), POLLIN, 0 }, + #endif + { RGFW_eventWait_forceStop[0], POLLIN, 0 }, + #if defined(__linux__) + { -1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0 }, {-1, POLLIN, 0} + #endif + }; -void RGFW_window_setBorder(RGFW_window* win, b8 border) { - RGFW_UNUSED(win); RGFW_UNUSED(border); + u8 index = 2; -/* TODO wayland */ -} + #if defined(__linux__) + for (i = 0; i < RGFW_gamepadCount; i++) { + if (RGFW_gamepads[i] == 0) + continue; -void RGFW_window_restore(RGFW_window* win) { - RGFW_UNUSED(win); + fds[index].fd = RGFW_gamepads[i]; + index++; + } + #endif -/* TODO wayland */ -} -void RGFW_window_minimize(RGFW_window* win) { - RGFW_UNUSED(win); + u64 start = RGFW_getTimeNS(); -/* TODO wayland */ -} -void RGFW_window_setMaxSize(RGFW_window* win, RGFW_area a) { - RGFW_UNUSED(win); RGFW_UNUSED(a); + #ifdef RGFW_WAYLAND + while (wl_display_dispatch(win->src.wl_display) <= 0 && waitMS >= -1) { + #else + while (XPending(win->src.display) == 0 && waitMS >= -1) { + #endif + if (poll(fds, index, waitMS) <= 0) + break; -/* TODO wayland */ -} + if (waitMS > 0) { + waitMS -= (RGFW_getTimeNS() - start) / 1e+6; + } + } -void RGFW_window_setMinSize(RGFW_window* win, RGFW_area a) { - RGFW_UNUSED(win); RGFW_UNUSED(a); + /* drain any data in the stop request */ + if (RGFW_eventWait_forceStop[2]) { + char data[64]; + (void)!read(RGFW_eventWait_forceStop[0], data, sizeof(data)); - /* TODO wayland */ + RGFW_eventWait_forceStop[2] = 0; + } } -RGFW_monitor RGFW_window_getMonitor(RGFW_window* win) { - RGFW_monitor m = {}; - RGFW_UNUSED(win); - RGFW_UNUSED(m); - /* TODO wayland */ +u64 RGFW_getTimeNS(void) { + struct timespec ts = { 0, 0 }; + #ifndef RGFW_NO_UNIX_CLOCK + clock_gettime(1, &ts); + #endif + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - return m; + return nanoSeconds; } -#ifndef RGFW_EGL - void RGFW_window_swapInterval(RGFW_window* win, i32 swapInterval) { RGFW_UNUSED(win); RGFW_UNUSED(swapInterval); } -#endif - -void RGFW_window_swapBuffers(RGFW_window* win) { - RGFW_ASSERT(win != NULL); - - /* clear the window*/ - #if defined(RGFW_BUFFER) || defined(RGFW_OSMESA) - #if !defined(RGFW_X11_DONT_CONVERT_BGR) && !defined(RGFW_OSMESA) - for (u32 y = 0; y < (u32)win->r.h; y++) { - for (u32 x = 0; x < (u32)win->r.w; x++) { - u32 index = (y * 4 * win->r.w) + x * 4; - u32 index2 = (y * 4 * RGFW_bufferSize.w) + x * 4; - - u8 red = win->buffer[index2]; - win->src.buffer[index] = win->buffer[index2 + 2]; - win->src.buffer[index + 1] = win->buffer[index2 + 1]; - win->src.buffer[index + 2] = red; - } - } - #else - for (size_t y = 0; y < win->r.h; y++) { - u32 index = (y * 4 * win->r.w); - u32 index2 = (y * 4 * RGFW_bufferSize.w); - RGFW_MEMCPY(&win->src.buffer[index], &win->buffer[index2], win->r.w * 4); - } - #endif - - wl_surface_frame_done(win, NULL, 0); - if (!(win->_flags & RGFW_NO_GPU_RENDER)) - #endif - { - #ifdef RGFW_OPENGL - eglSwapBuffers(win->src.EGL_display, win->src.EGL_surface); +u64 RGFW_getTime(void) { + struct timespec ts = { 0, 0 }; + #ifndef RGFW_NO_UNIX_CLOCK + clock_gettime(1, &ts); #endif - } + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - wl_surface_commit(win->src.surface); - + return (double)(nanoSeconds) * 1e-9; } - - -#endif /* RGFW_WAYLAND */ - -/* - End of Wayland defines -*/ +#endif /* end of wayland or X11 defines*/ /* diff --git a/examples/basic/basic.c b/examples/basic/basic.c index 191bedbf..756fa771 100644 --- a/examples/basic/basic.c +++ b/examples/basic/basic.c @@ -59,7 +59,7 @@ int main(void) { if (win2) RGFW_window_checkEvent(win2); #endif - RGFW_window_eventWait(win, RGFW_eventWaitNext); + //RGFW_window_eventWait(win, RGFW_eventWaitNext); while (RGFW_window_checkEvent(win) != NULL) { if (win->event.type == RGFW_windowMoved) {