Compare commits

...

9 Commits

Author SHA1 Message Date
Peru S
13a65795f4
Merge 959f13c1bd into 161fb1b6f6 2025-08-14 00:21:39 +08:00
Camilla Löwy
161fb1b6f6 Wayland: Fix fallback decoration scroll events
The fallback decorations would emit scroll events as if scrolling had
occurred over the content area of the window.
2025-08-12 17:11:27 +02:00
Camilla Löwy
645a35a38e Wayland: Cleanup 2025-08-12 17:11:27 +02:00
Camilla Löwy
7523b0e6bd Wayland: Move fallback decoration pointer logic
Decluttered the wl_pointer handlers by moving the bulk of fallback
decoration related logic to separate functions.
2025-08-12 17:11:26 +02:00
Camilla Löwy
5190a30d8a Wayland: Move fallback decoration struct member
The cursorPreviousName member was only used for the fallback decorations
but was not grouped with other related members.
2025-08-12 17:11:26 +02:00
Camilla Löwy
ddbb8e0f2c Wayland: Fix fallback decoration cursor position
If fallback decorations were in use, pointer motion over a decoration
surface would cause glfwGetCursorPos to provide incorrect cursor
positions.

The cursor position is now only updated when the pointer is over the
content area of the window, similar to libdecor and XDG decorations.
2025-08-12 17:11:24 +02:00
Camilla Löwy
5245180c56 Formatting 2025-08-12 17:10:43 +02:00
Peru S
959f13c1bd Add to CONTRIBUTORS.md and README.md 2025-07-29 12:59:07 -07:00
Peru S
2fded1aab2 Fix windowProc when SetPropW() fails silently
**Issue**

In some cases, on Windows, `SetPropW` returns `FALSE`.
glfw currently silently ignores the result.
This means that the message pump `windowProc` fails to work (does not receive keyboard events for example). Drawing is fine though as it's a different GLFW mechanism.

