Added empty event callback function. Implemented for Windows only at the moment

This commit is contained in:
REghZy 2023-12-08 22:09:59 +00:00
parent 46cebb5081
commit 57e00a0a3a
8 changed files with 131 additions and 5 deletions

3
.gitignore vendored
View File

@ -101,3 +101,6 @@ tests/triangle-vulkan
tests/window tests/window
tests/windows tests/windows
/.idea
/cmake-build-debug
/.gitignore

View File

@ -1974,6 +1974,23 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event);
*/ */
typedef void (* GLFWjoystickfun)(int jid, 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. /*! @brief Video mode type.
* *
* This describes a single video mode. * This describes a single video mode.
@ -5697,7 +5714,36 @@ GLFWAPI int glfwJoystickIsGamepad(int jid);
* *
* @ingroup input * @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. /*! @brief Adds the specified SDL_GameControllerDB gamepad mappings.
* *

View File

@ -1253,6 +1253,13 @@ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
return 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) GLFWAPI int glfwUpdateGamepadMappings(const char* string)
{ {
int jid; int jid;

View File

@ -866,6 +866,7 @@ struct _GLFWlibrary
struct { struct {
GLFWmonitorfun monitor; GLFWmonitorfun monitor;
GLFWjoystickfun joystick; GLFWjoystickfun joystick;
GLFWemptyeventfun emptyEvent;
} callbacks; } callbacks;
// These are defined in platform.h // These are defined in platform.h

View File

@ -363,6 +363,19 @@ static LRESULT CALLBACK helperWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LP
break; 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); return DefWindowProcW(hWnd, uMsg, wParam, lParam);
@ -405,6 +418,14 @@ static GLFWbool createHelperWindow(void)
return GLFW_FALSE; 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 // 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 // process passed along a STARTUPINFO, so clear that with a no-op call
ShowWindow(_glfw.win32.helperWindowHandle, SW_HIDE); ShowWindow(_glfw.win32.helperWindowHandle, SW_HIDE);

View File

@ -457,6 +457,7 @@ typedef struct _GLFWlibraryWin32
RAWINPUT* rawInput; RAWINPUT* rawInput;
int rawInputSize; int rawInputSize;
UINT mouseTrailSize; UINT mouseTrailSize;
UINT emptyEventMessage;
struct { struct {
HINSTANCE instance; HINSTANCE instance;

View File

@ -2128,7 +2128,7 @@ void _glfwWaitEventsTimeoutWin32(double timeout)
void _glfwPostEmptyEventWin32(void) 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) void _glfwGetCursorPosWin32(_GLFWwindow* window, double* xpos, double* ypos)

View File

@ -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 <Windows.h>
#endif // USE_WIN32_THREAD_EMPTY_EVENT_TEST
#define GLAD_GL_IMPLEMENTATION #define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h> #include <glad/gl.h>
#define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_NONE
@ -48,6 +58,35 @@
#include <stdlib.h> #include <stdlib.h>
#include <limits.h> #include <limits.h>
#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 main(int argc, char** argv)
{ {
int windowed_x, windowed_y, windowed_width, windowed_height; int windowed_x, windowed_y, windowed_width, windowed_height;
@ -66,12 +105,14 @@ int main(int argc, char** argv)
if (!glfwInit()) if (!glfwInit())
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
glfwSetEmptyEventCallback(onEmptyEventPosted);
glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE);
glfwWindowHint(GLFW_WIN32_KEYBOARD_MENU, GLFW_TRUE); glfwWindowHint(GLFW_WIN32_KEYBOARD_MENU, GLFW_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); 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) if (!window)
{ {
glfwTerminate(); glfwTerminate();
@ -121,7 +162,7 @@ int main(int argc, char** argv)
nk_glfw3_new_frame(); nk_glfw3_new_frame();
if (nk_begin(nk, "main", area, 0)) 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")) if (nk_button_label(nk, "Toggle Fullscreen"))
{ {
@ -156,10 +197,16 @@ int main(int argc, char** argv)
const double time = glfwGetTime() + 3.0; const double time = glfwGetTime() + 3.0;
while (glfwGetTime() < time) while (glfwGetTime() < time)
glfwWaitEventsTimeout(1.0); glfwWaitEventsTimeout(1.0);
glfwShowWindow(window); 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); nk_layout_row_dynamic(nk, 30, 1);
if (glfwGetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH)) if (glfwGetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH))