Compare commits

..

9 Commits

Author SHA1 Message Date
Matthew Leibowitz
7349aae803
Merge 6b5ea498f6 into 232164f62b 2026-02-05 12:46:39 -08:00
Camilla Löwy
232164f62b Wayland: Remove duplicate cursor state
The shared window struct already tracks the current cursor.
2026-02-05 17:28:32 +01:00
Camilla Löwy
abb9db0d75 Wayland: Fix fallback decoration button input
Fallback decoration mouse button actions were performed for both press
and release events.  The requests made using a mouse button release
serials were presumably (and correctly) discarded by the compositor.

This commit removes the generation of these nuisance requests.
2026-02-05 17:25:11 +01:00
Camilla Löwy
0aa77a9a3a Wayland: Release input devices where possible
On systems with wl_seat verison 3 or later, use release instead of
destroy on input devices when they are disconnected.
2026-02-04 18:08:37 +01:00
Camilla Löwy
7b370d8df0 Wayland: Simplify pointer surface state
Track which surface contains the pointer with one piece of state instead
of three partially overlapping ones.
2026-02-04 18:08:37 +01:00
Camilla Löwy
f871f14d5e Add update flag to timeout test
Redrawing the window contents for every possible event is not
a reasonable thing for an application to do.  The test should be whether
redrawing on demand plus periodic updates works with near-idle CPU use
and no extra input latency.

This change incidentally makes the test well-behaved on Wayland, where
it was previously broken due to every EGL buffer swap causing an event
to be received.
2026-02-04 18:07:19 +01:00
Camilla Löwy
bafb134769 Documentation work
Elaborate on what 'work' means in the context of joystick input.
2026-01-27 20:51:00 +01:00
Camilla Löwy
2d7ae8f2d0 Wayland: Cleanup
This is only a semantic change.  The values are the same.
2026-01-15 21:09:01 +01:00
Camilla Löwy
96e0f49395 X11: Place argument assertion after platform check
This lets automated testing check that GLFW_NOT_INITIALIZED is emitted
for every public function.
2026-01-14 21:03:13 +01:00
6 changed files with 101 additions and 70 deletions

View File

@ -4520,7 +4520,8 @@ GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow*
* GLFW will pass those events on to the application callbacks before * GLFW will pass those events on to the application callbacks before
* returning. * returning.
* *
* Event processing is not required for joystick input to work. * Event processing is not required to receive joystick input. Joystick state
* is polled when a joystick input or gamepad input function is called.
* *
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR. * GLFW_PLATFORM_ERROR.
@ -4565,7 +4566,8 @@ GLFWAPI void glfwPollEvents(void);
* GLFW will pass those events on to the application callbacks before * GLFW will pass those events on to the application callbacks before
* returning. * returning.
* *
* Event processing is not required for joystick input to work. * Event processing is not required to receive joystick input. Joystick state
* is polled when a joystick input or gamepad input function is called.
* *
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR. * GLFW_PLATFORM_ERROR.
@ -4612,7 +4614,8 @@ GLFWAPI void glfwWaitEvents(void);
* GLFW will pass those events on to the application callbacks before * GLFW will pass those events on to the application callbacks before
* returning. * returning.
* *
* Event processing is not required for joystick input to work. * Event processing is not required to receive joystick input. Joystick state
* is polled when a joystick input or gamepad input function is called.
* *
* @param[in] timeout The maximum amount of time, in seconds, to wait. * @param[in] timeout The maximum amount of time, in seconds, to wait.
* *

View File

@ -834,7 +834,7 @@ int _glfwInitWayland(void)
createKeyTables(); createKeyTables();
_glfw.wl.xkb.context = xkb_context_new(0); _glfw.wl.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!_glfw.wl.xkb.context) if (!_glfw.wl.xkb.context)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,

View File