I had this failure frequently infrequently and it was frustrating to deal with (just have to rerun the program a few times and hope `SetPropW` doesn't return `FALSE`). So sending this fix in case others are facing the issue on Windows.

**Fix**

On Windows+GLFW, `windowProc` will fallback to looking up the GLFW window list (just like `PollEvents` does).
Also added the error code to `_glfwInputError` message to aid debugging in the future.

My suggestion is to get rid of `SetPropW`/`GetPropW` as it is a string-based Windows API (slow/fragile) - we already have the lightweight linked list, might as well just use it always - also it's usually just the one or two windows we deal with.

- [x] Verified on Windows - message pump is stable with this fix (once I was able to repro this issue).
2025-07-29 12:53:19 -07:00
6 changed files with 237 additions and 191 deletions

View File

@ -232,6 +232,7 @@ video tutorials.
- Jan Schürkamp - Jan Schürkamp
- Christian Sdunek - Christian Sdunek
- Matt Sealey - Matt Sealey
- Perumaal Shanmugam
- Steve Sexton - Steve Sexton
- Arkady Shapkin - Arkady Shapkin
- Mingjie Shen - Mingjie Shen

View File

@ -135,11 +135,15 @@ information on what to include when reporting a bug.
- [Wayland] Bugfix: Reset key repeat timer when window destroyed (#2741,#2727) - [Wayland] Bugfix: Reset key repeat timer when window destroyed (#2741,#2727)
- [Wayland] Bugfix: Memory would leak if reading a data offer failed midway - [Wayland] Bugfix: Memory would leak if reading a data offer failed midway
- [Wayland] Bugfix: Keyboard leave event handler now processes key repeats (#2736) - [Wayland] Bugfix: Keyboard leave event handler now processes key repeats (#2736)
- [Wayland] Bugfix: Retrieved cursor position would be incorrect when hovering over
fallback decorations
- [Wayland] Bugfix: Fallback decorations would report scroll events
- [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631) - [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631)
- [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface` - [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface`
- [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless` - [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless`
- [EGL] Allowed native access on Wayland with `GLFW_CONTEXT_CREATION_API` set to - [EGL] Allowed native access on Wayland with `GLFW_CONTEXT_CREATION_API` set to
`GLFW_NATIVE_CONTEXT_API` (#2518) `GLFW_NATIVE_CONTEXT_API` (#2518)
- [Win32] Bugfix: Fix `windowProc` to work when `SetPropW` fails silently
## Contact ## Contact

View File

@ -493,18 +493,19 @@ void _glfwInputErrorWin32(int error, const char* description)
WCHAR buffer[_GLFW_MESSAGE_SIZE] = L""; WCHAR buffer[_GLFW_MESSAGE_SIZE] = L"";
char message[_GLFW_MESSAGE_SIZE] = ""; char message[_GLFW_MESSAGE_SIZE] = "";
DWORD lastError = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK, FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL, NULL,
GetLastError() & 0xffff, lastError & 0xffff,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buffer, buffer,
sizeof(buffer) / sizeof(WCHAR), sizeof(buffer) / sizeof(WCHAR),
NULL); NULL);
WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL);
_glfwInputError(error, "%s: %s", description, message); _glfwInputError(error, "%s (0x%lx / %lu): %s", description, lastError, lastError, message);
} }
// Updates key names according to the current keyboard layout // Updates key names according to the current keyboard layout

View File

@ -548,8 +548,23 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
EnableNonClientDpiScaling(hWnd); EnableNonClientDpiScaling(hWnd);
} }
} }
else
{
// HACK: SetPropW returns FALSE in some instances (GetLastError() = 0x8 Insufficient resources).
// We already have the global list of windows, check against it. It's not many, and we are already
// doing this in the (one of the callers) PollEvents below.
window = _glfw.windowListHead;
while (window)
{
if (window->win32.handle == hWnd) { break; }
window = window->next;
}
}
return DefWindowProcW(hWnd, uMsg, wParam, lParam); if (!window)
{
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
} }
switch (uMsg) switch (uMsg)
@ -1401,7 +1416,13 @@ static int createNativeWindow(_GLFWwindow* window,
return GLFW_FALSE; return GLFW_FALSE;
} }
SetPropW(window->win32.handle, L"GLFW", window); if (!SetPropW(window->win32.handle, L"GLFW", window))
{
// In some cases, SetPropW returns FALSE: GetLastError() returns 0x8 (Insufficient resources).
// The message pump fails to work because windowProc cannot look up the GLFW property.
// Instead of failing the program completely by raising an error instead, windowProc looks up
// the global window list to find the hWnd under consideration.
}
ChangeWindowMessageFilterEx(window->win32.handle, WM_DROPFILES, MSGFLT_ALLOW, NULL); ChangeWindowMessageFilterEx(window->win32.handle, WM_DROPFILES, MSGFLT_ALLOW, NULL);
ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYDATA, MSGFLT_ALLOW, NULL); ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYDATA, MSGFLT_ALLOW, NULL);

View File

