bug fixes + minimum and maximum window size options

This commit is contained in:
Patrick 2025-10-12 01:45:23 +02:00
commit 17d67cad27
8 changed files with 166 additions and 53 deletions

View file

@ -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. */ * the pgpl_window_start_render function for more information. */
void pgpl_window_finish_render(PGPL_Window *window, PGPL_Renderer *renderer); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -94,6 +94,16 @@ void pgpl_window_thread_reparent_to(PGPL_WindowThread *window_thread,
* pgpl_window_get_raw_window for more information. */ * pgpl_window_get_raw_window for more information. */
void *pgpl_window_thread_get_raw_window(PGPL_WindowThread *window_thread); 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. */ /* This function halts until the window_thread is destroyed. */
void pgpl_window_thread_await_destruction(PGPL_WindowThread *window_thread); void pgpl_window_thread_await_destruction(PGPL_WindowThread *window_thread);

View file

@ -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_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 #endif

View file

@ -20,7 +20,9 @@ typedef enum PGPL_WindowThreadAction {
PGPL_WINDOW_THREAD_SET_TRANSIENT, PGPL_WINDOW_THREAD_SET_TRANSIENT,
PGPL_WINDOW_THREAD_REPARENT, PGPL_WINDOW_THREAD_REPARENT,
PGPL_WINDOW_THREAD_GET_RAW_WINDOW, 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; } PGPL_WindowThreadAction;
typedef struct PGPL_WindowThreadCreateParameters { 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.type,
window_thread->data.event.data); window_thread->data.event.data);
break; 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); 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 /* This is a helper function that helps send a message inside the event loop and
* returns the result once done. */ * returns the result once done. */
static PGPL_WindowThreadActionData * static inline PGPL_WindowThreadActionData *
pgpl_window_thread_internal_send_message(PGPL_WindowThread *window_thread, pgpl_window_thread_internal_send_message(PGPL_WindowThread *window_thread,
PGPL_WindowThreadAction action, PGPL_WindowThreadAction action,
PGPL_WindowThreadActionData *data) { 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); 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) { void pgpl_window_thread_await_destruction(PGPL_WindowThread *window_thread) {
pgpl_thread_join(window_thread->thread); pgpl_thread_join(window_thread->thread);
} }

View file