@ -360,7 +360,6 @@ typedef struct _GLFWwindowWayland
GLFWbool maximized; GLFWbool maximized;
GLFWbool activated; GLFWbool activated;
GLFWbool fullscreen; GLFWbool fullscreen;
GLFWbool hovered;
GLFWbool transparent; GLFWbool transparent;
GLFWbool scaleFramebuffer; GLFWbool scaleFramebuffer;
struct wl_surface* surface; struct wl_surface* surface;
@ -389,7 +388,6 @@ typedef struct _GLFWwindowWayland
struct libdecor_frame* frame; struct libdecor_frame* frame;
} libdecor; } libdecor;
_GLFWcursor* currentCursor;
double cursorPosX, cursorPosY; double cursorPosX, cursorPosY;
char* appId; char* appId;
@ -416,7 +414,6 @@ typedef struct _GLFWwindowWayland
GLFWbool decorations; GLFWbool decorations;
struct wl_buffer* buffer; struct wl_buffer* buffer;
_GLFWfallbackEdgeWayland top, left, right, bottom; _GLFWfallbackEdgeWayland top, left, right, bottom;
struct wl_surface* focus;
wl_fixed_t pointerX, pointerY; wl_fixed_t pointerX, pointerY;
const char* cursorName; const char* cursorName;
} fallback; } fallback;
@ -457,6 +454,7 @@ typedef struct _GLFWlibraryWayland
const char* tag; const char* tag;
struct wl_surface* pointerSurface;
struct wl_cursor_theme* cursorTheme; struct wl_cursor_theme* cursorTheme;
struct wl_cursor_theme* cursorThemeHiDPI; struct wl_cursor_theme* cursorThemeHiDPI;
struct wl_surface* cursorSurface; struct wl_surface* cursorSurface;
@ -515,7 +513,6 @@ typedef struct _GLFWlibraryWayland
PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym;
} xkb; } xkb;
_GLFWwindow* pointerFocus;
_GLFWwindow* keyboardFocus; _GLFWwindow* keyboardFocus;
struct { struct {

View File

@ -253,6 +253,9 @@ static void createFallbackDecorations(_GLFWwindow* window)
static void destroyFallbackEdge(_GLFWfallbackEdgeWayland* edge) static void destroyFallbackEdge(_GLFWfallbackEdgeWayland* edge)
{ {
if (edge->surface == _glfw.wl.pointerSurface)
_glfw.wl.pointerSurface = NULL;
if (edge->subsurface) if (edge->subsurface)
wl_subsurface_destroy(edge->subsurface); wl_subsurface_destroy(edge->subsurface);
if (edge->surface) if (edge->surface)
@ -288,26 +291,26 @@ static void updateFallbackDecorationCursor(_GLFWwindow* window,
if (window->resizable) if (window->resizable)
{ {
if (window->wl.fallback.focus == window->wl.fallback.top.surface) if (_glfw.wl.pointerSurface == window->wl.fallback.top.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
cursorName = "n-resize"; cursorName = "n-resize";
} }
else if (window->wl.fallback.focus == window->wl.fallback.left.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.left.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
cursorName = "nw-resize"; cursorName = "nw-resize";
else else
cursorName = "w-resize"; cursorName = "w-resize";
} }
else if (window->wl.fallback.focus == window->wl.fallback.right.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.right.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
cursorName = "ne-resize"; cursorName = "ne-resize";
else else
cursorName = "e-resize"; cursorName = "e-resize";
} }
else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.bottom.surface)
{ {
if (xpos < GLFW_BORDER_SIZE) if (xpos < GLFW_BORDER_SIZE)
cursorName = "sw-resize"; cursorName = "sw-resize";
@ -360,8 +363,12 @@ static void updateFallbackDecorationCursor(_GLFWwindow* window,
static void handleFallbackDecorationButton(_GLFWwindow* window, static void handleFallbackDecorationButton(_GLFWwindow* window,
uint32_t serial, uint32_t serial,
uint32_t button) uint32_t button,
uint32_t state)
{ {
if (state != WL_POINTER_BUTTON_STATE_PRESSED)
return;
const double xpos = wl_fixed_to_double(window->wl.fallback.pointerX); const double xpos = wl_fixed_to_double(window->wl.fallback.pointerX);
const double ypos = wl_fixed_to_double(window->wl.fallback.pointerY); const double ypos = wl_fixed_to_double(window->wl.fallback.pointerY);
@ -369,28 +376,28 @@ static void handleFallbackDecorationButton(_GLFWwindow* window,
{ {
uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
if (window->wl.fallback.focus == window->wl.fallback.top.surface) if (_glfw.wl.pointerSurface == window->wl.fallback.top.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
else else
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
} }
else if (window->wl.fallback.focus == window->wl.fallback.left.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.left.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
else else
edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
} }
else if (window->wl.fallback.focus == window->wl.fallback.right.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.right.surface)
{ {
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
else else
edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
} }
else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface) else if (_glfw.wl.pointerSurface == window->wl.fallback.bottom.surface)
{ {
if (xpos < GLFW_BORDER_SIZE) if (xpos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
@ -408,7 +415,7 @@ static void handleFallbackDecorationButton(_GLFWwindow* window,
if (!window->wl.xdg.toplevel) if (!window->wl.xdg.toplevel)
return; return;
if (window->wl.fallback.focus != window->wl.fallback.top.surface) if (_glfw.wl.pointerSurface != window->wl.fallback.top.surface)
return; return;
if (ypos < GLFW_BORDER_SIZE) if (ypos < GLFW_BORDER_SIZE)
@ -1267,14 +1274,16 @@ static void setCursorImage(_GLFWwindow* window,
wl_surface_commit(surface); wl_surface_commit(surface);
} }
static void incrementCursorImage(_GLFWwindow* window) static void incrementCursorImage(void)
{ {
_GLFWcursor* cursor; if (!_glfw.wl.pointerSurface)
if (!window || !window->wl.hovered)
return; return;
cursor = window->wl.currentCursor; _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->wl.surface != _glfw.wl.pointerSurface)
return;
_GLFWcursor* cursor = window->cursor;
if (cursor && cursor->wl.cursor) if (cursor && cursor->wl.cursor)
{ {
cursor->wl.currentImage += 1; cursor->wl.currentImage += 1;
@ -1436,7 +1445,7 @@ static void handleEvents(double* timeout)
uint64_t repeats; uint64_t repeats;
if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8) if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8)
incrementCursorImage(_glfw.wl.pointerFocus); incrementCursorImage();
} }
if (fds[LIBDECOR_FD].revents & POLLIN) if (fds[LIBDECOR_FD].revents & POLLIN)
@ -1527,16 +1536,14 @@ static void pointerHandleEnter(void* userData,
if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
return; return;
_GLFWwindow* window = wl_surface_get_user_data(surface);
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
_glfw.wl.pointerEnterSerial = serial; _glfw.wl.pointerEnterSerial = serial;
_glfw.wl.pointerFocus = window; _glfw.wl.pointerSurface = surface;
if (surface == window->wl.surface) _GLFWwindow* window = wl_surface_get_user_data(surface);
if (window->wl.surface == surface)
{ {
window->wl.hovered = GLFW_TRUE; _glfwSetCursorWayland(window, window->cursor);
_glfwSetCursorWayland(window, window->wl.currentCursor);
_glfwInputCursorEnter(window, GLFW_TRUE); _glfwInputCursorEnter(window, GLFW_TRUE);
if (window->cursorMode != GLFW_CURSOR_DISABLED) if (window->cursorMode != GLFW_CURSOR_DISABLED)
@ -1549,10 +1556,7 @@ static void pointerHandleEnter(void* userData,
else else
{ {
if (window->wl.fallback.decorations) if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = surface;
updateFallbackDecorationCursor(window, sx, sy); updateFallbackDecorationCursor(window, sx, sy);
}
} }
} }
@ -1567,25 +1571,16 @@ static void pointerHandleLeave(void* userData,
if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
return; return;
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window)
return;
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
_glfw.wl.pointerFocus = NULL; _glfw.wl.pointerSurface = NULL;
if (window->wl.hovered) _GLFWwindow* window = wl_surface_get_user_data(surface);
{ if (window->wl.surface == surface)
window->wl.hovered = GLFW_FALSE;
_glfwInputCursorEnter(window, GLFW_FALSE); _glfwInputCursorEnter(window, GLFW_FALSE);
}
else else
{ {
if (window->wl.fallback.decorations) if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = NULL;
window->wl.fallback.cursorName = NULL; window->wl.fallback.cursorName = NULL;
}
} }
} }
@ -1595,14 +1590,15 @@ static void pointerHandleMotion(void* userData,
wl_fixed_t sx, wl_fixed_t sx,
wl_fixed_t sy) wl_fixed_t sy)
{ {
_GLFWwindow* window = _glfw.wl.pointerFocus; if (!_glfw.wl.pointerSurface)
if (!window)
return; return;
_GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
return; return;
if (window->wl.hovered) if (window->wl.surface == _glfw.wl.pointerSurface)
{ {
window->wl.cursorPosX = wl_fixed_to_double(sx); window->wl.cursorPosX = wl_fixed_to_double(sx);
window->wl.cursorPosY = wl_fixed_to_double(sy); window->wl.cursorPosY = wl_fixed_to_double(sy);
@ -1622,11 +1618,12 @@ static void pointerHandleButton(void* userData,
uint32_t button, uint32_t button,
uint32_t state) uint32_t state)
{ {
_GLFWwindow* window = _glfw.wl.pointerFocus; if (!_glfw.wl.pointerSurface)
if (!window)
return; return;
if (window->wl.hovered) _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->wl.surface == _glfw.wl.pointerSurface)
{ {
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
@ -1638,7 +1635,7 @@ static void pointerHandleButton(void* userData,
else else
{ {
if (window->wl.fallback.decorations) if (window->wl.fallback.decorations)
handleFallbackDecorationButton(window, serial, button); handleFallbackDecorationButton(window, serial, button, state);
} }
} }
@ -1648,11 +1645,12 @@ static void pointerHandleAxis(void* userData,
uint32_t axis, uint32_t axis,
wl_fixed_t value) wl_fixed_t value)
{ {
_GLFWwindow* window = _glfw.wl.pointerFocus; if (!_glfw.wl.pointerSurface)
if (!window)
return; return;
if (window->wl.hovered) _GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->wl.surface == _glfw.wl.pointerSurface)
{ {
// NOTE: 10 units of motion per mouse wheel step seems to be a common ratio // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
@ -1701,7 +1699,7 @@ static void keyboardHandleKeymap(void* userData,
keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context,
mapStr, mapStr,
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_FORMAT_TEXT_V1,
0); XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(mapStr, size); munmap(mapStr, size);
close(fd); close(fd);
@ -1932,7 +1930,11 @@ static void seatHandleCapabilities(void* userData,
} }
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
{ {
wl_pointer_destroy(_glfw.wl.pointer); if (wl_pointer_get_version(_glfw.wl.pointer) >= WL_POINTER_RELEASE_SINCE_VERSION)
wl_pointer_release(_glfw.wl.pointer);
else
wl_pointer_destroy(_glfw.wl.pointer);
_glfw.wl.pointer = NULL; _glfw.wl.pointer = NULL;
} }
@ -1943,7 +1945,11 @@ static void seatHandleCapabilities(void* userData,
} }
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
{ {
wl_keyboard_destroy(_glfw.wl.keyboard); if (wl_keyboard_get_version(_glfw.wl.keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION)
wl_keyboard_release(_glfw.wl.keyboard);
else
wl_keyboard_destroy(_glfw.wl.keyboard);
_glfw.wl.keyboard = NULL; _glfw.wl.keyboard = NULL;
} }
} }
@ -2223,8 +2229,8 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
void _glfwDestroyWindowWayland(_GLFWwindow* window) void _glfwDestroyWindowWayland(_GLFWwindow* window)
{ {
if (window == _glfw.wl.pointerFocus) if (window->wl.surface == _glfw.wl.pointerSurface)
_glfw.wl.pointerFocus = NULL; _glfw.wl.pointerSurface = NULL;
if (window == _glfw.wl.keyboardFocus) if (window == _glfw.wl.keyboardFocus)
{ {
@ -2600,7 +2606,7 @@ GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window)
GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window) GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window)
{ {
return window->wl.hovered; return window->wl.surface == _glfw.wl.pointerSurface;
} }
GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window) GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window)
@ -2730,7 +2736,7 @@ void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode) void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
{ {
_glfwSetCursorWayland(window, window->wl.currentCursor); _glfwSetCursorWayland(window, window->cursor);
} }
const char* _glfwGetScancodeNameWayland(int scancode) const char* _glfwGetScancodeNameWayland(int scancode)
@ -3064,11 +3070,7 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
if (!_glfw.wl.pointer) if (!_glfw.wl.pointer)
return; return;
window->wl.currentCursor = cursor; if (window->wl.surface != _glfw.wl.pointerSurface)
// If we're not in the correct window just save the cursor
// the next time the pointer enters the window the cursor will change
if (!window->wl.hovered)
return; return;
// Update pointer lock to match cursor mode // Update pointer lock to match cursor mode

View File

@ -3344,8 +3344,6 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* handle)
GLFWAPI void glfwSetX11SelectionString(const char* string) GLFWAPI void glfwSetX11SelectionString(const char* string)
{ {
assert(string != NULL);
_GLFW_REQUIRE_INIT(); _GLFW_REQUIRE_INIT();
if (_glfw.platform.platformID != GLFW_PLATFORM_X11) if (_glfw.platform.platformID != GLFW_PLATFORM_X11)
@ -3354,6 +3352,8 @@ GLFWAPI void glfwSetX11SelectionString(const char* string)
return; return;
} }
assert(string != NULL);
_glfw_free(_glfw.x11.primarySelectionString); _glfw_free(_glfw.x11.primarySelectionString);
_glfw.x11.primarySelectionString = _glfw_strdup(string); _glfw.x11.primarySelectionString = _glfw_strdup(string);

View File

@ -36,6 +36,14 @@
#include <math.h> #include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h>
static bool needs_update;
static void window_close_callback(GLFWwindow* window)
{
needs_update = true;
}
static void error_callback(int error, const char* description) static void error_callback(int error, const char* description)
{ {
@ -45,7 +53,15 @@ static void error_callback(int error, const char* description)
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{ {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GLFW_TRUE); glfwSetWindowShouldClose(window, GLFW_TRUE);
needs_update = true;
}
}
static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
needs_update = true;
} }
static float nrand(void) static float nrand(void)
@ -72,8 +88,11 @@ int main(void)
} }
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
glfwSwapInterval(0);
gladLoadGL(glfwGetProcAddress); gladLoadGL(glfwGetProcAddress);
glfwSetWindowCloseCallback(window, window_close_callback);
glfwSetKeyCallback(window, key_callback); glfwSetKeyCallback(window, key_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
while (!glfwWindowShouldClose(window)) while (!glfwWindowShouldClose(window))
{ {
@ -87,8 +106,18 @@ int main(void)
glClearColor(r / l, g / l, b / l, 1.f); glClearColor(r / l, g / l, b / l, 1.f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glfwSwapBuffers(window); glfwSwapBuffers(window);
needs_update = false;
glfwWaitEventsTimeout(1.0); const double start = glfwGetTime();
while (!needs_update)
{
const double elapsed = glfwGetTime() - start;
if (elapsed >= 1.0)
needs_update = true;
else
glfwWaitEventsTimeout(1.0 - elapsed);
}
} }
glfwDestroyWindow(window); glfwDestroyWindow(window);