@ -217,62 +217,62 @@ struct libdecor_configuration;
enum libdecor_error enum libdecor_error
{ {
LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE,
LIBDECOR_ERROR_INVALID_FRAME_CONFIGURATION, LIBDECOR_ERROR_INVALID_FRAME_CONFIGURATION,
}; };
enum libdecor_window_state enum libdecor_window_state
{ {
LIBDECOR_WINDOW_STATE_NONE = 0, LIBDECOR_WINDOW_STATE_NONE = 0,
LIBDECOR_WINDOW_STATE_ACTIVE = 1, LIBDECOR_WINDOW_STATE_ACTIVE = 1,
LIBDECOR_WINDOW_STATE_MAXIMIZED = 2, LIBDECOR_WINDOW_STATE_MAXIMIZED = 2,
LIBDECOR_WINDOW_STATE_FULLSCREEN = 4, LIBDECOR_WINDOW_STATE_FULLSCREEN = 4,
LIBDECOR_WINDOW_STATE_TILED_LEFT = 8, LIBDECOR_WINDOW_STATE_TILED_LEFT = 8,
LIBDECOR_WINDOW_STATE_TILED_RIGHT = 16, LIBDECOR_WINDOW_STATE_TILED_RIGHT = 16,
LIBDECOR_WINDOW_STATE_TILED_TOP = 32, LIBDECOR_WINDOW_STATE_TILED_TOP = 32,
LIBDECOR_WINDOW_STATE_TILED_BOTTOM = 64 LIBDECOR_WINDOW_STATE_TILED_BOTTOM = 64
}; };
enum libdecor_capabilities enum libdecor_capabilities
{ {
LIBDECOR_ACTION_MOVE = 1, LIBDECOR_ACTION_MOVE = 1,
LIBDECOR_ACTION_RESIZE = 2, LIBDECOR_ACTION_RESIZE = 2,
LIBDECOR_ACTION_MINIMIZE = 4, LIBDECOR_ACTION_MINIMIZE = 4,
LIBDECOR_ACTION_FULLSCREEN = 8, LIBDECOR_ACTION_FULLSCREEN = 8,
LIBDECOR_ACTION_CLOSE = 16 LIBDECOR_ACTION_CLOSE = 16
}; };
struct libdecor_interface struct libdecor_interface
{ {
void (* error)(struct libdecor*,enum libdecor_error,const char*); void (* error)(struct libdecor*,enum libdecor_error,const char*);
void (* reserved0)(void); void (* reserved0)(void);
void (* reserved1)(void); void (* reserved1)(void);
void (* reserved2)(void); void (* reserved2)(void);
void (* reserved3)(void); void (* reserved3)(void);
void (* reserved4)(void); void (* reserved4)(void);
void (* reserved5)(void); void (* reserved5)(void);
void (* reserved6)(void); void (* reserved6)(void);
void (* reserved7)(void); void (* reserved7)(void);
void (* reserved8)(void); void (* reserved8)(void);
void (* reserved9)(void); void (* reserved9)(void);
}; };
struct libdecor_frame_interface struct libdecor_frame_interface
{ {
void (* configure)(struct libdecor_frame*,struct libdecor_configuration*,void*); void (* configure)(struct libdecor_frame*,struct libdecor_configuration*,void*);
void (* close)(struct libdecor_frame*,void*); void (* close)(struct libdecor_frame*,void*);
void (* commit)(struct libdecor_frame*,void*); void (* commit)(struct libdecor_frame*,void*);
void (* dismiss_popup)(struct libdecor_frame*,const char*,void*); void (* dismiss_popup)(struct libdecor_frame*,const char*,void*);
void (* reserved0)(void); void (* reserved0)(void);
void (* reserved1)(void); void (* reserved1)(void);
void (* reserved2)(void); void (* reserved2)(void);
void (* reserved3)(void); void (* reserved3)(void);
void (* reserved4)(void); void (* reserved4)(void);
void (* reserved5)(void); void (* reserved5)(void);
void (* reserved6)(void); void (* reserved6)(void);
void (* reserved7)(void); void (* reserved7)(void);
void (* reserved8)(void); void (* reserved8)(void);
void (* reserved9)(void); void (* reserved9)(void);
}; };
typedef struct libdecor* (* PFN_libdecor_new)(struct wl_display*,const struct libdecor_interface*); typedef struct libdecor* (* PFN_libdecor_new)(struct wl_display*,const struct libdecor_interface*);
@ -413,6 +413,8 @@ typedef struct _GLFWwindowWayland
struct wl_buffer* buffer; struct wl_buffer* buffer;
_GLFWfallbackEdgeWayland top, left, right, bottom; _GLFWfallbackEdgeWayland top, left, right, bottom;
struct wl_surface* focus; struct wl_surface* focus;
wl_fixed_t pointerX, pointerY;
const char* cursorName;
} fallback; } fallback;
} _GLFWwindowWayland; } _GLFWwindowWayland;
@ -454,7 +456,6 @@ typedef struct _GLFWlibraryWayland
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;
const char* cursorPreviousName;
int cursorTimerfd; int cursorTimerfd;
uint32_t serial; uint32_t serial;
uint32_t pointerEnterSerial; uint32_t pointerEnterSerial;

View File

