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. */
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

View file

@ -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);

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_init_shared(PGPL_WindowShared *window_shared);
void pgpl_window_internal_deinit_shared(PGPL_WindowShared *window_shared);
#endif

View file

@ -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);
}

View file

@ -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

View file

@ -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

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
* projection matrix to be easier to work with. */
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_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);