@ -15,9 +15,17 @@ struct PGPL_Window {
PGPL_Vector *window_proc_event_data; PGPL_Vector *window_proc_event_data;
int32_t last_mouse_x, last_mouse_y; int32_t last_mouse_x, last_mouse_y;
int32_t scroll_accumulator; int32_t scroll_accumulator;
uint32_t min_width, min_height, max_width, max_height;
char title[128]; 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 /* 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 * things in here could be solved with more macros, to clean up and shorten the
* code a bit. * code a bit.
@ -25,7 +33,7 @@ struct PGPL_Window {
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) { LPARAM lParam) {
PGPL_Window *window = (PGPL_Window *)GetWindowLongPtr(hwnd, 0); PGPL_Window *window = (PGPL_Window *)GetWindowLongPtr(hwnd, 0);
PGPL_WindowEventType event_type; PGPL_WindowEventType event_type = PGPL_WINDOW_EVENT_NONE;
PGPL_WindowEventData event_data; PGPL_WindowEventData event_data;
if (window == NULL) { if (window == NULL) {
@ -145,14 +153,26 @@ static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
case WM_CLOSE: case WM_CLOSE:
event_type = PGPL_WINDOW_EVENT_CLOSE; event_type = PGPL_WINDOW_EVENT_CLOSE;
break; 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: default:
event_type = PGPL_WINDOW_EVENT_NONE;
pgpl_mutex_unlock(window->window_proc_vector_lock); pgpl_mutex_unlock(window->window_proc_vector_lock);
return DefWindowProc(hwnd, uMsg, wParam, lParam); return DefWindowProc(hwnd, uMsg, wParam, lParam);
} }
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_type, &event_type);
pgpl_vector_push_back(window->window_proc_event_data, &event_data); pgpl_vector_push_back(window->window_proc_event_data, &event_data);
}
pgpl_mutex_unlock(window->window_proc_vector_lock); pgpl_mutex_unlock(window->window_proc_vector_lock);
return 0; return 0;
} }
@ -190,10 +210,14 @@ PGPL_Window *pgpl_window_create(const char *title, uint32_t width,
0, 0,
0}; 0};
/* TODO: Move into shared function? */ pgpl_window_internal_init_shared(&window->shared);
window->shared.scale = 1.0;
window->shared.event_data = pgpl_vector_create(sizeof(PGPL_WindowEventData)); window->min_width = 0;
window->shared.event_types = pgpl_vector_create(sizeof(PGPL_WindowEventType)); 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 = window->window_proc_event_data =
pgpl_vector_create(sizeof(PGPL_WindowEventData)); 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) { void pgpl_window_destroy(PGPL_Window *window) {
/* TODO: Also turn this into a generic function perhaps. */ pgpl_window_internal_deinit_shared(&window->shared);
pgpl_vector_destroy(window->shared.event_data);
pgpl_vector_destroy(window->shared.event_types);
pgpl_mutex_destroy(window->window_proc_vector_lock); pgpl_mutex_destroy(window->window_proc_vector_lock);
wglMakeCurrent(NULL, NULL); wglMakeCurrent(NULL, NULL);
wglDeleteContext(window->wgl_context); wglDeleteContext(window->wgl_context);
@ -271,15 +293,20 @@ void pgpl_window_get_size(PGPL_Window *window, uint32_t *width,
uint32_t *height) { uint32_t *height) {
RECT rect; RECT rect;
GetClientRect(window->hwnd, &rect); GetClientRect(window->hwnd, &rect);
if (width != NULL) {
*width = rect.right - rect.left; *width = rect.right - rect.left;
}
if (height != NULL) {
*height = rect.bottom - rect.top; *height = rect.bottom - rect.top;
}
} }
void pgpl_window_set_size(PGPL_Window *window, uint32_t width, void pgpl_window_set_size(PGPL_Window *window, uint32_t width,
uint32_t height) { uint32_t height) {
int border_x = GetSystemMetrics(SM_CXSIZEFRAME) * 2; uint32_t border_x;
int border_y = uint32_t border_y;
GetSystemMetrics(SM_CYSIZEFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION);
pgpl_window_internal_get_borders(&border_x, &border_y);
SetWindowPos(window->hwnd, NULL, 0, 0, width + border_x, height + border_y, SetWindowPos(window->hwnd, NULL, 0, 0, width + border_x, height + border_y,
SWP_NOMOVE); 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) { void pgpl_window_get_position(PGPL_Window *window, int32_t *x, int32_t *y) {
WINDOWPLACEMENT placement; WINDOWPLACEMENT placement;
GetWindowPlacement(window->hwnd, &placement); GetWindowPlacement(window->hwnd, &placement);
if (x != NULL) {
*x = placement.ptMinPosition.x; *x = placement.ptMinPosition.x;
}
if (y != NULL) {
*y = placement.ptMinPosition.y; *y = placement.ptMinPosition.y;
}
} }
void pgpl_window_set_position(PGPL_Window *window, int32_t x, int32_t 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); 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 #endif

View file

@ -13,6 +13,7 @@ struct PGPL_Window {
GLXContext glx_context; GLXContext glx_context;
char *title; char *title;
bool reparented; bool reparented;
XSizeHints *size_hints;
}; };
/* Creates a basic X11 window with GLX. We select all the events that we handle /* 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; window->title = NULL;
/* TODO: Move into shared function? */ pgpl_window_internal_init_shared(&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));
window->reparented = 0; window->reparented = 0;
@ -66,15 +64,16 @@ PGPL_Window *pgpl_window_create(const char *title, uint32_t width,
XFlush(window->display); XFlush(window->display);
window->size_hints = XAllocSizeHints();
free(visual); free(visual);
return window; return window;
} }
void pgpl_window_destroy(PGPL_Window *window) { void pgpl_window_destroy(PGPL_Window *window) {
/* TODO: Also turn this into a generic function perhaps. */ pgpl_window_internal_deinit_shared(&window->shared);
pgpl_vector_destroy(window->shared.event_data); XFree(window->size_hints);
pgpl_vector_destroy(window->shared.event_types);
if (window->title != NULL) { if (window->title != NULL) {
XFree(window->title); XFree(window->title);
} }
@ -145,7 +144,7 @@ void pgpl_window_set_position(PGPL_Window *window, int32_t x, int32_t y) {
XFlush(window->display); 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; KeySym key_sym;
char buffer[32]; char buffer[32];
wchar_t *wbuffer; wchar_t *wbuffer;
@ -176,43 +175,33 @@ pgpl_window_internal_fetch_window_event(PGPL_Window *window,
XNextEvent(window->display, &event); XNextEvent(window->display, &event);
switch (event.type) { switch (event.type) {
case KeyPress: case KeyPress:
if (event_data != NULL) {
event_data->input_event.x = event.xkey.x; event_data->input_event.x = event.xkey.x;
event_data->input_event.y = event.xkey.y; event_data->input_event.y = event.xkey.y;
event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey); event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey);
}
return PGPL_WINDOW_EVENT_KEY_PRESS; return PGPL_WINDOW_EVENT_KEY_PRESS;
break; break;
case KeyRelease: case KeyRelease:
if (event_data != NULL) {
event_data->input_event.x = event.xkey.x; event_data->input_event.x = event.xkey.x;
event_data->input_event.y = event.xkey.y; event_data->input_event.y = event.xkey.y;
event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey); event_data->input_event.key = pgpl_window_internal_get_key(&event.xkey);
}
return PGPL_WINDOW_EVENT_KEY_RELEASE; return PGPL_WINDOW_EVENT_KEY_RELEASE;
break; break;
case ButtonPress: case ButtonPress:
if (event_data != NULL) {
event_data->input_event.x = event.xbutton.x; event_data->input_event.x = event.xbutton.x;
event_data->input_event.y = event.xbutton.y; event_data->input_event.y = event.xbutton.y;
event_data->input_event.key = event.xbutton.button; event_data->input_event.key = event.xbutton.button;
}
return PGPL_WINDOW_EVENT_MOUSE_PRESS; return PGPL_WINDOW_EVENT_MOUSE_PRESS;
break; break;
case ButtonRelease: case ButtonRelease:
if (event_data != NULL) {
event_data->input_event.x = event.xbutton.x; event_data->input_event.x = event.xbutton.x;
event_data->input_event.y = event.xbutton.y; event_data->input_event.y = event.xbutton.y;
event_data->input_event.key = event.xbutton.button; event_data->input_event.key = event.xbutton.button;
}
return PGPL_WINDOW_EVENT_MOUSE_RELEASE; return PGPL_WINDOW_EVENT_MOUSE_RELEASE;
break; break;
case MotionNotify: case MotionNotify:
if (event_data != NULL) {
event_data->input_event.x = event.xbutton.x; event_data->input_event.x = event.xbutton.x;
event_data->input_event.y = event.xbutton.y; event_data->input_event.y = event.xbutton.y;
event_data->input_event.key = 0; event_data->input_event.key = 0;
}
return PGPL_WINDOW_EVENT_MOUSE_MOVE; return PGPL_WINDOW_EVENT_MOUSE_MOVE;
break; break;
case ClientMessage: case ClientMessage:
@ -249,4 +238,20 @@ void pgpl_window_internal_finish_render(PGPL_Window *window) {
glXSwapBuffers(window->display, window->window); glXSwapBuffers(window->display, window->window);
pgpl_mutex_unlock(pgpl_internal_render_lock); 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 #endif

View file

@ -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 /* Enables important OpenGL settings, like transparency support, and sets up our
* projection matrix to be easier to work with. */ * projection matrix to be easier to work with. */
PGPL_Renderer *pgpl_window_start_render(PGPL_Window *window, PGPL_Color color) { PGPL_Renderer *pgpl_window_start_render(PGPL_Window *window, PGPL_Color color) {

View file

@ -64,6 +64,9 @@ int main(void) {
pgpl_gui_widget_add(gui, create_main_view(&app)); 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_gui_run_and_show(gui, true);
pgpl_font_destroy(NULL, font); pgpl_font_destroy(NULL, font);