@ -275,6 +275,146 @@ static void destroyFallbackDecorations(_GLFWwindow* window)
destroyFallbackEdge(&window->wl.fallback.bottom); destroyFallbackEdge(&window->wl.fallback.bottom);
} }
static void updateFallbackDecorationCursor(_GLFWwindow* window,
wl_fixed_t sx,
wl_fixed_t sy)
{
window->wl.fallback.pointerX = sx;
window->wl.fallback.pointerY = sy;
const double xpos = wl_fixed_to_double(sx);
const double ypos = wl_fixed_to_double(sy);
const char* cursorName = "left_ptr";
if (window->resizable)
{
if (window->wl.fallback.focus == window->wl.fallback.top.surface)
{
if (ypos < GLFW_BORDER_SIZE)
cursorName = "n-resize";
}
else if (window->wl.fallback.focus == 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)
{
if (ypos < GLFW_BORDER_SIZE)
cursorName = "ne-resize";
else
cursorName = "e-resize";
}
else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
{
if (xpos < GLFW_BORDER_SIZE)
cursorName = "sw-resize";
else if (xpos > window->wl.width + GLFW_BORDER_SIZE)
cursorName = "se-resize";
else
cursorName = "s-resize";
}
}
if (window->wl.fallback.cursorName != cursorName)
{
struct wl_surface* surface = _glfw.wl.cursorSurface;
struct wl_cursor_theme* theme = _glfw.wl.cursorTheme;
int scale = 1;
if (window->wl.bufferScale > 1 && _glfw.wl.cursorThemeHiDPI)
{
// We only support up to scale=2 for now, since libwayland-cursor
// requires us to load a different theme for each size.
scale = 2;
theme = _glfw.wl.cursorThemeHiDPI;
}
struct wl_cursor* cursor = wl_cursor_theme_get_cursor(theme, cursorName);
if (!cursor)
return;
// TODO: handle animated cursors too.
struct wl_cursor_image* image = cursor->images[0];
if (!image)
return;
struct wl_buffer* buffer = wl_cursor_image_get_buffer(image);
if (!buffer)
return;
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
surface,
image->hotspot_x / scale,
image->hotspot_y / scale);
wl_surface_set_buffer_scale(surface, scale);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage(surface, 0, 0, image->width, image->height);
wl_surface_commit(surface);
window->wl.fallback.cursorName = cursorName;
}
}
static void handleFallbackDecorationButton(_GLFWwindow* window,
uint32_t serial,
uint32_t button)
{
const double xpos = wl_fixed_to_double(window->wl.fallback.pointerX);
const double ypos = wl_fixed_to_double(window->wl.fallback.pointerY);
if (button == BTN_LEFT)
{
uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
if (window->wl.fallback.focus == 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)
{
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)
{
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)
{
if (xpos < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
else if (xpos > window->wl.width + GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
else
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
}
if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE)
xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, serial, edges);
}
else if (button == BTN_RIGHT)
{
if (window->wl.xdg.toplevel)
{
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
_glfw.wl.seat, serial,
window->wl.cursorPosX,
window->wl.cursorPosY);
}
}
}
static void xdgDecorationHandleConfigure(void* userData, static void xdgDecorationHandleConfigure(void* userData,
struct zxdg_toplevel_decoration_v1* decoration, struct zxdg_toplevel_decoration_v1* decoration,
uint32_t mode) uint32_t mode)
@ -1417,7 +1557,6 @@ static void pointerHandleLeave(void* userData,
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
_glfw.wl.pointerFocus = NULL; _glfw.wl.pointerFocus = NULL;
_glfw.wl.cursorPreviousName = NULL;
if (window->wl.hovered) if (window->wl.hovered)
{ {
@ -1427,7 +1566,10 @@ static void pointerHandleLeave(void* userData,
else else
{ {
if (window->wl.fallback.decorations) if (window->wl.fallback.decorations)
{
window->wl.fallback.focus = NULL; window->wl.fallback.focus = NULL;
window->wl.fallback.cursorName = NULL;
}
} }
} }
@ -1444,92 +1586,16 @@ static void pointerHandleMotion(void* userData,
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
return; return;
const double xpos = wl_fixed_to_double(sx);
const double ypos = wl_fixed_to_double(sy);
window->wl.cursorPosX = xpos;
window->wl.cursorPosY = ypos;
if (window->wl.hovered) if (window->wl.hovered)
{ {
_glfw.wl.cursorPreviousName = NULL; window->wl.cursorPosX = wl_fixed_to_double(sx);
_glfwInputCursorPos(window, xpos, ypos); window->wl.cursorPosY = wl_fixed_to_double(sy);
return; _glfwInputCursorPos(window, window->wl.cursorPosX, window->wl.cursorPosY);
} }
else
if (window->wl.fallback.decorations)
{ {
const char* cursorName = "left_ptr"; if (window->wl.fallback.decorations)
updateFallbackDecorationCursor(window, sx, sy);
if (window->resizable)
{
if (window->wl.fallback.focus == window->wl.fallback.top.surface)
{
if (ypos < GLFW_BORDER_SIZE)
cursorName = "n-resize";
}
else if (window->wl.fallback.focus == 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)
{
if (ypos < GLFW_BORDER_SIZE)
cursorName = "ne-resize";
else
cursorName = "e-resize";
}
else if (window->wl.fallback.focus == window->wl.fallback.bottom.surface)
{
if (xpos < GLFW_BORDER_SIZE)
cursorName = "sw-resize";
else if (xpos > window->wl.width + GLFW_BORDER_SIZE)
cursorName = "se-resize";
else
cursorName = "s-resize";
}
}
if (_glfw.wl.cursorPreviousName != cursorName)
{
struct wl_surface* surface = _glfw.wl.cursorSurface;
struct wl_cursor_theme* theme = _glfw.wl.cursorTheme;
int scale = 1;
if (window->wl.bufferScale > 1 && _glfw.wl.cursorThemeHiDPI)
{
// We only support up to scale=2 for now, since libwayland-cursor
// requires us to load a different theme for each size.
scale = 2;
theme = _glfw.wl.cursorThemeHiDPI;
}
struct wl_cursor* cursor = wl_cursor_theme_get_cursor(theme, cursorName);
if (!cursor)
return;
// TODO: handle animated cursors too.
struct wl_cursor_image* image = cursor->images[0];
if (!image)
return;
struct wl_buffer* buffer = wl_cursor_image_get_buffer(image);
if (!buffer)
return;
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial,
surface,
image->hotspot_x / scale,
image->hotspot_y / scale);
wl_surface_set_buffer_scale(surface, scale);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_damage(surface, 0, 0, image->width, image->height);
wl_surface_commit(surface);
_glfw.wl.cursorPreviousName = cursorName;
}
} }
} }
@ -1552,62 +1618,11 @@ static void pointerHandleButton(void* userData,
button - BTN_LEFT, button - BTN_LEFT,
state == WL_POINTER_BUTTON_STATE_PRESSED, state == WL_POINTER_BUTTON_STATE_PRESSED,
_glfw.wl.xkb.modifiers); _glfw.wl.xkb.modifiers);
return;
} }
else
if (window->wl.fallback.decorations)
{ {
if (button == BTN_LEFT) if (window->wl.fallback.decorations)
{ handleFallbackDecorationButton(window, serial, button);
uint32_t edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
if (window->wl.fallback.focus == window->wl.fallback.top.surface)
{
if (window->wl.cursorPosY < 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)
{
if (window->wl.cursorPosY < 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)
{
if (window->wl.cursorPosY < 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)
{
if (window->wl.cursorPosX < GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
else if (window->wl.cursorPosX > window->wl.width + GLFW_BORDER_SIZE)
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
else
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
}
if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE)
{
xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat,
serial, edges);
}
}
else if (button == BTN_RIGHT)
{
if (window->wl.xdg.toplevel)
{
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
_glfw.wl.seat, serial,
window->wl.cursorPosX,
window->wl.cursorPosY);
}
}
} }
} }
@ -1621,11 +1636,14 @@ static void pointerHandleAxis(void* userData,
if (!window) if (!window)
return; return;
// NOTE: 10 units of motion per mouse wheel step seems to be a common ratio if (window->wl.hovered)
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
_glfwInputScroll(window, -wl_fixed_to_double(value) / 10.0, 0.0); // NOTE: 10 units of motion per mouse wheel step seems to be a common ratio
else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
_glfwInputScroll(window, 0.0, -wl_fixed_to_double(value) / 10.0); _glfwInputScroll(window, -wl_fixed_to_double(value) / 10.0, 0.0);
else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
_glfwInputScroll(window, 0.0, -wl_fixed_to_double(value) / 10.0);
}
} }
static const struct wl_pointer_listener pointerListener = static const struct wl_pointer_listener pointerListener =
@ -2207,12 +2225,12 @@ void _glfwDestroyWindowWayland(_GLFWwindow* window)
_glfw.wl.pointerFocus = NULL; _glfw.wl.pointerFocus = NULL;
if (window == _glfw.wl.keyboardFocus) if (window == _glfw.wl.keyboardFocus)
{ {
struct itimerspec timer = {0}; struct itimerspec timer = {0};
timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
_glfw.wl.keyboardFocus = NULL; _glfw.wl.keyboardFocus = NULL;
} }
if (window->wl.fractionalScale) if (window->wl.fractionalScale)
wp_fractional_scale_v1_destroy(window->wl.fractionalScale); wp_fractional_scale_v1_destroy(window->wl.fractionalScale);