Compare commits

...

11 Commits

Author SHA1 Message Date
Santiago
ad5480e1db
Merge c8bb716886 into 0d2d85d19c 2025-08-18 12:02:03 +00:00
Doug Binks
0d2d85d19c Revert "Wayland: Keyboard leave event handler now processes key repeats" 2025-08-15 11:27:59 +02:00
Jan Hendrik Farr
768e81a0eb Wayland: Fix key repeat halting
Key repeat shoud only be halted when the repeating key
is released, not when another key is released.
2025-08-14 15:35:04 +02: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
Santiago
c8bb716886 Add premake file 2025-07-10 20:16:29 +02:00
Santiago
127ae123fe Add option to set OS dark theme 2025-07-10 20:16:16 +02:00
12 changed files with 394 additions and 208 deletions

View File

@ -68,6 +68,7 @@ video tutorials.
- Jan Ekström - Jan Ekström
- Siavash Eliasi - Siavash Eliasi
- er-azh - er-azh
- Jan Hendrik Farr
- Ahmad Fatoum - Ahmad Fatoum
- Nikita Fediuchin - Nikita Fediuchin
- Felipe Ferreira - Felipe Ferreira

View File

@ -135,6 +135,10 @@ 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
- [Wayland] Bugfix: Keyboard repeat events halted when any key is released (#2568)
- [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`

View File

@ -262,6 +262,11 @@ This is the new name, introduced in GLFW 3.4. The older
`GLFW_COCOA_RETINA_FRAMEBUFFER` name is also available for compatibility. Both `GLFW_COCOA_RETINA_FRAMEBUFFER` name is also available for compatibility. Both
names modify the same hint value. names modify the same hint value.
@anchor GLFW_THEME
__GLFW_THEME__ specifies whether the window should use light theme or not.
Possible values are `GLFW_THEME_AUTO`, `GLFW_THEME_DARK`, and `GLFW_THEME_LIGHT`. This hint only has effect
on windows operating system.
@anchor GLFW_MOUSE_PASSTHROUGH_hint @anchor GLFW_MOUSE_PASSTHROUGH_hint
__GLFW_MOUSE_PASSTHROUGH__ specifies whether the window is transparent to mouse __GLFW_MOUSE_PASSTHROUGH__ specifies whether the window is transparent to mouse
input, letting any mouse events pass through to whatever window is behind it. input, letting any mouse events pass through to whatever window is behind it.

View File

@ -1108,6 +1108,11 @@ extern "C" {
* [GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint) window hint for * [GLFW_SCALE_FRAMEBUFFER](@ref GLFW_SCALE_FRAMEBUFFER_hint) window hint for
* compatibility with earlier versions. * compatibility with earlier versions.
*/ */
#define GLFW_THEME 0x0002200E
/*! @brief windows specific
*
* Allows specifying whether light or dark theme should be used.
*/
#define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 #define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001
/*! @brief macOS specific /*! @brief macOS specific
* [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint).
@ -1182,6 +1187,10 @@ extern "C" {
#define GLFW_ANY_POSITION 0x80000000 #define GLFW_ANY_POSITION 0x80000000
#define GLFW_THEME_AUTO 0x00000000
#define GLFW_THEME_DARK 0x00000001
#define GLFW_THEME_LIGHT 0x00000002
/*! @defgroup shapes Standard cursor shapes /*! @defgroup shapes Standard cursor shapes
* @brief Standard system cursor shapes. * @brief Standard system cursor shapes.
* *
@ -3480,6 +3489,8 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos);
*/ */
GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos);
GLFWAPI int glfwIsWindowLightTheme(GLFWwindow* window);
/*! @brief Retrieves the size of the content area of the specified window. /*! @brief Retrieves the size of the content area of the specified window.
* *
* This function retrieves the size, in screen coordinates, of the content area * This function retrieves the size, in screen coordinates, of the content area

107
premake5.lua Normal file
View File

@ -0,0 +1,107 @@
project "GLFW"
kind "StaticLib"
language "C"
targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")
files
{
"include/GLFW/glfw3.h",
"include/GLFW/glfw3native.h",
"src/glfw_config.h",
"src/context.c",
"src/init.c",
"src/input.c",
"src/monitor.c",
"src/null_init.c",
"src/null_joystick.c",
"src/null_monitor.c",
"src/null_window.c",
"src/platform.c",
"src/vulkan.c",
"src/window.c"
}
filter "system:linux"
pic "On"
systemversion "latest"
staticruntime "On"
files
{
"src/x11_init.c",
"src/x11_monitor.c",
"src/x11_window.c",
"src/xkb_unicode.c",
"src/posix_module.c",
"src/posix_time.c",
"src/posix_thread.c",
"src/posix_module.c",
"src/posix_poll.c",
"src/glx_context.c",
"src/egl_context.c",
"src/osmesa_context.c",
"src/linux_joystick.c"
}
defines
{
"_GLFW_X11"
}
filter "system:macosx"
pic "On"
files
{
"src/cocoa_init.m",
"src/cocoa_monitor.m",
"src/cocoa_window.m",
"src/cocoa_joystick.m",
"src/cocoa_time.c",
"src/nsgl_context.m",
"src/posix_thread.c",
"src/posix_module.c",
"src/osmesa_context.c",
"src/egl_context.c"
}
defines
{
"_GLFW_COCOA"
}
filter "system:windows"
systemversion "latest"
staticruntime "On"
files
{
"src/win32_init.c",
"src/win32_joystick.c",
"src/win32_module.c",
"src/win32_monitor.c",
"src/win32_time.c",
"src/win32_thread.c",
"src/win32_window.c",
"src/wgl_context.c",
"src/egl_context.c",
"src/osmesa_context.c"
}
defines
{
"_GLFW_WIN32",
"_CRT_SECURE_NO_WARNINGS"
}
filter "configurations:Debug"
runtime "Debug"
symbols "on"
filter "configurations:Release"
runtime "Release"
optimize "on"

View File

@ -415,6 +415,7 @@ struct _GLFWwndconfig
GLFWbool mousePassthrough; GLFWbool mousePassthrough;
GLFWbool scaleToMonitor; GLFWbool scaleToMonitor;
GLFWbool scaleFramebuffer; GLFWbool scaleFramebuffer;
int theme;
struct { struct {
char frameName[256]; char frameName[256];
} ns; } ns;
@ -539,6 +540,7 @@ struct _GLFWwindow
GLFWbool focusOnShow; GLFWbool focusOnShow;
GLFWbool mousePassthrough; GLFWbool mousePassthrough;
GLFWbool shouldClose; GLFWbool shouldClose;
GLFWbool isLightTheme;
void* userPointer; void* userPointer;
GLFWbool doublebuffer; GLFWbool doublebuffer;
GLFWvidmode videoMode; GLFWvidmode videoMode;

View File

@ -145,6 +145,8 @@ static GLFWbool loadLibraries(void)
_glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow");
_glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor)
_glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); _glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor");
_glfw.win32.dwmapi.DwmSetWindowAttribute = (PFN_DwmSetWindowAttribute)
_glfwPlatformGetModuleSymbol(_glfw.win32.dwmapi.instance, "DwmSetWindowAttribute");
} }
_glfw.win32.shcore.instance = _glfwPlatformLoadModule("shcore.dll"); _glfw.win32.shcore.instance = _glfwPlatformLoadModule("shcore.dll");
@ -563,6 +565,14 @@ void _glfwUpdateKeyNamesWin32(void)
} }
} }
void _glfwSetWindowTheme(BOOL dark, HWND hwnd)
{
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#endif
_glfw.win32.dwmapi.DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &dark, sizeof(dark));
}
// Replacement for IsWindowsVersionOrGreater, as we cannot rely on the // Replacement for IsWindowsVersionOrGreater, as we cannot rely on the
// application having a correct embedded manifest // application having a correct embedded manifest
// //

View File

@ -241,8 +241,9 @@ typedef int (WINAPI * PFN_GetSystemMetricsForDpi)(int,UINT);
// dwmapi.dll function pointer typedefs // dwmapi.dll function pointer typedefs
typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*);
typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID);
typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); typedef HRESULT (WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*);
typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*); typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*);
typedef HRESULT (WINAPI * PFN_DwmSetWindowAttribute)(HWND, DWORD, LPCVOID, DWORD);
#define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled
#define DwmFlush _glfw.win32.dwmapi.Flush #define DwmFlush _glfw.win32.dwmapi.Flush
#define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow #define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow
@ -430,6 +431,7 @@ typedef struct _GLFWlibraryWin32
PFN_DwmFlush Flush; PFN_DwmFlush Flush;
PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow;
PFN_DwmGetColorizationColor GetColorizationColor; PFN_DwmGetColorizationColor GetColorizationColor;
PFN_DwmSetWindowAttribute DwmSetWindowAttribute;
} dwmapi; } dwmapi;
struct { struct {
@ -476,6 +478,7 @@ BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp);
BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build); BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build);
void _glfwInputErrorWin32(int error, const char* description); void _glfwInputErrorWin32(int error, const char* description);
void _glfwUpdateKeyNamesWin32(void); void _glfwUpdateKeyNamesWin32(void);
void _glfwSetWindowTheme(BOOL dark, HWND hwnd);
void _glfwPollMonitorsWin32(void); void _glfwPollMonitorsWin32(void);
void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired);

View File

@ -1392,6 +1392,36 @@ static int createNativeWindow(_GLFWwindow* window,
_glfw.win32.instance, _glfw.win32.instance,
(LPVOID) wndconfig); (LPVOID) wndconfig);
BOOL should_use_light_mode;
if (wndconfig->theme == GLFW_THEME_AUTO)
{
BOOL success = FALSE;
HKEY hRootKey = HKEY_CURRENT_USER;
const wchar_t* lpSubKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
const wchar_t* lpValueName = L"AppsUseLightTheme";
DWORD result;
{
HKEY hKey = 0;
if (RegOpenKeyExW(hRootKey, lpSubKey, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD dwBufferSize = sizeof(DWORD);
DWORD dwData = 0;
if (RegQueryValueExW(hKey, lpValueName, 0, NULL, (LPBYTE)(&dwData), &dwBufferSize) == ERROR_SUCCESS)
{
result = dwData;
success = TRUE;
}
RegCloseKey(hKey);
}
}
should_use_light_mode = success && result != 0;
}
else
should_use_light_mode = wndconfig->theme == GLFW_THEME_LIGHT;
_glfwSetWindowTheme(!should_use_light_mode, window->win32.handle);
window->isLightTheme = should_use_light_mode ? GLFW_TRUE : GLFW_FALSE;
_glfw_free(wideTitle); _glfw_free(wideTitle);
if (!window->win32.handle) if (!window->win32.handle)

View File

@ -388,6 +388,8 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_SCALE_FRAMEBUFFER: case GLFW_SCALE_FRAMEBUFFER:
case GLFW_COCOA_RETINA_FRAMEBUFFER: case GLFW_COCOA_RETINA_FRAMEBUFFER:
_glfw.hints.window.scaleFramebuffer = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.scaleFramebuffer = value ? GLFW_TRUE : GLFW_FALSE;
case GLFW_THEME:
_glfw.hints.window.theme = value;
return; return;
case GLFW_CENTER_CURSOR: case GLFW_CENTER_CURSOR:
_glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE;
@ -607,6 +609,15 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos)
_glfw.platform.setWindowPos(window, xpos, ypos); _glfw.platform.setWindowPos(window, xpos, ypos);
} }
GLFWAPI int glfwIsWindowLightTheme(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*)handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(0);
return window->isLightTheme;
}
GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height)
{ {
if (width) if (width)

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 =
@ -1760,24 +1778,6 @@ static void keyboardHandleLeave(void* userData,
if (!window) if (!window)
return; return;
// Handle any key repeats up to this point. We don't poll as this should be infrequent.
uint64_t repeats;
if (read(_glfw.wl.keyRepeatTimerfd, &repeats, sizeof(repeats)) == 8)
{
if(_glfw.wl.keyboardFocus)
{
for (uint64_t i = 0; i < repeats; i++)
{
_glfwInputKey(_glfw.wl.keyboardFocus,
translateKey(_glfw.wl.keyRepeatScancode),
_glfw.wl.keyRepeatScancode,
GLFW_PRESS,
_glfw.wl.xkb.modifiers);
inputText(_glfw.wl.keyboardFocus, _glfw.wl.keyRepeatScancode);
}
}
}
struct itimerspec timer = {0}; struct itimerspec timer = {0};
timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
@ -1820,11 +1820,12 @@ static void keyboardHandleKey(void* userData,
timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000; timer.it_value.tv_sec = _glfw.wl.keyRepeatDelay / 1000;
timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000; timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000;
timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
} }
} else if (scancode == _glfw.wl.keyRepeatScancode) {
timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
} }
timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
_glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers);
if (action == GLFW_PRESS) if (action == GLFW_PRESS)
@ -2207,12 +2208,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);