From 17d67cad27a5e8e5044f876522406f2f192eb7b9 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 12 Oct 2025 01:45:23 +0200 Subject: [PATCH] bug fixes + minimum and maximum window size options --- include/pgpl/window.h | 8 ++++ include/pgpl/window/thread.h | 10 +++++ src/window/internal.h | 4 ++ src/window/thread.c | 34 +++++++++++++++- src/window/window-win32.c | 78 +++++++++++++++++++++++++++--------- src/window/window-x11.c | 71 +++++++++++++++++--------------- src/window/window.c | 11 +++++ tests/program.c | 3 ++ 8 files changed, 166 insertions(+), 53 deletions(-) diff --git a/include/pgpl/window.h b/include/pgpl/window.h index b2a1af2..496f350 100644 --- a/include/pgpl/window.h +++ b/include/pgpl/window.h @@ -107,6 +107,14 @@ PGPL_Renderer *pgpl_window_start_render(PGPL_Window *window, PGPL_Color color); * the pgpl_window_start_render function for more information. */ void pgpl_window_finish_render(PGPL_Window *window, PGPL_Renderer *renderer); +/* This sets the minimum size that the given window can have. */ +void pgpl_window_set_minimum_size(PGPL_Window *window, uint32_t width, + uint32_t height); + +/* This sets the maximum size that the given window can have. */ +void pgpl_window_set_maximum_size(PGPL_Window *window, uint32_t width, + uint32_t height); + #ifdef __cplusplus } #endif diff --git a/include/pgpl/window/thread.h b/include/pgpl/window/thread.h index abcf21a..4cf2283 100644 --- a/include/pgpl/window/thread.h +++ b/include/pgpl/window/thread.h @@ -94,6 +94,16 @@ void pgpl_window_thread_reparent_to(PGPL_WindowThread *window_thread, * pgpl_window_get_raw_window for more information. */ void *pgpl_window_thread_get_raw_window(PGPL_WindowThread *window_thread); +/* Threaded version of pgpl_window_set_minimum_size, see + * pgpl_window_set_minimum_size for more information. */ +void pgpl_window_thread_set_minimum_size(PGPL_WindowThread *window_thread, + uint32_t width, uint32_t height); + +/* Threaded version of pgpl_window_set_maximum_size, see + * pgpl_window_set_maximum_size for more information. */ +void pgpl_window_thread_set_maximum_size(PGPL_WindowThread *window_thread, + uint32_t width, uint32_t height); + /* This function halts until the window_thread is destroyed. */ void pgpl_window_thread_await_destruction(PGPL_WindowThread *window_thread); diff --git a/src/window/internal.h b/src/window/internal.h index 426b078..a5c2e0d 100644 --- a/src/window/internal.h +++ b/src/window/internal.h @@ -21,4 +21,8 @@ void pgpl_window_internal_start_render(PGPL_Window *window); void pgpl_window_internal_finish_render(PGPL_Window *window); +void pgpl_window_internal_init_shared(PGPL_WindowShared *window_shared); + +void pgpl_window_internal_deinit_shared(PGPL_WindowShared *window_shared); + #endif diff --git a/src/window/thread.c b/src/window/thread.c index ace512b..751d868 100644 --- a/src/window/thread.c +++ b/src/window/thread.c @@ -20,7 +20,9 @@ typedef enum PGPL_WindowThreadAction { PGPL_WINDOW_THREAD_SET_TRANSIENT, PGPL_WINDOW_THREAD_REPARENT, PGPL_WINDOW_THREAD_GET_RAW_WINDOW, - PGPL_WINDOW_THREAD_CREATE_EVENT + PGPL_WINDOW_THREAD_CREATE_EVENT, + PGPL_WINDOW_THREAD_SET_MINIMUM_SIZE, + PGPL_WINDOW_THREAD_SET_MAXIMUM_SIZE, } PGPL_WindowThreadAction; typedef struct PGPL_WindowThreadCreateParameters { @@ -178,6 +180,16 @@ static void *pgpl_window_event_loop(void *arg) { window_thread->data.event.type, window_thread->data.event.data); break; + case PGPL_WINDOW_THREAD_SET_MINIMUM_SIZE: + pgpl_window_set_minimum_size(window_thread->window, + window_thread->data.size_data.width, + window_thread->data.size_data.height); + break; + case PGPL_WINDOW_THREAD_SET_MAXIMUM_SIZE: + pgpl_window_set_maximum_size(window_thread->window, + window_thread->data.size_data.width, + window_thread->data.size_data.height); + break; } pgpl_mutex_unlock(window_thread->result_ready_lock); } @@ -234,7 +246,7 @@ PGPL_WindowThread *pgpl_window_thread_create( /* This is a helper function that helps send a message inside the event loop and * returns the result once done. */ -static PGPL_WindowThreadActionData * +static inline PGPL_WindowThreadActionData * pgpl_window_thread_internal_send_message(PGPL_WindowThread *window_thread, PGPL_WindowThreadAction action, PGPL_WindowThreadActionData *data) { @@ -366,6 +378,24 @@ void pgpl_window_thread_create_event(PGPL_WindowThread *window_thread, window_thread, PGPL_WINDOW_THREAD_CREATE_EVENT, &data); } +void pgpl_window_thread_set_minimum_size(PGPL_WindowThread *window_thread, + uint32_t width, uint32_t height) { + PGPL_WindowThreadActionData data; + data.size_data.width = width; + data.size_data.height = height; + pgpl_window_thread_internal_send_message( + window_thread, PGPL_WINDOW_THREAD_SET_MINIMUM_SIZE, &data); +} + +void pgpl_window_thread_set_maximum_size(PGPL_WindowThread *window_thread, + uint32_t width, uint32_t height) { + PGPL_WindowThreadActionData data; + data.size_data.width = width; + data.size_data.height = height; + pgpl_window_thread_internal_send_message( + window_thread, PGPL_WINDOW_THREAD_SET_MAXIMUM_SIZE, &data); +} + void pgpl_window_thread_await_destruction(PGPL_WindowThread *window_thread) { pgpl_thread_join(window_thread->thread); } diff --git a/src/window/window-win32.c b/src/window/window-win32.c index e246be1..86b31e3 100644 --- a/src/window/window-win32.c +++ b/src/window/window-win32.c @@ -15,9 +15,17 @@ struct PGPL_Window { PGPL_Vector *window_proc_event_data; int32_t last_mouse_x, last_mouse_y; int32_t scroll_accumulator; + uint32_t min_width, min_height, max_width, max_height; char title[128]; }; +static inline void pgpl_window_internal_get_borders(uint32_t *width, + uint32_t *height) { + *width = GetSystemMetrics(SM_CXSIZEFRAME) * 2; + *height = + GetSystemMetrics(SM_CYSIZEFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION); +} + /* Our WindowProc function, this needs to be overhauled a bit, and certain * things in here could be solved with more macros, to clean up and shorten the * code a bit. @@ -25,7 +33,7 @@ struct PGPL_Window { static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PGPL_Window *window = (PGPL_Window *)GetWindowLongPtr(hwnd, 0); - PGPL_WindowEventType event_type; + PGPL_WindowEventType event_type = PGPL_WINDOW_EVENT_NONE; PGPL_WindowEventData event_data; if (window == NULL) { @@ -145,14 +153,26 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, case WM_CLOSE: event_type = PGPL_WINDOW_EVENT_CLOSE; break; + case WM_GETMINMAXINFO: { + LPMINMAXINFO min_max_info = (LPMINMAXINFO)lParam; + uint32_t border_x; + uint32_t border_y; + pgpl_window_internal_get_borders(&border_x, &border_y); + min_max_info->ptMinTrackSize.x = window->min_width + border_x; + min_max_info->ptMinTrackSize.y = window->min_height + border_y; + min_max_info->ptMaxTrackSize.x = window->max_width + border_x; + min_max_info->ptMaxTrackSize.y = window->max_height + border_y; + break; + } default: - event_type = PGPL_WINDOW_EVENT_NONE; pgpl_mutex_unlock(window->window_proc_vector_lock); return DefWindowProc(hwnd, uMsg, wParam, lParam); } - pgpl_vector_push_back(window->window_proc_event_type, &event_type); - pgpl_vector_push_back(window->window_proc_event_data, &event_data); + if (event_type != PGPL_WINDOW_EVENT_NONE) { + pgpl_vector_push_back(window->window_proc_event_type, &event_type); + pgpl_vector_push_back(window->window_proc_event_data, &event_data); + } pgpl_mutex_unlock(window->window_proc_vector_lock); return 0; } @@ -190,10 +210,14 @@ PGPL_Window *pgpl_window_create(const char *title, uint32_t width, 0, 0}; - /* TODO: Move into shared function? */ - window->shared.scale = 1.0; - window->shared.event_data = pgpl_vector_create(sizeof(PGPL_WindowEventData)); - window->shared.event_types = pgpl_vector_create(sizeof(PGPL_WindowEventType)); + pgpl_window_internal_init_shared(&window->shared); + + window->min_width = 0; + window->min_height = 0; + /* For some reason, UINT32_MAX might be interpreted as a negative number in + * wine. TODO: Investigate */ + window->max_width = INT16_MAX; + window->max_height = INT16_MAX; window->window_proc_event_data = pgpl_vector_create(sizeof(PGPL_WindowEventData)); @@ -238,9 +262,7 @@ PGPL_Window *pgpl_window_create(const char *title, uint32_t width, } void pgpl_window_destroy(PGPL_Window *window) { - /* TODO: Also turn this into a generic function perhaps. */ - pgpl_vector_destroy(window->shared.event_data); - pgpl_vector_destroy(window->shared.event_types); + pgpl_window_internal_deinit_shared(&window->shared); pgpl_mutex_destroy(window->window_proc_vector_lock); wglMakeCurrent(NULL, NULL); wglDeleteContext(window->wgl_context); @@ -271,15 +293,20 @@ void pgpl_window_get_size(PGPL_Window *window, uint32_t *width, uint32_t *height) { RECT rect; GetClientRect(window->hwnd, &rect); - *width = rect.right - rect.left; - *height = rect.bottom - rect.top; + if (width != NULL) { + *width = rect.right - rect.left; + } + if (height != NULL) { + *height = rect.bottom - rect.top; + } } void pgpl_window_set_size(PGPL_Window *window, uint32_t width, uint32_t height) { - int border_x = GetSystemMetrics(SM_CXSIZEFRAME) * 2; - int border_y = - GetSystemMetrics(SM_CYSIZEFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION); + uint32_t border_x; + uint32_t border_y; + + pgpl_window_internal_get_borders(&border_x, &border_y); SetWindowPos(window->hwnd, NULL, 0, 0, width + border_x, height + border_y, SWP_NOMOVE); @@ -288,8 +315,12 @@ void pgpl_window_set_size(PGPL_Window *window, uint32_t width, void pgpl_window_get_position(PGPL_Window *window, int32_t *x, int32_t *y) { WINDOWPLACEMENT placement; GetWindowPlacement(window->hwnd, &placement); - *x = placement.ptMinPosition.x; - *y = placement.ptMinPosition.y; + if (x != NULL) { + *x = placement.ptMinPosition.x; + } + if (y != NULL) { + *y = placement.ptMinPosition.y; + } } void pgpl_window_set_position(PGPL_Window *window, int32_t x, int32_t y) { @@ -355,4 +386,15 @@ void pgpl_window_internal_finish_render(PGPL_Window *window) { pgpl_mutex_unlock(pgpl_internal_render_lock); } +void pgpl_window_set_minimum_size(PGPL_Window *window, uint32_t width, + uint32_t height) { + window->min_width = width; + window->min_height = height; +} + +void pgpl_window_set_maximum_size(PGPL_Window *window, uint32_t width, + uint32_t height) { + window->max_width = width; + window->max_height = height; +} #endif diff --git a/src/window/window-x11.c b/src/window/window-x11.c index 1151e85..f2b5ef3 100644 --- a/src/window/window-x11.c +++ b/src/window/window-x11.c @@ -13,6 +13,7 @@ struct PGPL_Window { GLXContext glx_context; char *title; bool reparented; + XSizeHints *size_hints; }; /* Creates a basic X11 window with GLX. We select all the events that we handle @@ -30,10 +31,7 @@ PGPL_Window *pgpl_window_create(const char *title, uint32_t width, window->title = NULL; - /* TODO: Move into shared function? */ - window->shared.scale = 1.0; - window->shared.event_data = pgpl_vector_create(sizeof(PGPL_WindowEventData)); - window->shared.event_types = pgpl_vector_create(sizeof(PGPL_WindowEventType)); + pgpl_window_internal_init_shared(&window->shared); window->reparented = 0; @@ -66,15 +64,16 @@ PGPL_Window *pgpl_window_create(const char *title, uint32_t width, XFlush(window->display); + window->size_hints = XAllocSizeHints(); + free(visual); return window; } void pgpl_window_destroy(PGPL_Window *window) { - /* TODO: Also turn this into a generic function perhaps. */ - pgpl_vector_destroy(window->shared.event_data); - pgpl_vector_destroy(window->shared.event_types); + pgpl_window_internal_deinit_shared(&window->shared); + XFree(window->size_hints); if (window->title != NULL) { XFree(window->title); } @@ -145,7 +144,7 @@ void pgpl_window_set_position(PGPL_Window *window, int32_t x, int32_t y) { XFlush(window->display); } -static int32_t pgpl_window_internal_get_key(XKeyEvent *key_event) { +static inline int32_t pgpl_window_internal_get_key(XKeyEvent *key_event) { KeySym key_sym; char buffer[32]; wchar_t *wbuffer; @@ -176,43 +175,33 @@ pgpl_window_internal_fetch_window_event(PGPL_Window *window, XNextEvent(window->display, &event); switch (event.type) { case KeyPress: - if (event_data != NULL) { - event_data->input_event.x = event.xkey.x; - event_data->input_event.y = event.xkey.y; - event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey); - } + event_data->input_event.x = event.xkey.x; + event_data->input_event.y = event.xkey.y; + event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey); return PGPL_WINDOW_EVENT_KEY_PRESS; break; case KeyRelease: - if (event_data != NULL) { - event_data->input_event.x = event.xkey.x; - event_data->input_event.y = event.xkey.y; - event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey); - } + event_data->input_event.x = event.xkey.x; + event_data->input_event.y = event.xkey.y; + event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey); return PGPL_WINDOW_EVENT_KEY_RELEASE; break; case ButtonPress: - if (event_data != NULL) { - event_data->input_event.x = event.xbutton.x; - event_data->input_event.y = event.xbutton.y; - event_data->input_event.key = event.xbutton.button; - } + event_data->input_event.x = event.xbutton.x; + event_data->input_event.y = event.xbutton.y; + event_data->input_event.key = event.xbutton.button; return PGPL_WINDOW_EVENT_MOUSE_PRESS; break; case ButtonRelease: - if (event_data != NULL) { - event_data->input_event.x = event.xbutton.x; - event_data->input_event.y = event.xbutton.y; - event_data->input_event.key = event.xbutton.button; - } + event_data->input_event.x = event.xbutton.x; + event_data->input_event.y = event.xbutton.y; + event_data->input_event.key = event.xbutton.button; return PGPL_WINDOW_EVENT_MOUSE_RELEASE; break; case MotionNotify: - if (event_data != NULL) { - event_data->input_event.x = event.xbutton.x; - event_data->input_event.y = event.xbutton.y; - event_data->input_event.key = 0; - } + event_data->input_event.x = event.xbutton.x; + event_data->input_event.y = event.xbutton.y; + event_data->input_event.key = 0; return PGPL_WINDOW_EVENT_MOUSE_MOVE; break; case ClientMessage: @@ -249,4 +238,20 @@ void pgpl_window_internal_finish_render(PGPL_Window *window) { glXSwapBuffers(window->display, window->window); pgpl_mutex_unlock(pgpl_internal_render_lock); } + +void pgpl_window_set_minimum_size(PGPL_Window *window, uint32_t width, + uint32_t height) { + window->size_hints->flags |= PMinSize; + window->size_hints->min_width = width; + window->size_hints->min_height = height; + XSetWMNormalHints(window->display, window->window, window->size_hints); +} + +void pgpl_window_set_maximum_size(PGPL_Window *window, uint32_t width, + uint32_t height) { + window->size_hints->flags |= PMaxSize; + window->size_hints->max_width = width; + window->size_hints->max_height = height; + XSetWMNormalHints(window->display, window->window, window->size_hints); +} #endif diff --git a/src/window/window.c b/src/window/window.c index 3d8e3bd..b3e0601 100644 --- a/src/window/window.c +++ b/src/window/window.c @@ -57,6 +57,17 @@ void pgpl_window_create_event(PGPL_Window *window, } } +void pgpl_window_internal_init_shared(PGPL_WindowShared *window_shared) { + window_shared->scale = 1.0; + window_shared->event_data = pgpl_vector_create(sizeof(PGPL_WindowEventData)); + window_shared->event_types = pgpl_vector_create(sizeof(PGPL_WindowEventType)); +} + +void pgpl_window_internal_deinit_shared(PGPL_WindowShared *window_shared) { + pgpl_vector_destroy(window_shared->event_data); + pgpl_vector_destroy(window_shared->event_types); +} + /* Enables important OpenGL settings, like transparency support, and sets up our * projection matrix to be easier to work with. */ PGPL_Renderer *pgpl_window_start_render(PGPL_Window *window, PGPL_Color color) { diff --git a/tests/program.c b/tests/program.c index 7f18747..2f8dbe9 100644 --- a/tests/program.c +++ b/tests/program.c @@ -64,6 +64,9 @@ int main(void) { pgpl_gui_widget_add(gui, create_main_view(&app)); + pgpl_window_thread_set_minimum_size(gui->window_thread, 320, 240); + pgpl_window_thread_set_maximum_size(gui->window_thread, 640, 480); + pgpl_gui_run_and_show(gui, true); pgpl_font_destroy(NULL, font);