From 7068f399f4be1d87f635a8e95a16fa33fcb49c65 Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Sat, 6 Jan 2024 01:54:36 +0200 Subject: [PATCH] fix(zephr): change window type hint to be normal and fix fullscreen on WMs that use _NET_WM_ALLOWED_ACTIONS --- main.c | 3 +- zephr.c | 95 ++++++++++++++++++++++++++++++++++++++------------------- zephr.h | 5 +-- 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/main.c b/main.c index 431798d..051b08f 100644 --- a/main.c +++ b/main.c @@ -12,8 +12,7 @@ int main(int argc, char *argv[]) { CORE_UNUSED(argc); CORE_UNUSED(argv); - zephr_init(font_path, game_icon_path, title, (Size){1200, 800}); - zephr_make_window_non_resizable(); + zephr_init(font_path, game_icon_path, title, (Size){1200, 800}, true); /* zephr_toggle_fullscreen(); */ game_loop(); diff --git a/zephr.c b/zephr.c index 21a6afd..369aeca 100644 --- a/zephr.c +++ b/zephr.c @@ -488,9 +488,22 @@ void x11_resize_window(void) { glViewport(0, 0, win_attrs.width, win_attrs.height); } -// BUG: fullscreen doesn't work on gnome. may or may not be related to the window being -// opened as a dialog window void x11_go_fullscreen(void) { + // remove the resizing constraint before going fullscreen so WMs such as gnome + // can add the _NET_WM_ACTION_FULLSCREEN to the _NET_WM_ALLOWED_ACTIONS atom + // and properly fullscreen the window + XSizeHints *size_hints = XAllocSizeHints(); + + zephr_ctx->window.pre_fullscreen_size.width = zephr_ctx->window.size.width; + zephr_ctx->window.pre_fullscreen_size.height = zephr_ctx->window.size.height; + if (size_hints) { + size_hints->flags = PPosition | PSize; + size_hints->width = zephr_ctx->window.size.width; + size_hints->height = zephr_ctx->window.size.height; + XSetWMNormalHints(x11_display, x11_window, size_hints); + XFree(size_hints); + } + XEvent xev; Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); Atom fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); @@ -520,6 +533,25 @@ void x11_return_fullscreen(void) { xev.xclient.data.l[2] = 0; XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureNotifyMask | SubstructureRedirectMask, &xev); + + // restore the resizing constraint as well as the pre-fullscreen window size + // when returning from fullscreen + XSizeHints *size_hints = XAllocSizeHints(); + + if (size_hints) { + size_hints->flags = PPosition | PSize; + size_hints->width = zephr_ctx->window.pre_fullscreen_size.width; + size_hints->height = zephr_ctx->window.pre_fullscreen_size.height; + if (zephr_ctx->window.non_resizable) { + size_hints->flags |= PMinSize | PMaxSize; + size_hints->min_width = zephr_ctx->window.pre_fullscreen_size.width; + size_hints->min_height = zephr_ctx->window.pre_fullscreen_size.height; + size_hints->max_width = zephr_ctx->window.pre_fullscreen_size.width; + size_hints->max_height = zephr_ctx->window.pre_fullscreen_size.height; + } + XSetWMNormalHints(x11_display, x11_window, size_hints); + XFree(size_hints); + } } void x11_toggle_fullscreen(bool fullscreen) { @@ -557,7 +589,7 @@ void x11_assign_window_icon(const char *icon_path) { stbi_image_free(icon_data); } -int x11_create_window(const char* title, int window_width, int window_height, const char *icon_path) { +int x11_create_window(const char* title, int window_width, int window_height, const char *icon_path, bool window_non_resizable) { x11_display = XOpenDisplay(NULL); CORE_ASSERT(x11_display, "Cannot open x11 display connection\n"); @@ -585,12 +617,11 @@ int x11_create_window(const char* title, int window_width, int window_height, co x11_assign_window_icon(icon_path); } - // Hints to the WM that the window is a dialog window which makes it a floating - // window in tiling WMs (only tested on DWM though) + // Hints to the WM that the window is a normal window // Of course this is only a hint and the WM can ignore it Atom net_wm_window_type = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); - Atom net_wm_window_type_dialog = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_DIALOG", False); - XChangeProperty(x11_display, x11_window, net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_window_type_dialog, 1); + Atom net_wm_window_type_normal = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False); + XChangeProperty(x11_display, x11_window, net_wm_window_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_window_type_normal, 1); Atom wm_delete_window = XInternAtom(x11_display, "WM_DELETE_WINDOW", False); XSetWMProtocols(x11_display, x11_window, &wm_delete_window, 1); @@ -613,14 +644,33 @@ int x11_create_window(const char* title, int window_width, int window_height, co XClassHint *class_hint = XAllocClassHint(); - if (class_hint) - { + if (class_hint) { class_hint->res_name = class_hint->res_class = (char *)title; XSetClassHint(x11_display, x11_window, class_hint); XFree(class_hint); } } + XSizeHints *size_hints = XAllocSizeHints(); + + if (size_hints) { + size_hints->flags = PPosition | PSize; + /* size_hints->win_gravity = StaticGravity; */ + size_hints->x = window_start_x; + size_hints->y = window_start_y; + size_hints->width = window_width; + size_hints->height = window_height; + if (window_non_resizable) { + size_hints->flags |= PMinSize | PMaxSize; + size_hints->min_width = window_width; + size_hints->min_height = window_height; + size_hints->max_width = window_width; + size_hints->max_height = window_height; + } + XSetWMNormalHints(x11_display, x11_window, size_hints); + XFree(size_hints); + } + XMapWindow(x11_display, x11_window); CORE_ASSERT(x11_window, "Failed to create window\n"); @@ -689,8 +739,8 @@ int x11_create_window(const char* title, int window_width, int window_height, co return 0; } -int x11_init(const char* title, int window_width, int window_height, const char *icon_path) { - int res = x11_create_window(title, window_width, window_height, icon_path); +int x11_init(const char* title, int window_width, int window_height, const char *icon_path, bool window_non_resizable) { + int res = x11_create_window(title, window_width, window_height, icon_path, window_non_resizable); /* // loads the XMODIFIERS environment variable to see what IME to use */ /* XSetLocaleModifiers(""); */ @@ -752,17 +802,6 @@ void x11_get_screen_size(int *width, int *height) { *height = screen->height; } -void x11_make_window_non_resizable(int width, int height) { - XSizeHints *size_hints = XAllocSizeHints(); - size_hints->flags = PMinSize | PMaxSize; - size_hints->min_width = width; - size_hints->min_height = height; - size_hints->max_width = width; - size_hints->max_height = height; - XSetWMNormalHints(x11_display, x11_window, size_hints); - XFree(size_hints); -} - //////////////////////////// // // Zephr @@ -770,20 +809,21 @@ void x11_make_window_non_resizable(int width, int height) { /////////////////////////// -void zephr_init(const char* font_path, const char* icon_path, const char* window_title, Size window_size) { +void zephr_init(const char* font_path, const char* icon_path, const char* window_title, Size window_size, bool window_non_resizable) { zephr_ctx = calloc(1, sizeof(ZephrContext)); // TODO: should I initalize the audio here or let the game handle that?? int res = audio_init(); CORE_ASSERT(res == 0, "Failed to initialize audio"); - res = x11_init(window_title, window_size.width, window_size.height, icon_path); + res = x11_init(window_title, window_size.width, window_size.height, icon_path, window_non_resizable); CORE_ASSERT(res == 0, "Failed to initialize X11"); res = init_ui(font_path); CORE_ASSERT(res == 0, "Failed to initialize UI"); zephr_ctx->window.size = window_size; + zephr_ctx->window.non_resizable = window_non_resizable; zephr_ctx->projection = orthographic_projection_2d(0.f, window_size.width, window_size.height, 0.f); zephr_ctx->cursors[ZEPHR_CURSOR_ARROW] = XCreateFontCursor(x11_display, XC_left_ptr); @@ -835,13 +875,6 @@ Size zephr_get_window_size(void) { return zephr_ctx->window.size; } -// This MUST be called after calling zephr_init() -void zephr_make_window_non_resizable(void) { - CORE_DEBUG_ASSERT(zephr_ctx, "Zephr context not initialized"); - - x11_make_window_non_resizable(zephr_ctx->window.size.width, zephr_ctx->window.size.height); -} - void zephr_toggle_fullscreen(void) { x11_toggle_fullscreen(zephr_ctx->window.is_fullscreen); diff --git a/zephr.h b/zephr.h index 6bedc22..60d09ff 100644 --- a/zephr.h +++ b/zephr.h @@ -21,7 +21,9 @@ typedef struct ZephrWindow { Size size; + Size pre_fullscreen_size; bool is_fullscreen; + bool non_resizable; } ZephrWindow; typedef enum ZephrEventType { @@ -391,12 +393,11 @@ typedef struct ZephrContext { Matrix4x4 projection; } ZephrContext; -void zephr_init(const char* font_path, const char* icon_path, const char* window_title, Size window_size); +void zephr_init(const char* font_path, const char* icon_path, const char* window_title, Size window_size, bool window_non_resizable); void zephr_deinit(void); bool zephr_should_quit(void); void zephr_swap_buffers(void); Size zephr_get_window_size(void); -void zephr_make_window_non_resizable(void); void zephr_toggle_fullscreen(void); void zephr_quit(void); Color ColorRGBA(u8 r, u8 g, u8 b, u8 a);