From 57e00a0a3a3dc1bf91355355e17e52c7e40070c4 Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:09:59 +0000 Subject: [PATCH 1/9] Added empty event callback function. Implemented for Windows only at the moment --- .gitignore | 3 +++ include/GLFW/glfw3.h | 48 ++++++++++++++++++++++++++++++++++++++- src/input.c | 7 ++++++ src/internal.h | 1 + src/win32_init.c | 21 ++++++++++++++++++ src/win32_platform.h | 1 + src/win32_window.c | 2 +- tests/window.c | 53 +++++++++++++++++++++++++++++++++++++++++--- 8 files changed, 131 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 84636a0a..1cc9b59c 100644 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,6 @@ tests/triangle-vulkan tests/window tests/windows +/.idea +/cmake-build-debug +/.gitignore diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index eb98dafd..c3d01e09 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1974,6 +1974,23 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); */ typedef void (* GLFWjoystickfun)(int jid, int event); +/*! @brief The function pointer type for empty event callbacks. + * + * This is the function pointer type for empty event callbacks. + * An empty event callback function has the following signature: + * @code + * void function_name() + * @endcode + * + * @sa @ref empty_event + * @sa @ref glfwSetEmptyEventCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWemptyeventfun)(void); + /*! @brief Video mode type. * * This describes a single video mode. @@ -5697,7 +5714,36 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); * * @ingroup input */ -GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback);/*! @brief Sets the joystick configuration callback. + * + * + * This function sets the empty event callback, or removes the currently + * set callback. This is called on the GLFW thread at some point in the future + * when glfwPostEmptyEvent is called from any thread. + * + * @param[in] callback The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @callback_signature + * @code + * void function_name() + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWemptyeventfun). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread + * + * @sa @ref empty_event + * + * @since Added in version TODO + * + * @ingroup input + */ +GLFWAPI GLFWemptyeventfun glfwSetEmptyEventCallback(GLFWemptyeventfun callback); /*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. * diff --git a/src/input.c b/src/input.c index 8b7ef29c..2f5b3255 100644 --- a/src/input.c +++ b/src/input.c @@ -1253,6 +1253,13 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) return cbfun; } +GLFWAPI GLFWemptyeventfun glfwSetEmptyEventCallback(GLFWemptyeventfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP(GLFWemptyeventfun, _glfw.callbacks.emptyEvent, cbfun); + return cbfun; +} + GLFWAPI int glfwUpdateGamepadMappings(const char* string) { int jid; diff --git a/src/internal.h b/src/internal.h index fe0369aa..32192b6c 100644 --- a/src/internal.h +++ b/src/internal.h @@ -866,6 +866,7 @@ struct _GLFWlibrary struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; + GLFWemptyeventfun emptyEvent; } callbacks; // These are defined in platform.h diff --git a/src/win32_init.c b/src/win32_init.c index ef2615f1..5f7c3bdf 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -363,6 +363,19 @@ static LRESULT CALLBACK helperWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LP break; } + + default: + { + // this check may not be necessary, but it checks just in case the message + // has not been registered. this skips having to check uMsg != WM_NULL + UINT emptyEventMsg = _glfw.win32.emptyEventMessage; + if (emptyEventMsg != 0 && uMsg == emptyEventMsg) + { + if (_glfw.callbacks.emptyEvent) + _glfw.callbacks.emptyEvent(); + } + break; + } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); @@ -405,6 +418,14 @@ static GLFWbool createHelperWindow(void) return GLFW_FALSE; } + _glfw.win32.emptyEventMessage = RegisterWindowMessageW(L"GLFW.EmptyEventMessage"); + if (!_glfw.win32.emptyEventMessage) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to register empty event message"); + return GLFW_FALSE; + } + // HACK: The command to the first ShowWindow call is ignored if the parent // process passed along a STARTUPINFO, so clear that with a no-op call ShowWindow(_glfw.win32.helperWindowHandle, SW_HIDE); diff --git a/src/win32_platform.h b/src/win32_platform.h index 82b34bb9..32fff7bf 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -457,6 +457,7 @@ typedef struct _GLFWlibraryWin32 RAWINPUT* rawInput; int rawInputSize; UINT mouseTrailSize; + UINT emptyEventMessage; struct { HINSTANCE instance; diff --git a/src/win32_window.c b/src/win32_window.c index a4a18171..84753ec7 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -2128,7 +2128,7 @@ void _glfwWaitEventsTimeoutWin32(double timeout) void _glfwPostEmptyEventWin32(void) { - PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); + PostMessageW(_glfw.win32.helperWindowHandle, _glfw.win32.emptyEventMessage, 0, 0); } void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos) diff --git a/tests/window.c b/tests/window.c index 83baff46..7525b55f 100644 --- a/tests/window.c +++ b/tests/window.c @@ -23,6 +23,16 @@ // //======================================================================== +// TODO: Implement the postEmptyEvent test on multiple platforms. Works for Windows so far +#ifdef WIN32 +#define USE_WIN32_THREAD_EMPTY_EVENT_TEST +#endif // WIN32 + +#ifdef USE_WIN32_THREAD_EMPTY_EVENT_TEST +#include +#endif // USE_WIN32_THREAD_EMPTY_EVENT_TEST + + #define GLAD_GL_IMPLEMENTATION #include #define GLFW_INCLUDE_NONE @@ -48,6 +58,35 @@ #include #include +#ifdef USE_WIN32_THREAD_EMPTY_EVENT_TEST + +DWORD WINAPI win32ThreadEmptyEventTest(void) +{ + // Sleep for 3 seconds, and then post an empty event. The onEmptyEventPosted + // method should then be fired shortly after, on the GLFW main thread. + + Sleep(3000); + glfwPostEmptyEvent(); + return 0; +} + +void onEmptyEventPosted(void) +{ + // If you hold your LMB down on the window border (aka initialise the resize + // phase), you will see a big difference in stack trace here compared to + // when not resizing. This is because DefWindowProc seems to use its own + // message loop temporarily, and passes any unhandled messages back to the + // dummy window's wndproc, which detects the empty event message, firing the + // event callback. + // This is the only effective way to run code on the main thread during the + // window resize phase while the user isn't moving their mouse, apart from the + // actual resize events obviously + MessageBoxExW(NULL, L"Callback event received!", L"Empty Event Callback", NULL, NULL); + // printf("Empty event received! Put a break point here, and this will be fired on the main thread"); +} + +#endif // USE_WIN32_THREAD_EMPTY_EVENT_TEST + int main(int argc, char** argv) { int windowed_x, windowed_y, windowed_width, windowed_height; @@ -66,12 +105,14 @@ int main(int argc, char** argv) if (!glfwInit()) exit(EXIT_FAILURE); + glfwSetEmptyEventCallback(onEmptyEventPosted); + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); glfwWindowHint(GLFW_WIN32_KEYBOARD_MENU, GLFW_TRUE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); - GLFWwindow* window = glfwCreateWindow(600, 600, "Window Features", NULL, NULL); + GLFWwindow* window = glfwCreateWindow(750, 600, "Window Features", NULL, NULL); if (!window) { glfwTerminate(); @@ -121,7 +162,7 @@ int main(int argc, char** argv) nk_glfw3_new_frame(); if (nk_begin(nk, "main", area, 0)) { - nk_layout_row_dynamic(nk, 30, 5); + nk_layout_row_dynamic(nk, 30, 6); if (nk_button_label(nk, "Toggle Fullscreen")) { @@ -156,10 +197,16 @@ int main(int argc, char** argv) const double time = glfwGetTime() + 3.0; while (glfwGetTime() < time) glfwWaitEventsTimeout(1.0); - glfwShowWindow(window); } +#ifdef USE_WIN32_THREAD_EMPTY_EVENT_TEST + if (nk_button_label(nk, "Empty Event(3s)")) + { + CreateThread(NULL, 0, win32ThreadEmptyEventTest, NULL, 0, NULL); + } +#endif // USE_WIN32_THREAD_EMPTY_EVENT_TEST + nk_layout_row_dynamic(nk, 30, 1); if (glfwGetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH)) From dc2527888a0cc43d4644730074f7f08c91f86db6 Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:12:28 +0000 Subject: [PATCH 2/9] Update .gitignore --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1cc9b59c..44666a60 100644 --- a/.gitignore +++ b/.gitignore @@ -100,7 +100,3 @@ tests/title tests/triangle-vulkan tests/window tests/windows - -/.idea -/cmake-build-debug -/.gitignore From 0c29aa5cfd74b7cf5d211e9fe5e4796d591d867c Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:12:59 +0000 Subject: [PATCH 3/9] Update .gitignore From 3dc796da34857a83a4aaa985a67edf26c1e49c59 Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:23:34 +0000 Subject: [PATCH 4/9] Fixed test not compiling when USE_WIN32_THREAD_EMPTY_EVENT_TEST is not defined --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 44666a60..454a9d47 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,6 @@ tests/title tests/triangle-vulkan tests/window tests/windows +/.idea +/cmake-build-debug +/.gitignore From 1cbfe6be703916c362fa3b4b9783d2ef0ac6f3db Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:24:04 +0000 Subject: [PATCH 5/9] Fixed test not compiling when USE_WIN32_THREAD_EMPTY_EVENT_TEST is not defined --- tests/window.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/window.c b/tests/window.c index 7525b55f..378e3506 100644 --- a/tests/window.c +++ b/tests/window.c @@ -105,7 +105,9 @@ int main(int argc, char** argv) if (!glfwInit()) exit(EXIT_FAILURE); +#ifdef USE_WIN32_THREAD_EMPTY_EVENT_TEST glfwSetEmptyEventCallback(onEmptyEventPosted); +#endif // USE_WIN32_THREAD_EMPTY_EVENT_TEST glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); glfwWindowHint(GLFW_WIN32_KEYBOARD_MENU, GLFW_TRUE); From bcfdea54584173253dee4d7cec0c9006128a7e0e Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:24:42 +0000 Subject: [PATCH 6/9] Update .gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 454a9d47..44666a60 100644 --- a/.gitignore +++ b/.gitignore @@ -100,6 +100,3 @@ tests/title tests/triangle-vulkan tests/window tests/windows -/.idea -/cmake-build-debug -/.gitignore From bebe6eb12ac309dd924b69995103ca9d83faad3a Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:39:17 +0000 Subject: [PATCH 7/9] Fixed Win32 test: invalid function for CreateThread parameter --- .gitignore | 5 +--- tests/window.c | 67 +++++++++++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 454a9d47..b534b9cc 100644 --- a/.gitignore +++ b/.gitignore @@ -99,7 +99,4 @@ tests/timeout tests/title tests/triangle-vulkan tests/window -tests/windows -/.idea -/cmake-build-debug -/.gitignore +tests/windows \ No newline at end of file diff --git a/tests/window.c b/tests/window.c index 378e3506..3b191e91 100644 --- a/tests/window.c +++ b/tests/window.c @@ -32,7 +32,6 @@ #include #endif // USE_WIN32_THREAD_EMPTY_EVENT_TEST - #define GLAD_GL_IMPLEMENTATION #include #define GLFW_INCLUDE_NONE @@ -60,7 +59,7 @@ #ifdef USE_WIN32_THREAD_EMPTY_EVENT_TEST -DWORD WINAPI win32ThreadEmptyEventTest(void) +DWORD WINAPI win32ThreadEmptyEventTest(LPVOID lpThreadParameter) { // Sleep for 3 seconds, and then post an empty event. The onEmptyEventPosted // method should then be fired shortly after, on the GLFW main thread. @@ -81,7 +80,7 @@ void onEmptyEventPosted(void) // This is the only effective way to run code on the main thread during the // window resize phase while the user isn't moving their mouse, apart from the // actual resize events obviously - MessageBoxExW(NULL, L"Callback event received!", L"Empty Event Callback", NULL, NULL); + MessageBoxW((HWND)NULL, L"Callback event received!", L"Empty Event Callback", (UINT)0); // printf("Empty event received! Put a break point here, and this will be fired on the main thread"); } @@ -158,7 +157,7 @@ int main(int argc, char** argv) glfwGetWindowSize(window, &width, &height); - struct nk_rect area = nk_rect(0.f, 0.f, (float) width, (float) height); + struct nk_rect area = nk_rect(0.f, 0.f, (float)width, (float)height); nk_window_set_bounds(nk, "main", area); nk_glfw3_new_frame(); @@ -171,8 +170,8 @@ int main(int argc, char** argv) if (glfwGetWindowMonitor(window)) { glfwSetWindowMonitor(window, NULL, - windowed_x, windowed_y, - windowed_width, windowed_height, 0); + windowed_x, windowed_y, + windowed_width, windowed_height, 0); } else { @@ -181,8 +180,8 @@ int main(int argc, char** argv) glfwGetWindowPos(window, &windowed_x, &windowed_y); glfwGetWindowSize(window, &windowed_width, &windowed_height); glfwSetWindowMonitor(window, monitor, - 0, 0, mode->width, mode->height, - mode->refreshRate); + 0, 0, mode->width, mode->height, + mode->refreshRate); } } @@ -223,8 +222,8 @@ int main(int argc, char** argv) nk_flags events; const nk_flags flags = NK_EDIT_FIELD | - NK_EDIT_SIG_ENTER | - NK_EDIT_GOTO_END_ON_ACTIVATE; + NK_EDIT_SIG_ENTER | + NK_EDIT_GOTO_END_ON_ACTIVATE; if (position_supported) { @@ -235,8 +234,8 @@ int main(int argc, char** argv) nk_label(nk, "Position", NK_TEXT_LEFT); events = nk_edit_string_zero_terminated(nk, flags, xpos_buffer, - sizeof(xpos_buffer), - nk_filter_decimal); + sizeof(xpos_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { xpos = atoi(xpos_buffer); @@ -246,8 +245,8 @@ int main(int argc, char** argv) sprintf(xpos_buffer, "%i", xpos); events = nk_edit_string_zero_terminated(nk, flags, ypos_buffer, - sizeof(ypos_buffer), - nk_filter_decimal); + sizeof(ypos_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { ypos = atoi(ypos_buffer); @@ -266,8 +265,8 @@ int main(int argc, char** argv) nk_label(nk, "Size", NK_TEXT_LEFT); events = nk_edit_string_zero_terminated(nk, flags, width_buffer, - sizeof(width_buffer), - nk_filter_decimal); + sizeof(width_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { width = atoi(width_buffer); @@ -277,8 +276,8 @@ int main(int argc, char** argv) sprintf(width_buffer, "%i", width); events = nk_edit_string_zero_terminated(nk, flags, height_buffer, - sizeof(height_buffer), - nk_filter_decimal); + sizeof(height_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { height = atoi(height_buffer); @@ -295,8 +294,8 @@ int main(int argc, char** argv) update_ratio_limit = true; events = nk_edit_string_zero_terminated(nk, flags, numer_buffer, - sizeof(numer_buffer), - nk_filter_decimal); + sizeof(numer_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { aspect_numer = abs(atoi(numer_buffer)); @@ -306,8 +305,8 @@ int main(int argc, char** argv) sprintf(numer_buffer, "%i", aspect_numer); events = nk_edit_string_zero_terminated(nk, flags, denom_buffer, - sizeof(denom_buffer), - nk_filter_decimal); + sizeof(denom_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { aspect_denom = abs(atoi(denom_buffer)); @@ -330,8 +329,8 @@ int main(int argc, char** argv) update_size_limit = true; events = nk_edit_string_zero_terminated(nk, flags, min_width_buffer, - sizeof(min_width_buffer), - nk_filter_decimal); + sizeof(min_width_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { min_width = abs(atoi(min_width_buffer)); @@ -341,8 +340,8 @@ int main(int argc, char** argv) sprintf(min_width_buffer, "%i", min_width); events = nk_edit_string_zero_terminated(nk, flags, min_height_buffer, - sizeof(min_height_buffer), - nk_filter_decimal); + sizeof(min_height_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { min_height = abs(atoi(min_height_buffer)); @@ -355,8 +354,8 @@ int main(int argc, char** argv) update_size_limit = true; events = nk_edit_string_zero_terminated(nk, flags, max_width_buffer, - sizeof(max_width_buffer), - nk_filter_decimal); + sizeof(max_width_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { max_width = abs(atoi(max_width_buffer)); @@ -366,8 +365,8 @@ int main(int argc, char** argv) sprintf(max_width_buffer, "%i", max_width); events = nk_edit_string_zero_terminated(nk, flags, max_height_buffer, - sizeof(max_height_buffer), - nk_filter_decimal); + sizeof(max_height_buffer), + nk_filter_decimal); if (events & NK_EDIT_COMMITED) { max_height = abs(atoi(max_height_buffer)); @@ -379,10 +378,10 @@ int main(int argc, char** argv) if (update_size_limit) { glfwSetWindowSizeLimits(window, - limit_min_size ? min_width : GLFW_DONT_CARE, - limit_min_size ? min_height : GLFW_DONT_CARE, - limit_max_size ? max_width : GLFW_DONT_CARE, - limit_max_size ? max_height : GLFW_DONT_CARE); + limit_min_size ? min_width : GLFW_DONT_CARE, + limit_min_size ? min_height : GLFW_DONT_CARE, + limit_max_size ? max_width : GLFW_DONT_CARE, + limit_max_size ? max_height : GLFW_DONT_CARE); } int fb_width, fb_height; From 552c40d17507b875cdb19212ba98e39ea630d699 Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:42:08 +0000 Subject: [PATCH 8/9] Fixed Win32 test: invalid function for CreateThread parameter --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index b534b9cc..2a52f807 100644 --- a/.gitignore +++ b/.gitignore @@ -99,4 +99,3 @@ tests/timeout tests/title tests/triangle-vulkan tests/window -tests/windows \ No newline at end of file From f9c58d3df8d3fe769d9b434195bab5ede3ba7055 Mon Sep 17 00:00:00 2001 From: REghZy <32274404+AngryCarrot789@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:45:18 +0000 Subject: [PATCH 9/9] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2a52f807..44666a60 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,4 @@ tests/timeout tests/title tests/triangle-vulkan tests/window +tests/windows