Compare commits

...

10 Commits

Author SHA1 Message Date
Mārtiņš Možeiko
b069b041b8
Merge 11d96a4ab4 into 232164f62b 2026-02-05 20:57:42 +03: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
Mārtiņš Možeiko
11d96a4ab4 Win32: Do the event processing during window resize/move 2025-01-30 01:34:18 -08:00
12 changed files with 273 additions and 105 deletions

View File

@ -153,10 +153,23 @@ the `VK_KHR_xlib_surface` extension. Possible values are `GLFW_TRUE` and
`GLFW_FALSE`. This is ignored on other platforms.
#### Windows specific init hints {#init_hints_windows}
@anchor GLFW_WIN32_MESSAGES_IN_FIBER_hint
__GLFW_WIN32_MESSAGES_IN_FIBER__ specifies whether to use separate fiber for
processing Windows message, and not the main thread. This allows to unblock
window move and resize operations, and application can do updates and rendering
in its main loop. __Warning__ - setting this hint is not safe for environments
or frameworks where stack inspection is required (for example, moving garbage
collected languages such as Java or C#). Set this with @ref glfwInitHint.
#### Supported and default values {#init_hints_values}
Initialization hint | Default value | Supported values
-------------------------------- | ------------------------------- | ----------------
--------------------------------- | ------------------------------- | ----------------
--------------------------------- | ------------------------------- | ----------------
@ref GLFW_PLATFORM | `GLFW_ANY_PLATFORM` | `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`, `GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` or `GLFW_PLATFORM_NULL`
@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_ANGLE_PLATFORM_TYPE | `GLFW_ANGLE_PLATFORM_TYPE_NONE` | `GLFW_ANGLE_PLATFORM_TYPE_NONE`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGL`, `GLFW_ANGLE_PLATFORM_TYPE_OPENGLES`, `GLFW_ANGLE_PLATFORM_TYPE_D3D9`, `GLFW_ANGLE_PLATFORM_TYPE_D3D11`, `GLFW_ANGLE_PLATFORM_TYPE_VULKAN` or `GLFW_ANGLE_PLATFORM_TYPE_METAL`
@ -164,6 +177,7 @@ Initialization hint | Default value | Supported v
@ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_WAYLAND_LIBDECOR | `GLFW_WAYLAND_PREFER_LIBDECOR` | `GLFW_WAYLAND_PREFER_LIBDECOR` or `GLFW_WAYLAND_DISABLE_LIBDECOR`
@ref GLFW_X11_XCB_VULKAN_SURFACE | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@ref GLFW_WIN32_MESSAGES_IN_FIBER | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE`
### Runtime platform selection {#platform}

View File

@ -1328,6 +1328,11 @@ extern "C" {
* Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint).
*/
#define GLFW_WAYLAND_LIBDECOR 0x00053001
/*! @brief Windows specific init hint.
*
* Windows specific [init hint](@ref GLFW_WIN32_MESSAGES_IN_FIBER_hint).
*/
#define GLFW_WIN32_MESSAGES_IN_FIBER 0x00054001
/*! @} */
/*! @addtogroup init
@ -1604,6 +1609,10 @@ typedef void (* GLFWerrorfun)(int error_code, const char* description);
* @param[in] ypos The new y-coordinate, in screen coordinates, of the
* upper-left corner of the content area of the window.
*
* @remark @win32 On Windows moving or resizing of window will block event
* processing. Workaround for this is @ref GLFW_WIN32_MESSAGES_IN_FIBER init
* hint.
*
* @sa @ref window_pos
* @sa @ref glfwSetWindowPosCallback
*
@ -1625,6 +1634,10 @@ typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos);
* @param[in] width The new width, in screen coordinates, of the window.
* @param[in] height The new height, in screen coordinates, of the window.
*
* @remark @win32 On Windows moving or resizing of window will block event
* processing. Workaround for this is @ref GLFW_WIN32_MESSAGES_IN_FIBER init
* hint.
*
* @sa @ref window_size
* @sa @ref glfwSetWindowSizeCallback
*
@ -4520,7 +4533,8 @@ GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow*
* GLFW will pass those events on to the application callbacks before
* 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
* GLFW_PLATFORM_ERROR.
@ -4565,7 +4579,8 @@ GLFWAPI void glfwPollEvents(void);
* GLFW will pass those events on to the application callbacks before
* 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
* GLFW_PLATFORM_ERROR.
@ -4612,7 +4627,8 @@ GLFWAPI void glfwWaitEvents(void);
* GLFW will pass those events on to the application callbacks before
* 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.
*

View File

@ -66,6 +66,10 @@ static _GLFWinitconfig _glfwInitHints =
{
.libdecorMode = GLFW_WAYLAND_PREFER_LIBDECOR
},
.win32 =
{
.msgInFiber = GLFW_FALSE,
}
};
// The allocation function used when no custom allocator is set
@ -462,6 +466,9 @@ GLFWAPI void glfwInitHint(int hint, int value)
case GLFW_WAYLAND_LIBDECOR:
_glfwInitHints.wl.libdecorMode = value;
return;
case GLFW_WIN32_MESSAGES_IN_FIBER:
_glfwInitHints.win32.msgInFiber = value;
return;
}
_glfwInputError(GLFW_INVALID_ENUM,

View File

@ -388,6 +388,9 @@ struct _GLFWinitconfig
struct {
int libdecorMode;
} wl;
struct {
GLFWbool msgInFiber;
} win32;
};
// Window configuration

View File

@ -366,7 +366,6 @@ static LRESULT CALLBACK helperWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LP
//
static GLFWbool createHelperWindow(void)
{
MSG msg;
WNDCLASSEXW wc = { sizeof(wc) };
wc.style = CS_OWNDC;
@ -417,11 +416,19 @@ static GLFWbool createHelperWindow(void)
DEVICE_NOTIFY_WINDOW_HANDLE);
}
if (_glfw.hints.init.win32.msgInFiber)
{
SwitchToFiber(_glfw.win32.messageFiber);
}
else
{
MSG msg;
while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
return GLFW_TRUE;
}
@ -595,6 +602,45 @@ BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build)
return RtlVerifyVersionInfo(&osvi, mask, cond) == 0;
}
void _glfwPollMessageLoopWin32(void)
{
_GLFWwindow* window;
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
// NOTE: While GLFW does not itself post WM_QUIT, other processes
// may post it to this one, for example Task Manager
// HACK: Treat WM_QUIT as a close on all windows
window = _glfw.windowListHead;
while (window)
{
_glfwInputWindowCloseRequest(window);
window = window->next;
}
}
else
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
// Windows message dispatch fiber
void CALLBACK messageFiberProc(LPVOID lpFiberParameter)
{
(void)lpFiberParameter;
for (;;)
{
_glfwPollMessageLoopWin32();
SwitchToFiber(_glfw.win32.mainFiber);
}
}
GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform)
{
const _GLFWplatform win32 =
@ -692,6 +738,17 @@ int _glfwInitWin32(void)
else
SetProcessDPIAware();
if (_glfw.hints.init.win32.msgInFiber)
{
_glfw.win32.mainFiber = ConvertThreadToFiber(NULL);
if (!_glfw.win32.mainFiber)
return GLFW_FALSE;
_glfw.win32.messageFiber = CreateFiber(0, &messageFiberProc, NULL);
if (!_glfw.win32.messageFiber)
return GLFW_FALSE;
}
if (!createHelperWindow())
return GLFW_FALSE;
@ -714,6 +771,12 @@ void _glfwTerminateWin32(void)
if (_glfw.win32.mainWindowClass)
UnregisterClassW(MAKEINTATOM(_glfw.win32.mainWindowClass), _glfw.win32.instance);
if (_glfw.hints.init.win32.msgInFiber)
{
DeleteFiber(_glfw.win32.messageFiber);
ConvertFiberToThread();
}
_glfw_free(_glfw.win32.clipboardString);
_glfw_free(_glfw.win32.rawInput);

View File

@ -375,6 +375,11 @@ typedef struct _GLFWwindowWin32
int lastCursorPosX, lastCursorPosY;
// The last received high surrogate when decoding pairs of UTF-16 messages
WCHAR highSurrogate;
// If user pressed mouse button on window title bar
UINT ncMouseButton;
LPARAM ncMousePos;
} _GLFWwindowWin32;
// Win32-specific global data
@ -403,6 +408,9 @@ typedef struct _GLFWlibraryWin32
// The cursor handle to use to hide the cursor (NULL or a transparent cursor)
HCURSOR blankCursor;
LPVOID messageFiber;
LPVOID mainFiber;
struct {
HINSTANCE instance;
PFN_DirectInput8Create Create;
@ -519,6 +527,7 @@ void _glfwSetWindowOpacityWin32(_GLFWwindow* window, float opacity);
void _glfwSetRawMouseMotionWin32(_GLFWwindow *window, GLFWbool enabled);
GLFWbool _glfwRawMouseMotionSupportedWin32(void);
void _glfwPollMessageLoopWin32(void);
void _glfwPollEventsWin32(void);
void _glfwWaitEventsWin32(void);
void _glfwWaitEventsTimeoutWin32(double timeout);

View File

@ -853,11 +853,46 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
return 0;
}
case WM_NCLBUTTONDOWN:
{
if (wParam == HTCAPTION)
{
window->win32.ncMouseButton = uMsg;
window->win32.ncMousePos = lParam;
return 0;
}
break;
}
case WM_NCMOUSEMOVE:
{
if (window->win32.ncMouseButton)
{
if (GET_X_LPARAM(window->win32.ncMousePos) != GET_X_LPARAM(lParam) ||
GET_Y_LPARAM(window->win32.ncMousePos) != GET_Y_LPARAM(lParam))
{
DefWindowProcW(hWnd, window->win32.ncMouseButton, HTCAPTION, window->win32.ncMousePos);
window->win32.ncMouseButton = 0;
}
}
break;
}
case WM_MOUSEMOVE:
{
const int x = GET_X_LPARAM(lParam);
const int y = GET_Y_LPARAM(lParam);
if (window->win32.ncMouseButton)
{
if (GET_X_LPARAM(window->win32.ncMousePos) != x ||
GET_Y_LPARAM(window->win32.ncMousePos) != y)
{
DefWindowProcW(hWnd, window->win32.ncMouseButton, HTCAPTION, window->win32.ncMousePos);
window->win32.ncMouseButton = 0;
}
}
if (!window->win32.cursorTracked)
{
TRACKMOUSEEVENT tme;
@ -998,6 +1033,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
releaseCursor();
if (_glfw.hints.init.win32.msgInFiber)
SetTimer(hWnd, 1, 1, NULL);
break;
}
@ -1014,6 +1051,15 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
captureCursor(window);
if (_glfw.hints.init.win32.msgInFiber)
KillTimer(hWnd, 1);
break;
}
case WM_TIMER:
{
if (_glfw.hints.init.win32.msgInFiber && wParam == 1)
SwitchToFiber(_glfw.win32.mainFiber);
break;
}
@ -2088,31 +2134,13 @@ GLFWbool _glfwRawMouseMotionSupportedWin32(void)
void _glfwPollEventsWin32(void)
{
MSG msg;
HWND handle;
_GLFWwindow* window;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
// NOTE: While GLFW does not itself post WM_QUIT, other processes
// may post it to this one, for example Task Manager
// HACK: Treat WM_QUIT as a close on all windows
window = _glfw.windowListHead;
while (window)
{
_glfwInputWindowCloseRequest(window);
window = window->next;
}
}
if (_glfw.hints.init.win32.msgInFiber)
SwitchToFiber(_glfw.win32.messageFiber);
else
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
_glfwPollMessageLoopWin32();
// HACK: Release modifier keys that the system did not emit KEYUP for
// NOTE: Shift keys on Windows tend to "stick" when both are pressed as

View File

@ -834,7 +834,7 @@ int _glfwInitWayland(void)
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)
{
_glfwInputError(GLFW_PLATFORM_ERROR,

View File

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

View File

@ -253,6 +253,9 @@ static void createFallbackDecorations(_GLFWwindow* window)
static void destroyFallbackEdge(_GLFWfallbackEdgeWayland* edge)
{
if (edge->surface == _glfw.wl.pointerSurface)
_glfw.wl.pointerSurface = NULL;
if (edge->subsurface)
wl_subsurface_destroy(edge->subsurface);
if (edge->surface)
@ -288,26 +291,26 @@ static void updateFallbackDecorationCursor(_GLFWwindow* window,
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)
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)
cursorName = "nw-resize";
else
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)
cursorName = "ne-resize";
else
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)
cursorName = "sw-resize";
@ -360,8 +363,12 @@ static void updateFallbackDecorationCursor(_GLFWwindow* window,
static void handleFallbackDecorationButton(_GLFWwindow* window,
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 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;
if (window->wl.fallback.focus == window->wl.fallback.top.surface)
if (_glfw.wl.pointerSurface == window->wl.fallback.top.surface)
{
if (ypos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
else
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)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
else
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)
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
else
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)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
@ -408,7 +415,7 @@ static void handleFallbackDecorationButton(_GLFWwindow* window,
if (!window->wl.xdg.toplevel)
return;
if (window->wl.fallback.focus != window->wl.fallback.top.surface)
if (_glfw.wl.pointerSurface != window->wl.fallback.top.surface)
return;
if (ypos < GLFW_BORDER_SIZE)
@ -1267,14 +1274,16 @@ static void setCursorImage(_GLFWwindow* window,
wl_surface_commit(surface);
}
static void incrementCursorImage(_GLFWwindow* window)
static void incrementCursorImage(void)
{
_GLFWcursor* cursor;
if (!window || !window->wl.hovered)
if (!_glfw.wl.pointerSurface)
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)
{
cursor->wl.currentImage += 1;
@ -1436,7 +1445,7 @@ static void handleEvents(double* timeout)
uint64_t repeats;
if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8)
incrementCursorImage(_glfw.wl.pointerFocus);
incrementCursorImage();
}
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)
return;
_GLFWwindow* window = wl_surface_get_user_data(surface);
_glfw.wl.serial = 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->wl.currentCursor);
_glfwSetCursorWayland(window, window->cursor);
_glfwInputCursorEnter(window, GLFW_TRUE);
if (window->cursorMode != GLFW_CURSOR_DISABLED)
@ -1549,12 +1556,9 @@ static void pointerHandleEnter(void* userData,
else
{
if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = surface;
updateFallbackDecorationCursor(window, sx, sy);
}
}
}
static void pointerHandleLeave(void* userData,
struct wl_pointer* pointer,
@ -1567,27 +1571,18 @@ static void pointerHandleLeave(void* userData,
if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag)
return;
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window)
return;
_glfw.wl.serial = serial;
_glfw.wl.pointerFocus = NULL;
_glfw.wl.pointerSurface = NULL;
if (window->wl.hovered)
{
window->wl.hovered = GLFW_FALSE;
_GLFWwindow* window = wl_surface_get_user_data(surface);
if (window->wl.surface == surface)
_glfwInputCursorEnter(window, GLFW_FALSE);
}
else
{
if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = NULL;
window->wl.fallback.cursorName = NULL;
}
}
}
static void pointerHandleMotion(void* userData,
struct wl_pointer* pointer,
@ -1595,14 +1590,15 @@ static void pointerHandleMotion(void* userData,
wl_fixed_t sx,
wl_fixed_t sy)
{
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window)
if (!_glfw.wl.pointerSurface)
return;
_GLFWwindow* window = wl_surface_get_user_data(_glfw.wl.pointerSurface);
if (window->cursorMode == GLFW_CURSOR_DISABLED)
return;
if (window->wl.hovered)
if (window->wl.surface == _glfw.wl.pointerSurface)
{
window->wl.cursorPosX = wl_fixed_to_double(sx);
window->wl.cursorPosY = wl_fixed_to_double(sy);
@ -1622,11 +1618,12 @@ static void pointerHandleButton(void* userData,
uint32_t button,
uint32_t state)
{
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window)
if (!_glfw.wl.pointerSurface)
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;
@ -1638,7 +1635,7 @@ static void pointerHandleButton(void* userData,
else
{
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,
wl_fixed_t value)
{
_GLFWwindow* window = _glfw.wl.pointerFocus;
if (!window)
if (!_glfw.wl.pointerSurface)
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
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,
mapStr,
XKB_KEYMAP_FORMAT_TEXT_V1,
0);
XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(mapStr, size);
close(fd);
@ -1932,7 +1930,11 @@ static void seatHandleCapabilities(void* userData,
}
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _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;
}
@ -1943,7 +1945,11 @@ static void seatHandleCapabilities(void* userData,
}
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _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;
}
}
@ -2223,8 +2229,8 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
void _glfwDestroyWindowWayland(_GLFWwindow* window)
{
if (window == _glfw.wl.pointerFocus)
_glfw.wl.pointerFocus = NULL;
if (window->wl.surface == _glfw.wl.pointerSurface)
_glfw.wl.pointerSurface = NULL;
if (window == _glfw.wl.keyboardFocus)
{
@ -2600,7 +2606,7 @@ GLFWbool _glfwWindowMaximizedWayland(_GLFWwindow* window)
GLFWbool _glfwWindowHoveredWayland(_GLFWwindow* window)
{
return window->wl.hovered;
return window->wl.surface == _glfw.wl.pointerSurface;
}
GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window)
@ -2730,7 +2736,7 @@ void _glfwSetCursorPosWayland(_GLFWwindow* window, double x, double y)
void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
{
_glfwSetCursorWayland(window, window->wl.currentCursor);
_glfwSetCursorWayland(window, window->cursor);
}
const char* _glfwGetScancodeNameWayland(int scancode)
@ -3064,11 +3070,7 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
if (!_glfw.wl.pointer)
return;
window->wl.currentCursor = cursor;
// 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)
if (window->wl.surface != _glfw.wl.pointerSurface)
return;
// Update pointer lock to match cursor mode

View File

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

View File

@ -36,6 +36,14 @@
#include <math.h>
#include <stdio.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)
{
@ -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)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
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)
@ -72,8 +88,11 @@ int main(void)
}
glfwMakeContextCurrent(window);
glfwSwapInterval(0);
gladLoadGL(glfwGetProcAddress);
glfwSetWindowCloseCallback(window, window_close_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
while (!glfwWindowShouldClose(window))
{
@ -87,8 +106,18 @@ int main(void)
glClearColor(r / l, g / l, b / l, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
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);