diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4e4c72e0..c7b30df8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: CC: clang CFLAGS: -Werror steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install dependencies run: | sudo apt update @@ -39,7 +39,7 @@ jobs: CC: clang CFLAGS: -Werror steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install dependencies run: | sudo apt update @@ -62,7 +62,7 @@ jobs: CFLAGS: -Werror MACOSX_DEPLOYMENT_TARGET: 10.8 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Configure static library run: cmake -S . -B build-static @@ -80,7 +80,7 @@ jobs: env: CFLAGS: /WX steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Configure static library run: cmake -S . -B build-static -G "Visual Studio 17 2022" diff --git a/CMakeLists.txt b/CMakeLists.txt index dd256a0f..8b51ae96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) -string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} GLFW_STANDALONE) +string(COMPARE EQUAL "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}" GLFW_STANDALONE) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" ${GLFW_STANDALONE}) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 9303c2b5..8c91b741 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -203,6 +203,7 @@ video tutorials. - Aleksey Rybalkin - Mikko Rytkönen - Riku Salminen + - Yoshinori Sano - Brandon Schaefer - Sebastian Schuberth - Jan Schuerkamp diff --git a/README.md b/README.md index e9a2c79a..f3a1f350 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,7 @@ information on what to include when reporting a bug. - [X11] Bugfix: The OSMesa libray was not unloaded on termination - [X11] Bugfix: A malformed response during selection transfer could cause a segfault - [X11] Bugfix: Some calls would reset Xlib to the default error handler (#2108) + - [Wayland] Added improved fallback window decorations via libdecor (#1639,#1693) - [Wayland] Added dynamic loading of all Wayland libraries - [Wayland] Added support for key names via xkbcommon - [Wayland] Added support for file path drop events (#2040) @@ -383,6 +384,7 @@ information on what to include when reporting a bug. - [Wayland] Bugfix: `GLFW_DECORATED` was ignored when showing a window with XDG decorations - [Wayland] Bugfix: Connecting a mouse after `glfwInit` would segfault (#1450) + - [Wayland] Bugfix: Joysticks connected after `glfwInit` were not detected (#2198) - [POSIX] Removed use of deprecated function `gettimeofday` - [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled - [Linux] Bugfix: Joysticks without buttons were ignored (#2042,#2043) diff --git a/docs/compat.dox b/docs/compat.dox index 94372197..e9506c57 100644 --- a/docs/compat.dox +++ b/docs/compat.dox @@ -128,6 +128,14 @@ wayland-protocols 1.6, and mandatory at build time. If the running compositor does not support this protocol, the screensaver may start even for full screen windows. +GLFW uses the [libdecor library](https://gitlab.freedesktop.org/libdecor/libdecor) +for window decorations, where available. This in turn provides good quality +client-side decorations (drawn by the application) on desktop systems that do +not support server-side decorations (drawn by the window manager). On systems +that do not provide either libdecor or xdg-decoration, very basic window +decorations are provided. These do not include the window title or any caption +buttons. + GLFW uses the [xdg-decoration protocol](https://cgit.freedesktop.org/wayland/wayland-protocols/tree/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml) to request decorations to be drawn around its windows. This protocol is part diff --git a/docs/intro.dox b/docs/intro.dox index 79348323..36ec0ef5 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -144,6 +144,15 @@ the `VK_KHR_xlib_surface` extension. Possible values are `GLFW_TRUE` and `GLFW_FALSE`. This is ignored on other platforms. +@subsubsection init_hints_wayland Wayland specific init hints + +@anchor GLFW_WAYLAND_LIBDECOR_hint +__GLFW_WAYLAND_LIBDECOR__ specifies whether to use +[libdecor](https://gitlab.freedesktop.org/libdecor/libdecor) for window +decorations where available. Possible values are `GLFW_WAYLAND_PREFER_LIBDECOR` +and `GLFW_WAYLAND_DISABLE_LIBDECOR`. This is ignored on other platforms. + + @subsubsection init_hints_values Supported and default values Initialization hint | Default value | Supported values @@ -154,6 +163,7 @@ Initialization hint | Default value | Supported v @ref GLFW_COCOA_CHDIR_RESOURCES | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_COCOA_MENUBAR | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` @ref GLFW_X11_XCB_VULKAN_SURFACE | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +@ref GLFW_WAYLAND_LIBDECOR | `GLFW_WAYLAND_PREFER_LIBDECOR` | `GLFW_WAYLAND_PREFER_LIBDECOR` or `GLFW_WAYLAND_DISABLE_LIBDECOR` @subsection platform Runtime platform selection diff --git a/docs/news.dox b/docs/news.dox index b8854a0a..e16267b2 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -43,6 +43,16 @@ to whatever window is behind it. This can also be changed after window creation with the matching [window attribute](@ref GLFW_MOUSE_PASSTHROUGH_attrib). +@subsubsection wayland_libdecor_34 Wayland libdecor decorations + +GLFW now supports improved fallback window decorations via +[libdecor](https://gitlab.freedesktop.org/libdecor/libdecor). + +Support for libdecor can be toggled before GLFW is initialized with the +[GLFW_WAYLAND_LIBDECOR](@ref GLFW_WAYLAND_LIBDECOR_hint) init hint. It is +enabled by default. + + @subsubsection wayland_app_id_34 Wayland app_id specification GLFW now supports specifying the app_id for a Wayland window using the @@ -267,6 +277,9 @@ then GLFW will fail to initialize. - @ref GLFW_POSITION_X - @ref GLFW_POSITION_Y - @ref GLFW_ANY_POSITION + - @ref GLFW_WAYLAND_LIBDECOR + - @ref GLFW_WAYLAND_PREFER_LIBDECOR + - @ref GLFW_WAYLAND_DISABLE_LIBDECOR @section news_archive Release notes for earlier versions diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 85ae67dd..c0c9e760 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1165,6 +1165,9 @@ extern "C" { #define GLFW_ANGLE_PLATFORM_TYPE_VULKAN 0x00037007 #define GLFW_ANGLE_PLATFORM_TYPE_METAL 0x00037008 +#define GLFW_WAYLAND_PREFER_LIBDECOR 0x00038001 +#define GLFW_WAYLAND_DISABLE_LIBDECOR 0x00038002 + #define GLFW_ANY_POSITION 0x80000000 /*! @defgroup shapes Standard cursor shapes @@ -1361,6 +1364,11 @@ extern "C" { * X11 specific [init hint](@ref GLFW_X11_XCB_VULKAN_SURFACE_hint). */ #define GLFW_X11_XCB_VULKAN_SURFACE 0x00052001 +/*! @brief Wayland specific init hint. + * + * Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint). + */ +#define GLFW_WAYLAND_LIBDECOR 0x00053001 /*! @} */ /*! @addtogroup init diff --git a/src/init.c b/src/init.c index d07a492e..06dbb3f2 100644 --- a/src/init.c +++ b/src/init.c @@ -62,6 +62,9 @@ static _GLFWinitconfig _glfwInitHints = { GLFW_TRUE, // X11 XCB Vulkan surface }, + { + GLFW_WAYLAND_PREFER_LIBDECOR // Wayland libdecor mode + }, }; // The allocation function used when no custom allocator is set @@ -479,6 +482,9 @@ GLFWAPI void glfwInitHint(int hint, int value) case GLFW_X11_XCB_VULKAN_SURFACE: _glfwInitHints.x11.xcbVulkanSurface = value; return; + case GLFW_WAYLAND_LIBDECOR: + _glfwInitHints.wl.libdecorMode = value; + return; } _glfwInputError(GLFW_INVALID_ENUM, diff --git a/src/internal.h b/src/internal.h index 94b8c452..ee7143a4 100644 --- a/src/internal.h +++ b/src/internal.h @@ -379,6 +379,9 @@ struct _GLFWinitconfig struct { GLFWbool xcbVulkanSurface; } x11; + struct { + int libdecorMode; + } wl; }; // Window configuration diff --git a/src/null_init.c b/src/null_init.c index fd020207..911bfb79 100644 --- a/src/null_init.c +++ b/src/null_init.c @@ -30,6 +30,7 @@ #include "internal.h" #include +#include ////////////////////////////////////////////////////////////////////////// @@ -123,6 +124,138 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform) int _glfwInitNull(void) { + int scancode; + + memset(_glfw.null.keycodes, -1, sizeof(_glfw.null.keycodes)); + memset(_glfw.null.scancodes, -1, sizeof(_glfw.null.scancodes)); + + _glfw.null.keycodes[GLFW_NULL_SC_SPACE] = GLFW_KEY_SPACE; + _glfw.null.keycodes[GLFW_NULL_SC_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.null.keycodes[GLFW_NULL_SC_COMMA] = GLFW_KEY_COMMA; + _glfw.null.keycodes[GLFW_NULL_SC_MINUS] = GLFW_KEY_MINUS; + _glfw.null.keycodes[GLFW_NULL_SC_PERIOD] = GLFW_KEY_PERIOD; + _glfw.null.keycodes[GLFW_NULL_SC_SLASH] = GLFW_KEY_SLASH; + _glfw.null.keycodes[GLFW_NULL_SC_0] = GLFW_KEY_0; + _glfw.null.keycodes[GLFW_NULL_SC_1] = GLFW_KEY_1; + _glfw.null.keycodes[GLFW_NULL_SC_2] = GLFW_KEY_2; + _glfw.null.keycodes[GLFW_NULL_SC_3] = GLFW_KEY_3; + _glfw.null.keycodes[GLFW_NULL_SC_4] = GLFW_KEY_4; + _glfw.null.keycodes[GLFW_NULL_SC_5] = GLFW_KEY_5; + _glfw.null.keycodes[GLFW_NULL_SC_6] = GLFW_KEY_6; + _glfw.null.keycodes[GLFW_NULL_SC_7] = GLFW_KEY_7; + _glfw.null.keycodes[GLFW_NULL_SC_8] = GLFW_KEY_8; + _glfw.null.keycodes[GLFW_NULL_SC_9] = GLFW_KEY_9; + _glfw.null.keycodes[GLFW_NULL_SC_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.null.keycodes[GLFW_NULL_SC_EQUAL] = GLFW_KEY_EQUAL; + _glfw.null.keycodes[GLFW_NULL_SC_A] = GLFW_KEY_A; + _glfw.null.keycodes[GLFW_NULL_SC_B] = GLFW_KEY_B; + _glfw.null.keycodes[GLFW_NULL_SC_C] = GLFW_KEY_C; + _glfw.null.keycodes[GLFW_NULL_SC_D] = GLFW_KEY_D; + _glfw.null.keycodes[GLFW_NULL_SC_E] = GLFW_KEY_E; + _glfw.null.keycodes[GLFW_NULL_SC_F] = GLFW_KEY_F; + _glfw.null.keycodes[GLFW_NULL_SC_G] = GLFW_KEY_G; + _glfw.null.keycodes[GLFW_NULL_SC_H] = GLFW_KEY_H; + _glfw.null.keycodes[GLFW_NULL_SC_I] = GLFW_KEY_I; + _glfw.null.keycodes[GLFW_NULL_SC_J] = GLFW_KEY_J; + _glfw.null.keycodes[GLFW_NULL_SC_K] = GLFW_KEY_K; + _glfw.null.keycodes[GLFW_NULL_SC_L] = GLFW_KEY_L; + _glfw.null.keycodes[GLFW_NULL_SC_M] = GLFW_KEY_M; + _glfw.null.keycodes[GLFW_NULL_SC_N] = GLFW_KEY_N; + _glfw.null.keycodes[GLFW_NULL_SC_O] = GLFW_KEY_O; + _glfw.null.keycodes[GLFW_NULL_SC_P] = GLFW_KEY_P; + _glfw.null.keycodes[GLFW_NULL_SC_Q] = GLFW_KEY_Q; + _glfw.null.keycodes[GLFW_NULL_SC_R] = GLFW_KEY_R; + _glfw.null.keycodes[GLFW_NULL_SC_S] = GLFW_KEY_S; + _glfw.null.keycodes[GLFW_NULL_SC_T] = GLFW_KEY_T; + _glfw.null.keycodes[GLFW_NULL_SC_U] = GLFW_KEY_U; + _glfw.null.keycodes[GLFW_NULL_SC_V] = GLFW_KEY_V; + _glfw.null.keycodes[GLFW_NULL_SC_W] = GLFW_KEY_W; + _glfw.null.keycodes[GLFW_NULL_SC_X] = GLFW_KEY_X; + _glfw.null.keycodes[GLFW_NULL_SC_Y] = GLFW_KEY_Y; + _glfw.null.keycodes[GLFW_NULL_SC_Z] = GLFW_KEY_Z; + _glfw.null.keycodes[GLFW_NULL_SC_LEFT_BRACKET] = GLFW_KEY_LEFT_BRACKET; + _glfw.null.keycodes[GLFW_NULL_SC_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.null.keycodes[GLFW_NULL_SC_RIGHT_BRACKET] = GLFW_KEY_RIGHT_BRACKET; + _glfw.null.keycodes[GLFW_NULL_SC_GRAVE_ACCENT] = GLFW_KEY_GRAVE_ACCENT; + _glfw.null.keycodes[GLFW_NULL_SC_WORLD_1] = GLFW_KEY_WORLD_1; + _glfw.null.keycodes[GLFW_NULL_SC_WORLD_2] = GLFW_KEY_WORLD_2; + _glfw.null.keycodes[GLFW_NULL_SC_ESCAPE] = GLFW_KEY_ESCAPE; + _glfw.null.keycodes[GLFW_NULL_SC_ENTER] = GLFW_KEY_ENTER; + _glfw.null.keycodes[GLFW_NULL_SC_TAB] = GLFW_KEY_TAB; + _glfw.null.keycodes[GLFW_NULL_SC_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.null.keycodes[GLFW_NULL_SC_INSERT] = GLFW_KEY_INSERT; + _glfw.null.keycodes[GLFW_NULL_SC_DELETE] = GLFW_KEY_DELETE; + _glfw.null.keycodes[GLFW_NULL_SC_RIGHT] = GLFW_KEY_RIGHT; + _glfw.null.keycodes[GLFW_NULL_SC_LEFT] = GLFW_KEY_LEFT; + _glfw.null.keycodes[GLFW_NULL_SC_DOWN] = GLFW_KEY_DOWN; + _glfw.null.keycodes[GLFW_NULL_SC_UP] = GLFW_KEY_UP; + _glfw.null.keycodes[GLFW_NULL_SC_PAGE_UP] = GLFW_KEY_PAGE_UP; + _glfw.null.keycodes[GLFW_NULL_SC_PAGE_DOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.null.keycodes[GLFW_NULL_SC_HOME] = GLFW_KEY_HOME; + _glfw.null.keycodes[GLFW_NULL_SC_END] = GLFW_KEY_END; + _glfw.null.keycodes[GLFW_NULL_SC_CAPS_LOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.null.keycodes[GLFW_NULL_SC_SCROLL_LOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.null.keycodes[GLFW_NULL_SC_NUM_LOCK] = GLFW_KEY_NUM_LOCK; + _glfw.null.keycodes[GLFW_NULL_SC_PRINT_SCREEN] = GLFW_KEY_PRINT_SCREEN; + _glfw.null.keycodes[GLFW_NULL_SC_PAUSE] = GLFW_KEY_PAUSE; + _glfw.null.keycodes[GLFW_NULL_SC_F1] = GLFW_KEY_F1; + _glfw.null.keycodes[GLFW_NULL_SC_F2] = GLFW_KEY_F2; + _glfw.null.keycodes[GLFW_NULL_SC_F3] = GLFW_KEY_F3; + _glfw.null.keycodes[GLFW_NULL_SC_F4] = GLFW_KEY_F4; + _glfw.null.keycodes[GLFW_NULL_SC_F5] = GLFW_KEY_F5; + _glfw.null.keycodes[GLFW_NULL_SC_F6] = GLFW_KEY_F6; + _glfw.null.keycodes[GLFW_NULL_SC_F7] = GLFW_KEY_F7; + _glfw.null.keycodes[GLFW_NULL_SC_F8] = GLFW_KEY_F8; + _glfw.null.keycodes[GLFW_NULL_SC_F9] = GLFW_KEY_F9; + _glfw.null.keycodes[GLFW_NULL_SC_F10] = GLFW_KEY_F10; + _glfw.null.keycodes[GLFW_NULL_SC_F11] = GLFW_KEY_F11; + _glfw.null.keycodes[GLFW_NULL_SC_F12] = GLFW_KEY_F12; + _glfw.null.keycodes[GLFW_NULL_SC_F13] = GLFW_KEY_F13; + _glfw.null.keycodes[GLFW_NULL_SC_F14] = GLFW_KEY_F14; + _glfw.null.keycodes[GLFW_NULL_SC_F15] = GLFW_KEY_F15; + _glfw.null.keycodes[GLFW_NULL_SC_F16] = GLFW_KEY_F16; + _glfw.null.keycodes[GLFW_NULL_SC_F17] = GLFW_KEY_F17; + _glfw.null.keycodes[GLFW_NULL_SC_F18] = GLFW_KEY_F18; + _glfw.null.keycodes[GLFW_NULL_SC_F19] = GLFW_KEY_F19; + _glfw.null.keycodes[GLFW_NULL_SC_F20] = GLFW_KEY_F20; + _glfw.null.keycodes[GLFW_NULL_SC_F21] = GLFW_KEY_F21; + _glfw.null.keycodes[GLFW_NULL_SC_F22] = GLFW_KEY_F22; + _glfw.null.keycodes[GLFW_NULL_SC_F23] = GLFW_KEY_F23; + _glfw.null.keycodes[GLFW_NULL_SC_F24] = GLFW_KEY_F24; + _glfw.null.keycodes[GLFW_NULL_SC_F25] = GLFW_KEY_F25; + _glfw.null.keycodes[GLFW_NULL_SC_KP_0] = GLFW_KEY_KP_0; + _glfw.null.keycodes[GLFW_NULL_SC_KP_1] = GLFW_KEY_KP_1; + _glfw.null.keycodes[GLFW_NULL_SC_KP_2] = GLFW_KEY_KP_2; + _glfw.null.keycodes[GLFW_NULL_SC_KP_3] = GLFW_KEY_KP_3; + _glfw.null.keycodes[GLFW_NULL_SC_KP_4] = GLFW_KEY_KP_4; + _glfw.null.keycodes[GLFW_NULL_SC_KP_5] = GLFW_KEY_KP_5; + _glfw.null.keycodes[GLFW_NULL_SC_KP_6] = GLFW_KEY_KP_6; + _glfw.null.keycodes[GLFW_NULL_SC_KP_7] = GLFW_KEY_KP_7; + _glfw.null.keycodes[GLFW_NULL_SC_KP_8] = GLFW_KEY_KP_8; + _glfw.null.keycodes[GLFW_NULL_SC_KP_9] = GLFW_KEY_KP_9; + _glfw.null.keycodes[GLFW_NULL_SC_KP_DECIMAL] = GLFW_KEY_KP_DECIMAL; + _glfw.null.keycodes[GLFW_NULL_SC_KP_DIVIDE] = GLFW_KEY_KP_DIVIDE; + _glfw.null.keycodes[GLFW_NULL_SC_KP_MULTIPLY] = GLFW_KEY_KP_MULTIPLY; + _glfw.null.keycodes[GLFW_NULL_SC_KP_SUBTRACT] = GLFW_KEY_KP_SUBTRACT; + _glfw.null.keycodes[GLFW_NULL_SC_KP_ADD] = GLFW_KEY_KP_ADD; + _glfw.null.keycodes[GLFW_NULL_SC_KP_ENTER] = GLFW_KEY_KP_ENTER; + _glfw.null.keycodes[GLFW_NULL_SC_KP_EQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.null.keycodes[GLFW_NULL_SC_LEFT_SHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.null.keycodes[GLFW_NULL_SC_LEFT_CONTROL] = GLFW_KEY_LEFT_CONTROL; + _glfw.null.keycodes[GLFW_NULL_SC_LEFT_ALT] = GLFW_KEY_LEFT_ALT; + _glfw.null.keycodes[GLFW_NULL_SC_LEFT_SUPER] = GLFW_KEY_LEFT_SUPER; + _glfw.null.keycodes[GLFW_NULL_SC_RIGHT_SHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.null.keycodes[GLFW_NULL_SC_RIGHT_CONTROL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.null.keycodes[GLFW_NULL_SC_RIGHT_ALT] = GLFW_KEY_RIGHT_ALT; + _glfw.null.keycodes[GLFW_NULL_SC_RIGHT_SUPER] = GLFW_KEY_RIGHT_SUPER; + _glfw.null.keycodes[GLFW_NULL_SC_MENU] = GLFW_KEY_MENU; + + for (scancode = GLFW_NULL_SC_FIRST; scancode < GLFW_NULL_SC_LAST; scancode++) + { + if (_glfw.null.keycodes[scancode] > 0) + _glfw.null.scancodes[_glfw.null.keycodes[scancode]] = scancode; + } + _glfwPollMonitorsNull(); return GLFW_TRUE; } diff --git a/src/null_platform.h b/src/null_platform.h index 966d04b8..1bd980f3 100644 --- a/src/null_platform.h +++ b/src/null_platform.h @@ -33,6 +33,128 @@ #define GLFW_NULL_CURSOR_STATE #define GLFW_NULL_LIBRARY_CONTEXT_STATE +#define GLFW_NULL_SC_FIRST GLFW_NULL_SC_SPACE +#define GLFW_NULL_SC_SPACE 1 +#define GLFW_NULL_SC_APOSTROPHE 2 +#define GLFW_NULL_SC_COMMA 3 +#define GLFW_NULL_SC_MINUS 4 +#define GLFW_NULL_SC_PERIOD 5 +#define GLFW_NULL_SC_SLASH 6 +#define GLFW_NULL_SC_0 7 +#define GLFW_NULL_SC_1 8 +#define GLFW_NULL_SC_2 9 +#define GLFW_NULL_SC_3 10 +#define GLFW_NULL_SC_4 11 +#define GLFW_NULL_SC_5 12 +#define GLFW_NULL_SC_6 13 +#define GLFW_NULL_SC_7 14 +#define GLFW_NULL_SC_8 15 +#define GLFW_NULL_SC_9 16 +#define GLFW_NULL_SC_SEMICOLON 17 +#define GLFW_NULL_SC_EQUAL 18 +#define GLFW_NULL_SC_LEFT_BRACKET 19 +#define GLFW_NULL_SC_BACKSLASH 20 +#define GLFW_NULL_SC_RIGHT_BRACKET 21 +#define GLFW_NULL_SC_GRAVE_ACCENT 22 +#define GLFW_NULL_SC_WORLD_1 23 +#define GLFW_NULL_SC_WORLD_2 24 +#define GLFW_NULL_SC_ESCAPE 25 +#define GLFW_NULL_SC_ENTER 26 +#define GLFW_NULL_SC_TAB 27 +#define GLFW_NULL_SC_BACKSPACE 28 +#define GLFW_NULL_SC_INSERT 29 +#define GLFW_NULL_SC_DELETE 30 +#define GLFW_NULL_SC_RIGHT 31 +#define GLFW_NULL_SC_LEFT 32 +#define GLFW_NULL_SC_DOWN 33 +#define GLFW_NULL_SC_UP 34 +#define GLFW_NULL_SC_PAGE_UP 35 +#define GLFW_NULL_SC_PAGE_DOWN 36 +#define GLFW_NULL_SC_HOME 37 +#define GLFW_NULL_SC_END 38 +#define GLFW_NULL_SC_CAPS_LOCK 39 +#define GLFW_NULL_SC_SCROLL_LOCK 40 +#define GLFW_NULL_SC_NUM_LOCK 41 +#define GLFW_NULL_SC_PRINT_SCREEN 42 +#define GLFW_NULL_SC_PAUSE 43 +#define GLFW_NULL_SC_A 44 +#define GLFW_NULL_SC_B 45 +#define GLFW_NULL_SC_C 46 +#define GLFW_NULL_SC_D 47 +#define GLFW_NULL_SC_E 48 +#define GLFW_NULL_SC_F 49 +#define GLFW_NULL_SC_G 50 +#define GLFW_NULL_SC_H 51 +#define GLFW_NULL_SC_I 52 +#define GLFW_NULL_SC_J 53 +#define GLFW_NULL_SC_K 54 +#define GLFW_NULL_SC_L 55 +#define GLFW_NULL_SC_M 56 +#define GLFW_NULL_SC_N 57 +#define GLFW_NULL_SC_O 58 +#define GLFW_NULL_SC_P 59 +#define GLFW_NULL_SC_Q 60 +#define GLFW_NULL_SC_R 61 +#define GLFW_NULL_SC_S 62 +#define GLFW_NULL_SC_T 63 +#define GLFW_NULL_SC_U 64 +#define GLFW_NULL_SC_V 65 +#define GLFW_NULL_SC_W 66 +#define GLFW_NULL_SC_X 67 +#define GLFW_NULL_SC_Y 68 +#define GLFW_NULL_SC_Z 69 +#define GLFW_NULL_SC_F1 70 +#define GLFW_NULL_SC_F2 71 +#define GLFW_NULL_SC_F3 72 +#define GLFW_NULL_SC_F4 73 +#define GLFW_NULL_SC_F5 74 +#define GLFW_NULL_SC_F6 75 +#define GLFW_NULL_SC_F7 76 +#define GLFW_NULL_SC_F8 77 +#define GLFW_NULL_SC_F9 78 +#define GLFW_NULL_SC_F10 79 +#define GLFW_NULL_SC_F11 80 +#define GLFW_NULL_SC_F12 81 +#define GLFW_NULL_SC_F13 82 +#define GLFW_NULL_SC_F14 83 +#define GLFW_NULL_SC_F15 84 +#define GLFW_NULL_SC_F16 85 +#define GLFW_NULL_SC_F17 86 +#define GLFW_NULL_SC_F18 87 +#define GLFW_NULL_SC_F19 88 +#define GLFW_NULL_SC_F20 89 +#define GLFW_NULL_SC_F21 90 +#define GLFW_NULL_SC_F22 91 +#define GLFW_NULL_SC_F23 92 +#define GLFW_NULL_SC_F24 93 +#define GLFW_NULL_SC_F25 94 +#define GLFW_NULL_SC_KP_0 95 +#define GLFW_NULL_SC_KP_1 96 +#define GLFW_NULL_SC_KP_2 97 +#define GLFW_NULL_SC_KP_3 98 +#define GLFW_NULL_SC_KP_4 99 +#define GLFW_NULL_SC_KP_5 100 +#define GLFW_NULL_SC_KP_6 101 +#define GLFW_NULL_SC_KP_7 102 +#define GLFW_NULL_SC_KP_8 103 +#define GLFW_NULL_SC_KP_9 104 +#define GLFW_NULL_SC_KP_DECIMAL 105 +#define GLFW_NULL_SC_KP_DIVIDE 106 +#define GLFW_NULL_SC_KP_MULTIPLY 107 +#define GLFW_NULL_SC_KP_SUBTRACT 108 +#define GLFW_NULL_SC_KP_ADD 109 +#define GLFW_NULL_SC_KP_ENTER 110 +#define GLFW_NULL_SC_KP_EQUAL 111 +#define GLFW_NULL_SC_LEFT_SHIFT 112 +#define GLFW_NULL_SC_LEFT_CONTROL 113 +#define GLFW_NULL_SC_LEFT_ALT 114 +#define GLFW_NULL_SC_LEFT_SUPER 115 +#define GLFW_NULL_SC_RIGHT_SHIFT 116 +#define GLFW_NULL_SC_RIGHT_CONTROL 117 +#define GLFW_NULL_SC_RIGHT_ALT 118 +#define GLFW_NULL_SC_RIGHT_SUPER 119 +#define GLFW_NULL_SC_MENU 120 +#define GLFW_NULL_SC_LAST GLFW_NULL_SC_MENU // Null-specific per-window data // @@ -68,6 +190,8 @@ typedef struct _GLFWlibraryNull int ycursor; char* clipboardString; _GLFWwindow* focusedWindow; + uint16_t keycodes[GLFW_NULL_SC_LAST + 1]; + uint8_t scancodes[GLFW_KEY_LAST + 1]; } _GLFWlibraryNull; void _glfwPollMonitorsNull(void); diff --git a/src/null_window.c b/src/null_window.c index d5d21758..830e8692 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -580,7 +580,7 @@ EGLNativeWindowType _glfwGetEGLNativeWindowNull(_GLFWwindow* window) const char* _glfwGetScancodeNameNull(int scancode) { - if (scancode < GLFW_KEY_SPACE || scancode > GLFW_KEY_LAST) + if (scancode < GLFW_NULL_SC_FIRST || scancode > GLFW_NULL_SC_LAST) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; @@ -588,117 +588,117 @@ const char* _glfwGetScancodeNameNull(int scancode) switch (scancode) { - case GLFW_KEY_APOSTROPHE: + case GLFW_NULL_SC_APOSTROPHE: return "'"; - case GLFW_KEY_COMMA: + case GLFW_NULL_SC_COMMA: return ","; - case GLFW_KEY_MINUS: - case GLFW_KEY_KP_SUBTRACT: + case GLFW_NULL_SC_MINUS: + case GLFW_NULL_SC_KP_SUBTRACT: return "-"; - case GLFW_KEY_PERIOD: - case GLFW_KEY_KP_DECIMAL: + case GLFW_NULL_SC_PERIOD: + case GLFW_NULL_SC_KP_DECIMAL: return "."; - case GLFW_KEY_SLASH: - case GLFW_KEY_KP_DIVIDE: + case GLFW_NULL_SC_SLASH: + case GLFW_NULL_SC_KP_DIVIDE: return "/"; - case GLFW_KEY_SEMICOLON: + case GLFW_NULL_SC_SEMICOLON: return ";"; - case GLFW_KEY_EQUAL: - case GLFW_KEY_KP_EQUAL: + case GLFW_NULL_SC_EQUAL: + case GLFW_NULL_SC_KP_EQUAL: return "="; - case GLFW_KEY_LEFT_BRACKET: + case GLFW_NULL_SC_LEFT_BRACKET: return "["; - case GLFW_KEY_RIGHT_BRACKET: + case GLFW_NULL_SC_RIGHT_BRACKET: return "]"; - case GLFW_KEY_KP_MULTIPLY: + case GLFW_NULL_SC_KP_MULTIPLY: return "*"; - case GLFW_KEY_KP_ADD: + case GLFW_NULL_SC_KP_ADD: return "+"; - case GLFW_KEY_BACKSLASH: - case GLFW_KEY_WORLD_1: - case GLFW_KEY_WORLD_2: + case GLFW_NULL_SC_BACKSLASH: + case GLFW_NULL_SC_WORLD_1: + case GLFW_NULL_SC_WORLD_2: return "\\"; - case GLFW_KEY_0: - case GLFW_KEY_KP_0: + case GLFW_NULL_SC_0: + case GLFW_NULL_SC_KP_0: return "0"; - case GLFW_KEY_1: - case GLFW_KEY_KP_1: + case GLFW_NULL_SC_1: + case GLFW_NULL_SC_KP_1: return "1"; - case GLFW_KEY_2: - case GLFW_KEY_KP_2: + case GLFW_NULL_SC_2: + case GLFW_NULL_SC_KP_2: return "2"; - case GLFW_KEY_3: - case GLFW_KEY_KP_3: + case GLFW_NULL_SC_3: + case GLFW_NULL_SC_KP_3: return "3"; - case GLFW_KEY_4: - case GLFW_KEY_KP_4: + case GLFW_NULL_SC_4: + case GLFW_NULL_SC_KP_4: return "4"; - case GLFW_KEY_5: - case GLFW_KEY_KP_5: + case GLFW_NULL_SC_5: + case GLFW_NULL_SC_KP_5: return "5"; - case GLFW_KEY_6: - case GLFW_KEY_KP_6: + case GLFW_NULL_SC_6: + case GLFW_NULL_SC_KP_6: return "6"; - case GLFW_KEY_7: - case GLFW_KEY_KP_7: + case GLFW_NULL_SC_7: + case GLFW_NULL_SC_KP_7: return "7"; - case GLFW_KEY_8: - case GLFW_KEY_KP_8: + case GLFW_NULL_SC_8: + case GLFW_NULL_SC_KP_8: return "8"; - case GLFW_KEY_9: - case GLFW_KEY_KP_9: + case GLFW_NULL_SC_9: + case GLFW_NULL_SC_KP_9: return "9"; - case GLFW_KEY_A: + case GLFW_NULL_SC_A: return "a"; - case GLFW_KEY_B: + case GLFW_NULL_SC_B: return "b"; - case GLFW_KEY_C: + case GLFW_NULL_SC_C: return "c"; - case GLFW_KEY_D: + case GLFW_NULL_SC_D: return "d"; - case GLFW_KEY_E: + case GLFW_NULL_SC_E: return "e"; - case GLFW_KEY_F: + case GLFW_NULL_SC_F: return "f"; - case GLFW_KEY_G: + case GLFW_NULL_SC_G: return "g"; - case GLFW_KEY_H: + case GLFW_NULL_SC_H: return "h"; - case GLFW_KEY_I: + case GLFW_NULL_SC_I: return "i"; - case GLFW_KEY_J: + case GLFW_NULL_SC_J: return "j"; - case GLFW_KEY_K: + case GLFW_NULL_SC_K: return "k"; - case GLFW_KEY_L: + case GLFW_NULL_SC_L: return "l"; - case GLFW_KEY_M: + case GLFW_NULL_SC_M: return "m"; - case GLFW_KEY_N: + case GLFW_NULL_SC_N: return "n"; - case GLFW_KEY_O: + case GLFW_NULL_SC_O: return "o"; - case GLFW_KEY_P: + case GLFW_NULL_SC_P: return "p"; - case GLFW_KEY_Q: + case GLFW_NULL_SC_Q: return "q"; - case GLFW_KEY_R: + case GLFW_NULL_SC_R: return "r"; - case GLFW_KEY_S: + case GLFW_NULL_SC_S: return "s"; - case GLFW_KEY_T: + case GLFW_NULL_SC_T: return "t"; - case GLFW_KEY_U: + case GLFW_NULL_SC_U: return "u"; - case GLFW_KEY_V: + case GLFW_NULL_SC_V: return "v"; - case GLFW_KEY_W: + case GLFW_NULL_SC_W: return "w"; - case GLFW_KEY_X: + case GLFW_NULL_SC_X: return "x"; - case GLFW_KEY_Y: + case GLFW_NULL_SC_Y: return "y"; - case GLFW_KEY_Z: + case GLFW_NULL_SC_Z: return "z"; } @@ -707,7 +707,7 @@ const char* _glfwGetScancodeNameNull(int scancode) int _glfwGetKeyScancodeNull(int key) { - return key; + return _glfw.null.scancodes[key]; } void _glfwGetRequiredInstanceExtensionsNull(char** extensions) diff --git a/src/win32_init.c b/src/win32_init.c index c9ae4b31..083bf94f 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -384,7 +384,7 @@ static GLFWbool createHelperWindow(void) if (!_glfw.win32.helperWindowClass) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, - "WIn32: Failed to register helper window class"); + "Win32: Failed to register helper window class"); return GLFW_FALSE; } diff --git a/src/wl_init.c b/src/wl_init.c index cb9c8966..26198b93 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -102,10 +102,9 @@ static void registryHandleGlobal(void* userData, { if (strcmp(interface, "wl_compositor") == 0) { - _glfw.wl.compositorVersion = _glfw_min(3, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, - _glfw.wl.compositorVersion); + _glfw_min(3, version)); } else if (strcmp(interface, "wl_subcompositor") == 0) { @@ -125,10 +124,9 @@ static void registryHandleGlobal(void* userData, { if (!_glfw.wl.seat) { - _glfw.wl.seatVersion = _glfw_min(4, version); _glfw.wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, - _glfw.wl.seatVersion); + _glfw_min(4, version)); _glfwAddSeatListenerWayland(_glfw.wl.seat); } } @@ -204,6 +202,20 @@ static const struct wl_registry_listener registryListener = registryHandleGlobalRemove }; +void libdecorHandleError(struct libdecor* context, + enum libdecor_error error, + const char* message) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: libdecor error %u: %s", + error, message); +} + +static const struct libdecor_interface libdecorInterface = +{ + libdecorHandleError +}; + // Create key code translation tables // static void createKeyTables(void) @@ -339,7 +351,7 @@ static void createKeyTables(void) static GLFWbool loadCursorTheme(void) { - int cursorSize = 32; + int cursorSize = 16; const char* sizeString = getenv("XCURSOR_SIZE"); if (sizeString) @@ -514,6 +526,8 @@ int _glfwInitWayland(void) _glfw.wl.keyRepeatTimerfd = -1; _glfw.wl.cursorTimerfd = -1; + _glfw.wl.tag = glfwGetVersionString(); + _glfw.wl.client.display_flush = (PFN_wl_display_flush) _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_flush"); _glfw.wl.client.display_cancel_read = (PFN_wl_display_cancel_read) @@ -544,6 +558,10 @@ int _glfwInitWayland(void) _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_get_user_data"); _glfw.wl.client.proxy_set_user_data = (PFN_wl_proxy_set_user_data) _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_set_user_data"); + _glfw.wl.client.proxy_get_tag = (PFN_wl_proxy_get_tag) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_get_tag"); + _glfw.wl.client.proxy_set_tag = (PFN_wl_proxy_set_tag) + _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_set_tag"); _glfw.wl.client.proxy_get_version = (PFN_wl_proxy_get_version) _glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_get_version"); _glfw.wl.client.proxy_marshal_flags = (PFN_wl_proxy_marshal_flags) @@ -563,7 +581,9 @@ int _glfwInitWayland(void) !_glfw.wl.client.proxy_marshal_constructor || !_glfw.wl.client.proxy_marshal_constructor_versioned || !_glfw.wl.client.proxy_get_user_data || - !_glfw.wl.client.proxy_set_user_data) + !_glfw.wl.client.proxy_set_user_data || + !_glfw.wl.client.proxy_get_tag || + !_glfw.wl.client.proxy_set_tag) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to load libwayland-client entry point"); @@ -651,6 +671,93 @@ int _glfwInitWayland(void) _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) _glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); + if (_glfw.hints.init.wl.libdecorMode == GLFW_WAYLAND_PREFER_LIBDECOR) + _glfw.wl.libdecor.handle = _glfwPlatformLoadModule("libdecor-0.so.0"); + + if (_glfw.wl.libdecor.handle) + { + _glfw.wl.libdecor.libdecor_new_ = (PFN_libdecor_new) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_new"); + _glfw.wl.libdecor.libdecor_unref_ = (PFN_libdecor_unref) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_unref"); + _glfw.wl.libdecor.libdecor_get_fd_ = (PFN_libdecor_get_fd) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_get_fd"); + _glfw.wl.libdecor.libdecor_dispatch_ = (PFN_libdecor_dispatch) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_dispatch"); + _glfw.wl.libdecor.libdecor_decorate_ = (PFN_libdecor_decorate) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_decorate"); + _glfw.wl.libdecor.libdecor_frame_unref_ = (PFN_libdecor_frame_unref) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_unref"); + _glfw.wl.libdecor.libdecor_frame_set_app_id_ = (PFN_libdecor_frame_set_app_id) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_app_id"); + _glfw.wl.libdecor.libdecor_frame_set_title_ = (PFN_libdecor_frame_set_title) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_title"); + _glfw.wl.libdecor.libdecor_frame_set_minimized_ = (PFN_libdecor_frame_set_minimized) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_minimized"); + _glfw.wl.libdecor.libdecor_frame_set_fullscreen_ = (PFN_libdecor_frame_set_fullscreen) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_fullscreen"); + _glfw.wl.libdecor.libdecor_frame_unset_fullscreen_ = (PFN_libdecor_frame_unset_fullscreen) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_unset_fullscreen"); + _glfw.wl.libdecor.libdecor_frame_map_ = (PFN_libdecor_frame_map) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_map"); + _glfw.wl.libdecor.libdecor_frame_commit_ = (PFN_libdecor_frame_commit) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_commit"); + _glfw.wl.libdecor.libdecor_frame_set_min_content_size_ = (PFN_libdecor_frame_set_min_content_size) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_min_content_size"); + _glfw.wl.libdecor.libdecor_frame_set_max_content_size_ = (PFN_libdecor_frame_set_max_content_size) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_max_content_size"); + _glfw.wl.libdecor.libdecor_frame_set_maximized_ = (PFN_libdecor_frame_set_maximized) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_maximized"); + _glfw.wl.libdecor.libdecor_frame_unset_maximized_ = (PFN_libdecor_frame_unset_maximized) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_unset_maximized"); + _glfw.wl.libdecor.libdecor_frame_set_capabilities_ = (PFN_libdecor_frame_set_capabilities) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_capabilities"); + _glfw.wl.libdecor.libdecor_frame_unset_capabilities_ = (PFN_libdecor_frame_unset_capabilities) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_unset_capabilities"); + _glfw.wl.libdecor.libdecor_frame_set_visibility_ = (PFN_libdecor_frame_set_visibility) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_set_visibility"); + _glfw.wl.libdecor.libdecor_frame_get_xdg_toplevel_ = (PFN_libdecor_frame_get_xdg_toplevel) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_frame_get_xdg_toplevel"); + _glfw.wl.libdecor.libdecor_configuration_get_content_size_ = (PFN_libdecor_configuration_get_content_size) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_configuration_get_content_size"); + _glfw.wl.libdecor.libdecor_configuration_get_window_state_ = (PFN_libdecor_configuration_get_window_state) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_configuration_get_window_state"); + _glfw.wl.libdecor.libdecor_state_new_ = (PFN_libdecor_state_new) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_state_new"); + _glfw.wl.libdecor.libdecor_state_free_ = (PFN_libdecor_state_free) + _glfwPlatformGetModuleSymbol(_glfw.wl.libdecor.handle, "libdecor_state_free"); + + if (!_glfw.wl.libdecor.libdecor_new_ || + !_glfw.wl.libdecor.libdecor_unref_ || + !_glfw.wl.libdecor.libdecor_get_fd_ || + !_glfw.wl.libdecor.libdecor_dispatch_ || + !_glfw.wl.libdecor.libdecor_decorate_ || + !_glfw.wl.libdecor.libdecor_frame_unref_ || + !_glfw.wl.libdecor.libdecor_frame_set_app_id_ || + !_glfw.wl.libdecor.libdecor_frame_set_title_ || + !_glfw.wl.libdecor.libdecor_frame_set_minimized_ || + !_glfw.wl.libdecor.libdecor_frame_set_fullscreen_ || + !_glfw.wl.libdecor.libdecor_frame_unset_fullscreen_ || + !_glfw.wl.libdecor.libdecor_frame_map_ || + !_glfw.wl.libdecor.libdecor_frame_commit_ || + !_glfw.wl.libdecor.libdecor_frame_set_min_content_size_ || + !_glfw.wl.libdecor.libdecor_frame_set_max_content_size_ || + !_glfw.wl.libdecor.libdecor_frame_set_maximized_ || + !_glfw.wl.libdecor.libdecor_frame_unset_maximized_ || + !_glfw.wl.libdecor.libdecor_frame_set_capabilities_ || + !_glfw.wl.libdecor.libdecor_frame_unset_capabilities_ || + !_glfw.wl.libdecor.libdecor_frame_set_visibility_ || + !_glfw.wl.libdecor.libdecor_frame_get_xdg_toplevel_ || + !_glfw.wl.libdecor.libdecor_configuration_get_content_size_ || + !_glfw.wl.libdecor.libdecor_configuration_get_window_state_ || + !_glfw.wl.libdecor.libdecor_state_new_ || + !_glfw.wl.libdecor.libdecor_state_free_) + { + _glfwPlatformFreeModule(_glfw.wl.libdecor.handle); + memset(&_glfw.wl.libdecor, 0, sizeof(_glfw.wl.libdecor)); + } + } + _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); @@ -670,8 +777,17 @@ int _glfwInitWayland(void) // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); + if (_glfw.wl.libdecor.handle) + { + _glfw.wl.libdecor.context = libdecor_new(_glfw.wl.display, &libdecorInterface); + + // Allow libdecor to receive its globals before proceeding + if (_glfw.wl.libdecor.context) + libdecor_dispatch(_glfw.wl.libdecor.context, 1); + } + #ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION - if (_glfw.wl.seatVersion >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) + if (wl_seat_get_version(_glfw.wl.seat) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { _glfw.wl.keyRepeatTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); @@ -711,6 +827,15 @@ void _glfwTerminateWayland(void) _glfwTerminateEGL(); _glfwTerminateOSMesa(); + if (_glfw.wl.libdecor.context) + libdecor_unref(_glfw.wl.libdecor.context); + + if (_glfw.wl.libdecor.handle) + { + _glfwPlatformFreeModule(_glfw.wl.libdecor.handle); + _glfw.wl.libdecor.handle = NULL; + } + if (_glfw.wl.egl.handle) { _glfwPlatformFreeModule(_glfw.wl.egl.handle); diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 3d4fcbb8..0b1a3d8d 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -114,14 +114,15 @@ static void outputHandleScale(void* userData, { struct _GLFWmonitor* monitor = userData; - monitor->wl.scale = factor; + monitor->wl.contentScale = factor; for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) { - for (int i = 0; i < window->wl.monitorsCount; i++) + for (int i = 0; i < window->wl.scaleCount; i++) { - if (window->wl.monitors[i] == monitor) + if (window->wl.scales[i].output == monitor->wl.output) { + window->wl.scales[i].factor = monitor->wl.contentScale; _glfwUpdateContentScaleWayland(window); break; } @@ -187,10 +188,11 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version) // The actual name of this output will be set in the geometry handler _GLFWmonitor* monitor = _glfwAllocMonitor("", 0, 0); - monitor->wl.scale = 1; + monitor->wl.contentScale = 1; monitor->wl.output = output; monitor->wl.name = name; + wl_proxy_set_tag((struct wl_proxy*) output, &_glfw.wl.tag); wl_output_add_listener(output, &outputListener, monitor); } @@ -217,9 +219,9 @@ void _glfwGetMonitorContentScaleWayland(_GLFWmonitor* monitor, float* xscale, float* yscale) { if (xscale) - *xscale = (float) monitor->wl.scale; + *xscale = (float) monitor->wl.contentScale; if (yscale) - *yscale = (float) monitor->wl.scale; + *yscale = (float) monitor->wl.contentScale; } void _glfwGetMonitorWorkareaWayland(_GLFWmonitor* monitor, diff --git a/src/wl_platform.h b/src/wl_platform.h index 302ade1d..2f97ff7b 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -28,6 +28,8 @@ #include #include +#include + typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; typedef struct VkWaylandSurfaceCreateInfoKHR @@ -61,6 +63,8 @@ typedef struct wl_proxy* (* PFN_wl_proxy_marshal_constructor)(struct wl_proxy*,u typedef struct wl_proxy* (* PFN_wl_proxy_marshal_constructor_versioned)(struct wl_proxy*,uint32_t,const struct wl_interface*,uint32_t,...); typedef void* (* PFN_wl_proxy_get_user_data)(struct wl_proxy*); typedef void (* PFN_wl_proxy_set_user_data)(struct wl_proxy*,void*); +typedef void (* PFN_wl_proxy_set_tag)(struct wl_proxy*,const char*const*); +typedef const char* const* (* PFN_wl_proxy_get_tag)(struct wl_proxy*); typedef uint32_t (* PFN_wl_proxy_get_version)(struct wl_proxy*); typedef struct wl_proxy* (* PFN_wl_proxy_marshal_flags)(struct wl_proxy*,uint32_t,const struct wl_interface*,uint32_t,uint32_t,...); #define wl_display_flush _glfw.wl.client.display_flush @@ -78,10 +82,13 @@ typedef struct wl_proxy* (* PFN_wl_proxy_marshal_flags)(struct wl_proxy*,uint32_ #define wl_proxy_marshal_constructor_versioned _glfw.wl.client.proxy_marshal_constructor_versioned #define wl_proxy_get_user_data _glfw.wl.client.proxy_get_user_data #define wl_proxy_set_user_data _glfw.wl.client.proxy_set_user_data +#define wl_proxy_get_tag _glfw.wl.client.proxy_get_tag +#define wl_proxy_set_tag _glfw.wl.client.proxy_set_tag #define wl_proxy_get_version _glfw.wl.client.proxy_get_version #define wl_proxy_marshal_flags _glfw.wl.client.proxy_marshal_flags struct wl_shm; +struct wl_output; #define wl_display_interface _glfw_wl_display_interface #define wl_subcompositor_interface _glfw_wl_subcompositor_interface @@ -198,13 +205,129 @@ typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_st #define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status #define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym +struct libdecor; +struct libdecor_frame; +struct libdecor_state; +struct libdecor_configuration; + +enum libdecor_error +{ + LIBDECOR_ERROR_COMPOSITOR_INCOMPATIBLE, + LIBDECOR_ERROR_INVALID_FRAME_CONFIGURATION, +}; + +enum libdecor_window_state +{ + LIBDECOR_WINDOW_STATE_NONE = 0, + LIBDECOR_WINDOW_STATE_ACTIVE = 1, + LIBDECOR_WINDOW_STATE_MAXIMIZED = 2, + LIBDECOR_WINDOW_STATE_FULLSCREEN = 4, + LIBDECOR_WINDOW_STATE_TILED_LEFT = 8, + LIBDECOR_WINDOW_STATE_TILED_RIGHT = 16, + LIBDECOR_WINDOW_STATE_TILED_TOP = 32, + LIBDECOR_WINDOW_STATE_TILED_BOTTOM = 64 +}; + +enum libdecor_capabilities +{ + LIBDECOR_ACTION_MOVE = 1, + LIBDECOR_ACTION_RESIZE = 2, + LIBDECOR_ACTION_MINIMIZE = 4, + LIBDECOR_ACTION_FULLSCREEN = 8, + LIBDECOR_ACTION_CLOSE = 16 +}; + +struct libdecor_interface +{ + void (* error)(struct libdecor*,enum libdecor_error,const char*); + void (* reserved0)(void); + void (* reserved1)(void); + void (* reserved2)(void); + void (* reserved3)(void); + void (* reserved4)(void); + void (* reserved5)(void); + void (* reserved6)(void); + void (* reserved7)(void); + void (* reserved8)(void); + void (* reserved9)(void); +}; + +struct libdecor_frame_interface +{ + void (* configure)(struct libdecor_frame*,struct libdecor_configuration*,void*); + void (* close)(struct libdecor_frame*,void*); + void (* commit)(struct libdecor_frame*,void*); + void (* dismiss_popup)(struct libdecor_frame*,const char*,void*); + void (* reserved0)(void); + void (* reserved1)(void); + void (* reserved2)(void); + void (* reserved3)(void); + void (* reserved4)(void); + void (* reserved5)(void); + void (* reserved6)(void); + void (* reserved7)(void); + void (* reserved8)(void); + void (* reserved9)(void); +}; + +typedef struct libdecor* (* PFN_libdecor_new)(struct wl_display*,const struct libdecor_interface*); +typedef void (* PFN_libdecor_unref)(struct libdecor*); +typedef int (* PFN_libdecor_get_fd)(struct libdecor*); +typedef int (* PFN_libdecor_dispatch)(struct libdecor*,int); +typedef struct libdecor_frame* (* PFN_libdecor_decorate)(struct libdecor*,struct wl_surface*,const struct libdecor_frame_interface*,void*); +typedef void (* PFN_libdecor_frame_unref)(struct libdecor_frame*); +typedef void (* PFN_libdecor_frame_set_app_id)(struct libdecor_frame*,const char*); +typedef void (* PFN_libdecor_frame_set_title)(struct libdecor_frame*,const char*); +typedef void (* PFN_libdecor_frame_set_minimized)(struct libdecor_frame*); +typedef void (* PFN_libdecor_frame_set_fullscreen)(struct libdecor_frame*,struct wl_output*); +typedef void (* PFN_libdecor_frame_unset_fullscreen)(struct libdecor_frame*); +typedef void (* PFN_libdecor_frame_map)(struct libdecor_frame*); +typedef void (* PFN_libdecor_frame_commit)(struct libdecor_frame*,struct libdecor_state*,struct libdecor_configuration*); +typedef void (* PFN_libdecor_frame_set_min_content_size)(struct libdecor_frame*,int,int); +typedef void (* PFN_libdecor_frame_set_max_content_size)(struct libdecor_frame*,int,int); +typedef void (* PFN_libdecor_frame_set_maximized)(struct libdecor_frame*); +typedef void (* PFN_libdecor_frame_unset_maximized)(struct libdecor_frame*); +typedef void (* PFN_libdecor_frame_set_capabilities)(struct libdecor_frame*,enum libdecor_capabilities); +typedef void (* PFN_libdecor_frame_unset_capabilities)(struct libdecor_frame*,enum libdecor_capabilities); +typedef void (* PFN_libdecor_frame_set_visibility)(struct libdecor_frame*,bool visible); +typedef struct xdg_toplevel* (* PFN_libdecor_frame_get_xdg_toplevel)(struct libdecor_frame*); +typedef bool (* PFN_libdecor_configuration_get_content_size)(struct libdecor_configuration*,struct libdecor_frame*,int*,int*); +typedef bool (* PFN_libdecor_configuration_get_window_state)(struct libdecor_configuration*,enum libdecor_window_state*); +typedef struct libdecor_state* (* PFN_libdecor_state_new)(int,int); +typedef void (* PFN_libdecor_state_free)(struct libdecor_state*); +#define libdecor_new _glfw.wl.libdecor.libdecor_new_ +#define libdecor_unref _glfw.wl.libdecor.libdecor_unref_ +#define libdecor_get_fd _glfw.wl.libdecor.libdecor_get_fd_ +#define libdecor_dispatch _glfw.wl.libdecor.libdecor_dispatch_ +#define libdecor_decorate _glfw.wl.libdecor.libdecor_decorate_ +#define libdecor_frame_unref _glfw.wl.libdecor.libdecor_frame_unref_ +#define libdecor_frame_set_app_id _glfw.wl.libdecor.libdecor_frame_set_app_id_ +#define libdecor_frame_set_title _glfw.wl.libdecor.libdecor_frame_set_title_ +#define libdecor_frame_set_minimized _glfw.wl.libdecor.libdecor_frame_set_minimized_ +#define libdecor_frame_set_fullscreen _glfw.wl.libdecor.libdecor_frame_set_fullscreen_ +#define libdecor_frame_unset_fullscreen _glfw.wl.libdecor.libdecor_frame_unset_fullscreen_ +#define libdecor_frame_map _glfw.wl.libdecor.libdecor_frame_map_ +#define libdecor_frame_commit _glfw.wl.libdecor.libdecor_frame_commit_ +#define libdecor_frame_set_min_content_size _glfw.wl.libdecor.libdecor_frame_set_min_content_size_ +#define libdecor_frame_set_max_content_size _glfw.wl.libdecor.libdecor_frame_set_max_content_size_ +#define libdecor_frame_set_maximized _glfw.wl.libdecor.libdecor_frame_set_maximized_ +#define libdecor_frame_unset_maximized _glfw.wl.libdecor.libdecor_frame_unset_maximized_ +#define libdecor_frame_set_capabilities _glfw.wl.libdecor.libdecor_frame_set_capabilities_ +#define libdecor_frame_unset_capabilities _glfw.wl.libdecor.libdecor_frame_unset_capabilities_ +#define libdecor_frame_set_visibility _glfw.wl.libdecor.libdecor_frame_set_visibility_ +#define libdecor_frame_get_xdg_toplevel _glfw.wl.libdecor.libdecor_frame_get_xdg_toplevel_ +#define libdecor_configuration_get_content_size _glfw.wl.libdecor.libdecor_configuration_get_content_size_ +#define libdecor_configuration_get_window_state _glfw.wl.libdecor.libdecor_configuration_get_window_state_ +#define libdecor_state_new _glfw.wl.libdecor.libdecor_state_new_ +#define libdecor_state_free _glfw.wl.libdecor.libdecor_state_free_ + typedef enum _GLFWdecorationSideWayland { - mainWindow, - topDecoration, - leftDecoration, - rightDecoration, - bottomDecoration, + GLFW_MAIN_WINDOW, + GLFW_TOP_DECORATION, + GLFW_LEFT_DECORATION, + GLFW_RIGHT_DECORATION, + GLFW_BOTTOM_DECORATION } _GLFWdecorationSideWayland; typedef struct _GLFWdecorationWayland @@ -221,6 +344,12 @@ typedef struct _GLFWofferWayland GLFWbool text_uri_list; } _GLFWofferWayland; +typedef struct _GLFWscaleWayland +{ + struct wl_output* output; + int factor; +} _GLFWscaleWayland; + // Wayland-specific per-window data // typedef struct _GLFWwindowWayland @@ -254,6 +383,11 @@ typedef struct _GLFWwindowWayland uint32_t decorationMode; } xdg; + struct { + struct libdecor_frame* frame; + int mode; + } libdecor; + _GLFWcursor* currentCursor; double cursorPosX, cursorPosY; @@ -262,10 +396,10 @@ typedef struct _GLFWwindowWayland // We need to track the monitors the window spans on to calculate the // optimal scaling factor. - int scale; - _GLFWmonitor** monitors; - int monitorsCount; - int monitorsSize; + int contentScale; + _GLFWscaleWayland* scales; + int scaleCount; + int scaleSize; struct zwp_relative_pointer_v1* relativePointer; struct zwp_locked_pointer_v1* lockedPointer; @@ -311,8 +445,7 @@ typedef struct _GLFWlibraryWayland _GLFWwindow* dragFocus; uint32_t dragSerial; - int compositorVersion; - int seatVersion; + const char* tag; struct wl_cursor_theme* cursorTheme; struct wl_cursor_theme* cursorThemeHiDPI; @@ -391,6 +524,8 @@ typedef struct _GLFWlibraryWayland PFN_wl_proxy_marshal_constructor_versioned proxy_marshal_constructor_versioned; PFN_wl_proxy_get_user_data proxy_get_user_data; PFN_wl_proxy_set_user_data proxy_set_user_data; + PFN_wl_proxy_get_tag proxy_get_tag; + PFN_wl_proxy_set_tag proxy_set_tag; PFN_wl_proxy_get_version proxy_get_version; PFN_wl_proxy_marshal_flags proxy_marshal_flags; } client; @@ -411,6 +546,36 @@ typedef struct _GLFWlibraryWayland PFN_wl_egl_window_destroy window_destroy; PFN_wl_egl_window_resize window_resize; } egl; + + struct { + void* handle; + struct libdecor* context; + PFN_libdecor_new libdecor_new_; + PFN_libdecor_unref libdecor_unref_; + PFN_libdecor_get_fd libdecor_get_fd_; + PFN_libdecor_dispatch libdecor_dispatch_; + PFN_libdecor_decorate libdecor_decorate_; + PFN_libdecor_frame_unref libdecor_frame_unref_; + PFN_libdecor_frame_set_app_id libdecor_frame_set_app_id_; + PFN_libdecor_frame_set_title libdecor_frame_set_title_; + PFN_libdecor_frame_set_minimized libdecor_frame_set_minimized_; + PFN_libdecor_frame_set_fullscreen libdecor_frame_set_fullscreen_; + PFN_libdecor_frame_unset_fullscreen libdecor_frame_unset_fullscreen_; + PFN_libdecor_frame_map libdecor_frame_map_; + PFN_libdecor_frame_commit libdecor_frame_commit_; + PFN_libdecor_frame_set_min_content_size libdecor_frame_set_min_content_size_; + PFN_libdecor_frame_set_max_content_size libdecor_frame_set_max_content_size_; + PFN_libdecor_frame_set_maximized libdecor_frame_set_maximized_; + PFN_libdecor_frame_unset_maximized libdecor_frame_unset_maximized_; + PFN_libdecor_frame_set_capabilities libdecor_frame_set_capabilities_; + PFN_libdecor_frame_unset_capabilities libdecor_frame_unset_capabilities_; + PFN_libdecor_frame_set_visibility libdecor_frame_set_visibility_; + PFN_libdecor_frame_get_xdg_toplevel libdecor_frame_get_xdg_toplevel_; + PFN_libdecor_configuration_get_content_size libdecor_configuration_get_content_size_; + PFN_libdecor_configuration_get_window_state libdecor_configuration_get_window_state_; + PFN_libdecor_state_new libdecor_state_new_; + PFN_libdecor_state_free libdecor_state_free_; + } libdecor; } _GLFWlibraryWayland; // Wayland-specific per-monitor data @@ -423,7 +588,7 @@ typedef struct _GLFWmonitorWayland int x; int y; - int scale; + int contentScale; } _GLFWmonitorWayland; // Wayland-specific per-cursor data diff --git a/src/wl_window.c b/src/wl_window.c index 339bff27..1dba366f 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -192,13 +192,16 @@ static struct wl_buffer* createShmBuffer(const GLFWimage* image) return buffer; } -static void createFallbackDecoration(_GLFWdecorationWayland* decoration, +static void createFallbackDecoration(_GLFWwindow* window, + _GLFWdecorationWayland* decoration, struct wl_surface* parent, struct wl_buffer* buffer, int x, int y, int width, int height) { decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor); + wl_surface_set_user_data(decoration->surface, window); + wl_proxy_set_tag((struct wl_proxy*) decoration->surface, &_glfw.wl.tag); decoration->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, decoration->surface, parent); @@ -228,19 +231,19 @@ static void createFallbackDecorations(_GLFWwindow* window) if (!window->wl.decorations.buffer) return; - createFallbackDecoration(&window->wl.decorations.top, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.top, window->wl.surface, window->wl.decorations.buffer, 0, -GLFW_CAPTION_HEIGHT, window->wl.width, GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.left, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.left, window->wl.surface, window->wl.decorations.buffer, -GLFW_BORDER_SIZE, -GLFW_CAPTION_HEIGHT, GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.right, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.right, window->wl.surface, window->wl.decorations.buffer, window->wl.width, -GLFW_CAPTION_HEIGHT, GLFW_BORDER_SIZE, window->wl.height + GLFW_CAPTION_HEIGHT); - createFallbackDecoration(&window->wl.decorations.bottom, window->wl.surface, + createFallbackDecoration(window, &window->wl.decorations.bottom, window->wl.surface, window->wl.decorations.buffer, -GLFW_BORDER_SIZE, window->wl.height, window->wl.width + GLFW_BORDER_SIZE * 2, GLFW_BORDER_SIZE); @@ -306,7 +309,7 @@ static void setContentAreaOpaque(_GLFWwindow* window) static void resizeWindow(_GLFWwindow* window) { - int scale = window->wl.scale; + int scale = window->wl.contentScale; int scaledWidth = window->wl.width * scale; int scaledHeight = window->wl.height * scale; @@ -342,22 +345,28 @@ static void resizeWindow(_GLFWwindow* window) void _glfwUpdateContentScaleWayland(_GLFWwindow* window) { - if (_glfw.wl.compositorVersion < WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) + if (wl_compositor_get_version(_glfw.wl.compositor) < + WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) + { return; + } // Get the scale factor from the highest scale monitor. int maxScale = 1; - for (int i = 0; i < window->wl.monitorsCount; i++) - maxScale = _glfw_max(window->wl.monitors[i]->wl.scale, maxScale); + for (int i = 0; i < window->wl.scaleCount; i++) + maxScale = _glfw_max(window->wl.scales[i].factor, maxScale); // Only change the framebuffer size if the scale changed. - if (window->wl.scale != maxScale) + if (window->wl.contentScale != maxScale) { - window->wl.scale = maxScale; + window->wl.contentScale = maxScale; wl_surface_set_buffer_scale(window->wl.surface, maxScale); _glfwInputWindowContentScale(window, maxScale, maxScale); resizeWindow(window); + + if (window->wl.visible) + _glfwInputWindowDamage(window); } } @@ -365,18 +374,25 @@ static void surfaceHandleEnter(void* userData, struct wl_surface* surface, struct wl_output* output) { + if (wl_proxy_get_tag((struct wl_proxy*) output) != &_glfw.wl.tag) + return; + _GLFWwindow* window = userData; _GLFWmonitor* monitor = wl_output_get_user_data(output); + if (!window || !monitor) + return; - if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) + if (window->wl.scaleCount + 1 > window->wl.scaleSize) { - ++window->wl.monitorsSize; - window->wl.monitors = - _glfw_realloc(window->wl.monitors, - window->wl.monitorsSize * sizeof(_GLFWmonitor*)); + window->wl.scaleSize++; + window->wl.scales = + _glfw_realloc(window->wl.scales, + window->wl.scaleSize * sizeof(_GLFWscaleWayland)); } - window->wl.monitors[window->wl.monitorsCount++] = monitor; + window->wl.scaleCount++; + window->wl.scales[window->wl.scaleCount - 1].factor = monitor->wl.contentScale; + window->wl.scales[window->wl.scaleCount - 1].output = output; _glfwUpdateContentScaleWayland(window); } @@ -385,18 +401,20 @@ static void surfaceHandleLeave(void* userData, struct wl_surface* surface, struct wl_output* output) { - _GLFWwindow* window = userData; - _GLFWmonitor* monitor = wl_output_get_user_data(output); - GLFWbool found = GLFW_FALSE; + if (wl_proxy_get_tag((struct wl_proxy*) output) != &_glfw.wl.tag) + return; - for (int i = 0; i < window->wl.monitorsCount - 1; ++i) + _GLFWwindow* window = userData; + + for (int i = 0; i < window->wl.scaleCount; i++) { - if (monitor == window->wl.monitors[i]) - found = GLFW_TRUE; - if (found) - window->wl.monitors[i] = window->wl.monitors[i + 1]; + if (window->wl.scales[i].output == output) + { + window->wl.scales[i] = window->wl.scales[window->wl.scaleCount - 1]; + window->wl.scaleCount--; + break; + } } - window->wl.monitors[--window->wl.monitorsCount] = NULL; _glfwUpdateContentScaleWayland(window); } @@ -429,7 +447,12 @@ static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) // static void acquireMonitor(_GLFWwindow* window) { - if (window->wl.xdg.toplevel) + if (window->wl.libdecor.frame) + { + libdecor_frame_set_fullscreen(window->wl.libdecor.frame, + window->monitor->wl.output); + } + else if (window->wl.xdg.toplevel) { xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, window->monitor->wl.output); @@ -445,12 +468,15 @@ static void acquireMonitor(_GLFWwindow* window) // static void releaseMonitor(_GLFWwindow* window) { - if (window->wl.xdg.toplevel) + if (window->wl.libdecor.frame) + libdecor_frame_unset_fullscreen(window->wl.libdecor.frame); + else if (window->wl.xdg.toplevel) xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); setIdleInhibitor(window, GLFW_FALSE); - if (window->wl.xdg.decorationMode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) + if (!window->wl.libdecor.frame && + window->wl.xdg.decorationMode != ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) { if (window->decorated) createFallbackDecorations(window); @@ -593,7 +619,190 @@ static const struct xdg_surface_listener xdgSurfaceListener = xdgSurfaceHandleConfigure }; -static GLFWbool createShellObjects(_GLFWwindow* window) +void libdecorFrameHandleConfigure(struct libdecor_frame* frame, + struct libdecor_configuration* config, + void* userData) +{ + _GLFWwindow* window = userData; + int width, height; + + enum libdecor_window_state windowState; + GLFWbool fullscreen, activated, maximized; + + if (libdecor_configuration_get_window_state(config, &windowState)) + { + fullscreen = (windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN) != 0; + activated = (windowState & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; + maximized = (windowState & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0; + } + else + { + fullscreen = window->wl.fullscreen; + activated = window->wl.activated; + maximized = window->wl.maximized; + } + + if (!libdecor_configuration_get_content_size(config, frame, &width, &height)) + { + width = window->wl.width; + height = window->wl.height; + } + + if (!maximized && !fullscreen) + { + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) + { + const float aspectRatio = (float) width / (float) height; + const float targetRatio = (float) window->numer / (float) window->denom; + if (aspectRatio < targetRatio) + height = width / targetRatio; + else if (aspectRatio > targetRatio) + width = height * targetRatio; + } + } + + struct libdecor_state* frameState = libdecor_state_new(width, height); + libdecor_frame_commit(frame, frameState, config); + libdecor_state_free(frameState); + + if (window->wl.activated != activated) + { + window->wl.activated = activated; + if (!window->wl.activated) + { + if (window->monitor && window->autoIconify) + libdecor_frame_set_minimized(window->wl.libdecor.frame); + } + } + + if (window->wl.maximized != maximized) + { + window->wl.maximized = maximized; + _glfwInputWindowMaximize(window, window->wl.maximized); + } + + window->wl.fullscreen = fullscreen; + + GLFWbool damaged = GLFW_FALSE; + + if (!window->wl.visible) + { + window->wl.visible = GLFW_TRUE; + damaged = GLFW_TRUE; + } + + if (width != window->wl.width || height != window->wl.height) + { + window->wl.width = width; + window->wl.height = height; + resizeWindow(window); + + _glfwInputWindowSize(window, width, height); + damaged = GLFW_TRUE; + } + + if (damaged) + _glfwInputWindowDamage(window); + else + wl_surface_commit(window->wl.surface); +} + +void libdecorFrameHandleClose(struct libdecor_frame* frame, void* userData) +{ + _GLFWwindow* window = userData; + _glfwInputWindowCloseRequest(window); +} + +void libdecorFrameHandleCommit(struct libdecor_frame* frame, void* userData) +{ + _GLFWwindow* window = userData; + wl_surface_commit(window->wl.surface); +} + +void libdecorFrameHandleDismissPopup(struct libdecor_frame* frame, + const char* seatName, + void* userData) +{ +} + +static const struct libdecor_frame_interface libdecorFrameInterface = +{ + libdecorFrameHandleConfigure, + libdecorFrameHandleClose, + libdecorFrameHandleCommit, + libdecorFrameHandleDismissPopup +}; + +static GLFWbool createLibdecorFrame(_GLFWwindow* window) +{ + window->wl.libdecor.frame = libdecor_decorate(_glfw.wl.libdecor.context, + window->wl.surface, + &libdecorFrameInterface, + window); + if (!window->wl.libdecor.frame) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create libdecor frame"); + return GLFW_FALSE; + } + + if (strlen(window->wl.appId)) + libdecor_frame_set_app_id(window->wl.libdecor.frame, window->wl.appId); + + if (strlen(window->wl.title)) + libdecor_frame_set_title(window->wl.libdecor.frame, window->wl.title); + + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + libdecor_frame_set_min_content_size(window->wl.libdecor.frame, + window->minwidth, + window->minheight); + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + libdecor_frame_set_max_content_size(window->wl.libdecor.frame, + window->maxwidth, + window->maxheight); + } + + if (!window->resizable) + { + libdecor_frame_unset_capabilities(window->wl.libdecor.frame, + LIBDECOR_ACTION_RESIZE); + } + + if (window->monitor) + { + // HACK: Allow libdecor to finish initialization of itself and its + // plugin so it will create the xdg_toplevel for the frame + // This needs to exist when setting the frame to fullscreen + while (!libdecor_frame_get_xdg_toplevel(window->wl.libdecor.frame)) + _glfwWaitEventsWayland(); + + libdecor_frame_set_fullscreen(window->wl.libdecor.frame, + window->monitor->wl.output); + setIdleInhibitor(window, GLFW_TRUE); + } + else + { + if (window->wl.maximized) + libdecor_frame_set_maximized(window->wl.libdecor.frame); + + if (!window->decorated) + libdecor_frame_set_visibility(window->wl.libdecor.frame, false); + + setIdleInhibitor(window, GLFW_FALSE); + } + + libdecor_frame_map(window->wl.libdecor.frame); + wl_display_roundtrip(_glfw.wl.display); + return GLFW_TRUE; +} + +static GLFWbool createXdgShellObjects(_GLFWwindow* window) { window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase, window->wl.surface); @@ -633,30 +842,30 @@ static GLFWbool createShellObjects(_GLFWwindow* window) xdg_toplevel_set_maximized(window->wl.xdg.toplevel); setIdleInhibitor(window, GLFW_FALSE); + } - if (_glfw.wl.decorationManager) - { - window->wl.xdg.decoration = - zxdg_decoration_manager_v1_get_toplevel_decoration( - _glfw.wl.decorationManager, window->wl.xdg.toplevel); - zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, - &xdgDecorationListener, - window); + if (_glfw.wl.decorationManager) + { + window->wl.xdg.decoration = + zxdg_decoration_manager_v1_get_toplevel_decoration( + _glfw.wl.decorationManager, window->wl.xdg.toplevel); + zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, + &xdgDecorationListener, + window); - uint32_t mode; + uint32_t mode; - if (window->decorated) - mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; - else - mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; - - zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); - } + if (window->decorated) + mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE; else - { - if (window->decorated) - createFallbackDecorations(window); - } + mode = ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; + + zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); + } + else + { + if (window->decorated && !window->monitor) + createFallbackDecorations(window); } if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) @@ -689,14 +898,27 @@ static GLFWbool createShellObjects(_GLFWwindow* window) wl_surface_commit(window->wl.surface); wl_display_roundtrip(_glfw.wl.display); - return GLFW_TRUE; } +static GLFWbool createShellObjects(_GLFWwindow* window) +{ + if (_glfw.wl.libdecor.context) + { + if (createLibdecorFrame(window)) + return GLFW_TRUE; + } + + return createXdgShellObjects(window); +} + static void destroyShellObjects(_GLFWwindow* window) { destroyFallbackDecorations(window); + if (window->wl.libdecor.frame) + libdecor_frame_unref(window->wl.libdecor.frame); + if (window->wl.xdg.decoration) zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); @@ -706,6 +928,7 @@ static void destroyShellObjects(_GLFWwindow* window) if (window->wl.xdg.surface) xdg_surface_destroy(window->wl.xdg.surface); + window->wl.libdecor.frame = NULL; window->wl.xdg.decoration = NULL; window->wl.xdg.decorationMode = 0; window->wl.xdg.toplevel = NULL; @@ -723,15 +946,14 @@ static GLFWbool createNativeSurface(_GLFWwindow* window, return GLFW_FALSE; } + wl_proxy_set_tag((struct wl_proxy*) window->wl.surface, &_glfw.wl.tag); wl_surface_add_listener(window->wl.surface, &surfaceListener, window); - wl_surface_set_user_data(window->wl.surface, window); - window->wl.width = wndconfig->width; window->wl.height = wndconfig->height; - window->wl.scale = 1; + window->wl.contentScale = 1; window->wl.title = _glfw_strdup(wndconfig->title); window->wl.appId = _glfw_strdup(wndconfig->wl.appId); @@ -758,7 +980,7 @@ static void setCursorImage(_GLFWwindow* window, buffer = cursorWayland->buffer; else { - if (window->wl.scale > 1 && cursorWayland->cursorHiDPI) + if (window->wl.contentScale > 1 && cursorWayland->cursorHiDPI) { wlCursor = cursorWayland->cursorHiDPI; scale = 2; @@ -794,7 +1016,7 @@ static void incrementCursorImage(_GLFWwindow* window) { _GLFWcursor* cursor; - if (!window || window->wl.decorations.focus != mainWindow) + if (!window || window->wl.decorations.focus != GLFW_MAIN_WINDOW) return; cursor = window->wl.currentCursor; @@ -873,14 +1095,23 @@ static void inputText(_GLFWwindow* window, uint32_t scancode) static void handleEvents(double* timeout) { +#if defined(GLFW_BUILD_LINUX_JOYSTICK) + if (_glfw.joysticksInitialized) + _glfwDetectJoystickConnectionLinux(); +#endif + GLFWbool event = GLFW_FALSE; - struct pollfd fds[] = + struct pollfd fds[4] = { { wl_display_get_fd(_glfw.wl.display), POLLIN }, { _glfw.wl.keyRepeatTimerfd, POLLIN }, { _glfw.wl.cursorTimerfd, POLLIN }, + { -1, POLLIN } }; + if (_glfw.wl.libdecor.context) + fds[3].fd = libdecor_get_fd(_glfw.wl.libdecor.context); + while (!event) { while (wl_display_prepare_read(_glfw.wl.display) != 0) @@ -902,7 +1133,7 @@ static void handleEvents(double* timeout) return; } - if (!_glfwPollPOSIX(fds, 3, timeout)) + if (!_glfwPollPOSIX(fds, sizeof(fds) / sizeof(fds[0]), timeout)) { wl_display_cancel_read(_glfw.wl.display); return; @@ -947,6 +1178,9 @@ static void handleEvents(double* timeout) event = GLFW_TRUE; } } + + if (fds[3].revents & POLLIN) + libdecor_dispatch(_glfw.wl.libdecor.context, 0); } } @@ -1014,40 +1248,6 @@ static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mime return string; } -static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, - _GLFWdecorationSideWayland* which) -{ - _GLFWdecorationSideWayland focus; - _GLFWwindow* window = _glfw.windowListHead; - if (!which) - which = &focus; - while (window) - { - if (surface == window->wl.decorations.top.surface) - { - *which = topDecoration; - break; - } - if (surface == window->wl.decorations.left.surface) - { - *which = leftDecoration; - break; - } - if (surface == window->wl.decorations.right.surface) - { - *which = rightDecoration; - break; - } - if (surface == window->wl.decorations.bottom.surface) - { - *which = bottomDecoration; - break; - } - window = window->next; - } - return window; -} - static void pointerHandleEnter(void* userData, struct wl_pointer* pointer, uint32_t serial, @@ -1059,16 +1259,22 @@ static void pointerHandleEnter(void* userData, if (!surface) return; - _GLFWdecorationSideWayland focus = mainWindow; - _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, &focus); - if (!window) - return; - } + if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) + return; + + _GLFWwindow* window = wl_surface_get_user_data(surface); + + if (surface == window->wl.decorations.top.surface) + window->wl.decorations.focus = GLFW_TOP_DECORATION; + else if (surface == window->wl.decorations.left.surface) + window->wl.decorations.focus = GLFW_LEFT_DECORATION; + else if (surface == window->wl.decorations.right.surface) + window->wl.decorations.focus = GLFW_RIGHT_DECORATION; + else if (surface == window->wl.decorations.bottom.surface) + window->wl.decorations.focus = GLFW_BOTTOM_DECORATION; + else + window->wl.decorations.focus = GLFW_MAIN_WINDOW; - window->wl.decorations.focus = focus; _glfw.wl.serial = serial; _glfw.wl.pointerEnterSerial = serial; _glfw.wl.pointerFocus = window; @@ -1084,8 +1290,13 @@ static void pointerHandleLeave(void* userData, uint32_t serial, struct wl_surface* surface) { - _GLFWwindow* window = _glfw.wl.pointerFocus; + if (!surface) + return; + if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) + return; + + _GLFWwindow* window = _glfw.wl.pointerFocus; if (!window) return; @@ -1106,7 +1317,7 @@ static void setCursor(_GLFWwindow* window, const char* name) struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; int scale = 1; - if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) + if (window->wl.contentScale > 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. @@ -1164,29 +1375,29 @@ static void pointerHandleMotion(void* userData, switch (window->wl.decorations.focus) { - case mainWindow: + case GLFW_MAIN_WINDOW: _glfw.wl.cursorPreviousName = NULL; _glfwInputCursorPos(window, x, y); return; - case topDecoration: + case GLFW_TOP_DECORATION: if (y < GLFW_BORDER_SIZE) cursorName = "n-resize"; else cursorName = "left_ptr"; break; - case leftDecoration: + case GLFW_LEFT_DECORATION: if (y < GLFW_BORDER_SIZE) cursorName = "nw-resize"; else cursorName = "w-resize"; break; - case rightDecoration: + case GLFW_RIGHT_DECORATION: if (y < GLFW_BORDER_SIZE) cursorName = "ne-resize"; else cursorName = "e-resize"; break; - case bottomDecoration: + case GLFW_BOTTOM_DECORATION: if (x < GLFW_BORDER_SIZE) cursorName = "sw-resize"; else if (x > window->wl.width + GLFW_BORDER_SIZE) @@ -1218,27 +1429,27 @@ static void pointerHandleButton(void* userData, { switch (window->wl.decorations.focus) { - case mainWindow: + case GLFW_MAIN_WINDOW: break; - case topDecoration: + case GLFW_TOP_DECORATION: 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); break; - case leftDecoration: + case GLFW_LEFT_DECORATION: if (window->wl.cursorPosY < GLFW_BORDER_SIZE) edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; else edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break; - case rightDecoration: + case GLFW_RIGHT_DECORATION: if (window->wl.cursorPosY < GLFW_BORDER_SIZE) edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; else edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; break; - case bottomDecoration: + case GLFW_BOTTOM_DECORATION: 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) @@ -1258,7 +1469,8 @@ static void pointerHandleButton(void* userData, } else if (button == BTN_RIGHT) { - if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) + if (window->wl.decorations.focus != GLFW_MAIN_WINDOW && + window->wl.xdg.toplevel) { xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, _glfw.wl.seat, serial, @@ -1269,7 +1481,7 @@ static void pointerHandleButton(void* userData, } // Don’t pass the button to the user if it was related to a decoration. - if (window->wl.decorations.focus != mainWindow) + if (window->wl.decorations.focus != GLFW_MAIN_WINDOW) return; _glfw.wl.serial = serial; @@ -1423,13 +1635,12 @@ static void keyboardHandleEnter(void* userData, if (!surface) return; + if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) + return; + _GLFWwindow* window = wl_surface_get_user_data(surface); - if (!window) - { - window = findWindowFromDecorationSurface(surface, NULL); - if (!window) - return; - } + if (surface != window->wl.surface) + return; _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = window; @@ -1677,9 +1888,12 @@ static void dataDeviceHandleEnter(void* userData, _GLFWwindow* window = NULL; if (surface) - window = wl_surface_get_user_data(surface); + { + if (wl_proxy_get_tag((struct wl_proxy*) surface) == &_glfw.wl.tag) + window = wl_surface_get_user_data(surface); + } - if (window && _glfw.wl.offers[i].text_uri_list) + if (surface == window->wl.surface && _glfw.wl.offers[i].text_uri_list) { _glfw.wl.dragOffer = offer; _glfw.wl.dragFocus = window; @@ -1692,6 +1906,9 @@ static void dataDeviceHandleEnter(void* userData, } } + if (wl_proxy_get_tag((struct wl_proxy*) surface) != &_glfw.wl.tag) + return; + if (_glfw.wl.dragOffer) wl_data_offer_accept(offer, serial, "text/uri-list"); else @@ -1890,7 +2107,7 @@ void _glfwDestroyWindowWayland(_GLFWwindow* window) _glfw_free(window->wl.title); _glfw_free(window->wl.appId); - _glfw_free(window->wl.monitors); + _glfw_free(window->wl.scales); } void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title) @@ -1899,7 +2116,9 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title) _glfw_free(window->wl.title); window->wl.title = copy; - if (window->wl.xdg.toplevel) + if (window->wl.libdecor.frame) + libdecor_frame_set_title(window->wl.libdecor.frame, title); + else if (window->wl.xdg.toplevel) xdg_toplevel_set_title(window->wl.xdg.toplevel, title); } @@ -1975,6 +2194,16 @@ void _glfwSetWindowSizeWayland(_GLFWwindow* window, int width, int height) window->wl.width = width; window->wl.height = height; resizeWindow(window); + + if (window->wl.libdecor.frame) + { + struct libdecor_state* frameState = libdecor_state_new(width, height); + libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL); + libdecor_state_free(frameState); + } + + if (window->wl.visible) + _glfwInputWindowDamage(window); } } @@ -1982,7 +2211,20 @@ void _glfwSetWindowSizeLimitsWayland(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { - if (window->wl.xdg.toplevel) + if (window->wl.libdecor.frame) + { + if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) + minwidth = minheight = 0; + + if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) + maxwidth = maxheight = 0; + + libdecor_frame_set_min_content_size(window->wl.libdecor.frame, + minwidth, minheight); + libdecor_frame_set_max_content_size(window->wl.libdecor.frame, + maxwidth, maxheight); + } + else if (window->wl.xdg.toplevel) { if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) minwidth = minheight = 0; @@ -2017,16 +2259,35 @@ void _glfwSetWindowAspectRatioWayland(_GLFWwindow* window, int numer, int denom) if (window->wl.maximized || window->wl.fullscreen) return; + int width = window->wl.width, height = window->wl.height; + if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) { - const float aspectRatio = (float) window->wl.width / (float) window->wl.height; + const float aspectRatio = (float) width / (float) height; const float targetRatio = (float) numer / (float) denom; if (aspectRatio < targetRatio) - window->wl.height = window->wl.width / targetRatio; + height /= targetRatio; else if (aspectRatio > targetRatio) - window->wl.width = window->wl.height * targetRatio; + width *= targetRatio; + } + if (width != window->wl.width || height != window->wl.height) + { + window->wl.width = width; + window->wl.height = height; resizeWindow(window); + + if (window->wl.libdecor.frame) + { + struct libdecor_state* frameState = libdecor_state_new(width, height); + libdecor_frame_commit(window->wl.libdecor.frame, frameState, NULL); + libdecor_state_free(frameState); + } + + _glfwInputWindowSize(window, width, height); + + if (window->wl.visible) + _glfwInputWindowDamage(window); } } @@ -2034,16 +2295,16 @@ void _glfwGetFramebufferSizeWayland(_GLFWwindow* window, int* width, int* height { _glfwGetWindowSizeWayland(window, width, height); if (width) - *width *= window->wl.scale; + *width *= window->wl.contentScale; if (height) - *height *= window->wl.scale; + *height *= window->wl.contentScale; } void _glfwGetWindowFrameSizeWayland(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { - if (window->decorated && !window->monitor && window->wl.decorations.top.surface) + if (window->wl.decorations.top.surface) { if (top) *top = GLFW_CAPTION_HEIGHT; @@ -2060,14 +2321,16 @@ void _glfwGetWindowContentScaleWayland(_GLFWwindow* window, float* xscale, float* yscale) { if (xscale) - *xscale = (float) window->wl.scale; + *xscale = (float) window->wl.contentScale; if (yscale) - *yscale = (float) window->wl.scale; + *yscale = (float) window->wl.contentScale; } void _glfwIconifyWindowWayland(_GLFWwindow* window) { - if (window->wl.xdg.toplevel) + if (window->wl.libdecor.frame) + libdecor_frame_set_minimized(window->wl.libdecor.frame); + else if (window->wl.xdg.toplevel) xdg_toplevel_set_minimized(window->wl.xdg.toplevel); } @@ -2084,7 +2347,9 @@ void _glfwRestoreWindowWayland(_GLFWwindow* window) if (window->wl.maximized) { - if (window->wl.xdg.toplevel) + if (window->wl.libdecor.frame) + libdecor_frame_unset_maximized(window->wl.libdecor.frame); + else if (window->wl.xdg.toplevel) xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); else window->wl.maximized = GLFW_FALSE; @@ -2094,7 +2359,9 @@ void _glfwRestoreWindowWayland(_GLFWwindow* window) void _glfwMaximizeWindowWayland(_GLFWwindow* window) { - if (window->wl.xdg.toplevel) + if (window->wl.libdecor.frame) + libdecor_frame_set_maximized(window->wl.libdecor.frame); + else if (window->wl.xdg.toplevel) xdg_toplevel_set_maximized(window->wl.xdg.toplevel); else window->wl.maximized = GLFW_TRUE; @@ -2102,7 +2369,7 @@ void _glfwMaximizeWindowWayland(_GLFWwindow* window) void _glfwShowWindowWayland(_GLFWwindow* window) { - if (!window->wl.xdg.toplevel) + if (!window->wl.libdecor.frame && !window->wl.xdg.toplevel) { // NOTE: The XDG surface and role are created here so command-line applications // with off-screen windows do not appear in for example the Unity dock @@ -2194,14 +2461,34 @@ GLFWbool _glfwFramebufferTransparentWayland(_GLFWwindow* window) void _glfwSetWindowResizableWayland(_GLFWwindow* window, GLFWbool enabled) { - // TODO - _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, - "Wayland: Window attribute setting not implemented yet"); + if (window->wl.libdecor.frame) + { + if (enabled) + { + libdecor_frame_set_capabilities(window->wl.libdecor.frame, + LIBDECOR_ACTION_RESIZE); + } + else + { + libdecor_frame_unset_capabilities(window->wl.libdecor.frame, + LIBDECOR_ACTION_RESIZE); + } + } + else + { + // TODO + _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED, + "Wayland: Window attribute setting not implemented yet"); + } } void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled) { - if (window->wl.xdg.decoration) + if (window->wl.libdecor.frame) + { + libdecor_frame_set_visibility(window->wl.libdecor.frame, enabled); + } + else if (window->wl.xdg.decoration) { uint32_t mode; @@ -2212,7 +2499,7 @@ void _glfwSetWindowDecoratedWayland(_GLFWwindow* window, GLFWbool enabled) zxdg_toplevel_decoration_v1_set_mode(window->wl.xdg.decoration, mode); } - else + else if (window->wl.xdg.toplevel) { if (enabled) createFallbackDecorations(window); @@ -2622,8 +2909,11 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* 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 != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) + if (window != _glfw.wl.pointerFocus || + window->wl.decorations.focus != GLFW_MAIN_WINDOW) + { return; + } // Update pointer lock to match cursor mode if (window->cursorMode == GLFW_CURSOR_DISABLED) diff --git a/tests/glfwinfo.c b/tests/glfwinfo.c index eda2f821..12acbccc 100644 --- a/tests/glfwinfo.c +++ b/tests/glfwinfo.c @@ -266,9 +266,10 @@ static void list_vulkan_instance_layers(void) for (uint32_t i = 0; i < lp_count; i++) { - printf(" %s (spec version %u) \"%s\"\n", + printf(" %s (spec version %u.%u) \"%s\"\n", lp[i].layerName, - lp[i].specVersion >> 22, + VK_VERSION_MAJOR(lp[i].specVersion), + VK_VERSION_MINOR(lp[i].specVersion), lp[i].description); } @@ -286,9 +287,10 @@ static void list_vulkan_device_layers(VkInstance instance, VkPhysicalDevice devi for (uint32_t i = 0; i < lp_count; i++) { - printf(" %s (spec version %u) \"%s\"\n", + printf(" %s (spec version %u.%u) \"%s\"\n", lp[i].layerName, - lp[i].specVersion >> 22, + VK_VERSION_MAJOR(lp[i].specVersion), + VK_VERSION_MINOR(lp[i].specVersion), lp[i].description); } @@ -710,190 +712,187 @@ int main(int argc, char** argv) glfwWindowHint(GLFW_COCOA_GRAPHICS_SWITCHING, cocoa_graphics_switching); GLFWwindow* window = glfwCreateWindow(200, 200, "Version", NULL, NULL); - if (!window) + if (window) { - glfwTerminate(); - exit(EXIT_FAILURE); - } + glfwMakeContextCurrent(window); + gladLoadGL(glfwGetProcAddress); - glfwMakeContextCurrent(window); - gladLoadGL(glfwGetProcAddress); + const GLenum error = glGetError(); + if (error != GL_NO_ERROR) + printf("*** OpenGL error after make current: 0x%08x ***\n", error); - const GLenum error = glGetError(); - if (error != GL_NO_ERROR) - printf("*** OpenGL error after make current: 0x%08x ***\n", error); + // Report client API version - // Report client API version + const int client = glfwGetWindowAttrib(window, GLFW_CLIENT_API); + const int major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); + const int minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); + const int revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); + const int profile = glfwGetWindowAttrib(window, GLFW_OPENGL_PROFILE); - const int client = glfwGetWindowAttrib(window, GLFW_CLIENT_API); - const int major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); - const int minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); - const int revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); - const int profile = glfwGetWindowAttrib(window, GLFW_OPENGL_PROFILE); - - printf("%s context version string: \"%s\"\n", - get_api_name(client), - glGetString(GL_VERSION)); - - printf("%s context version parsed by GLFW: %u.%u.%u\n", - get_api_name(client), - major, minor, revision); - - // Report client API context properties - - if (client == GLFW_OPENGL_API) - { - if (major >= 3) - { - GLint flags; - - glGetIntegerv(GL_CONTEXT_FLAGS, &flags); - printf("%s context flags (0x%08x):", get_api_name(client), flags); - - if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) - printf(" forward-compatible"); - if (flags & 2/*GL_CONTEXT_FLAG_DEBUG_BIT*/) - printf(" debug"); - if (flags & GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB) - printf(" robustness"); - if (flags & 8/*GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR*/) - printf(" no-error"); - putchar('\n'); - - printf("%s context flags parsed by GLFW:", get_api_name(client)); - - if (glfwGetWindowAttrib(window, GLFW_OPENGL_FORWARD_COMPAT)) - printf(" forward-compatible"); - if (glfwGetWindowAttrib(window, GLFW_CONTEXT_DEBUG)) - printf(" debug"); - if (glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS) == GLFW_LOSE_CONTEXT_ON_RESET) - printf(" robustness"); - if (glfwGetWindowAttrib(window, GLFW_CONTEXT_NO_ERROR)) - printf(" no-error"); - putchar('\n'); - } - - if (major >= 4 || (major == 3 && minor >= 2)) - { - GLint mask; - glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); - - printf("%s profile mask (0x%08x): %s\n", - get_api_name(client), - mask, - get_profile_name_gl(mask)); - - printf("%s profile mask parsed by GLFW: %s\n", - get_api_name(client), - get_profile_name_glfw(profile)); - } - - if (GLAD_GL_ARB_robustness) - { - const int robustness = glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS); - GLint strategy; - glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy); - - printf("%s robustness strategy (0x%08x): %s\n", - get_api_name(client), - strategy, - get_strategy_name_gl(strategy)); - - printf("%s robustness strategy parsed by GLFW: %s\n", - get_api_name(client), - get_strategy_name_glfw(robustness)); - } - } - - printf("%s context renderer string: \"%s\"\n", - get_api_name(client), - glGetString(GL_RENDERER)); - printf("%s context vendor string: \"%s\"\n", - get_api_name(client), - glGetString(GL_VENDOR)); - - if (major >= 2) - { - printf("%s context shading language version: \"%s\"\n", + printf("%s context version string: \"%s\"\n", get_api_name(client), - glGetString(GL_SHADING_LANGUAGE_VERSION)); + glGetString(GL_VERSION)); + + printf("%s context version parsed by GLFW: %u.%u.%u\n", + get_api_name(client), + major, minor, revision); + + // Report client API context properties + + if (client == GLFW_OPENGL_API) + { + if (major >= 3) + { + GLint flags; + + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + printf("%s context flags (0x%08x):", get_api_name(client), flags); + + if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) + printf(" forward-compatible"); + if (flags & 2/*GL_CONTEXT_FLAG_DEBUG_BIT*/) + printf(" debug"); + if (flags & GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB) + printf(" robustness"); + if (flags & 8/*GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR*/) + printf(" no-error"); + putchar('\n'); + + printf("%s context flags parsed by GLFW:", get_api_name(client)); + + if (glfwGetWindowAttrib(window, GLFW_OPENGL_FORWARD_COMPAT)) + printf(" forward-compatible"); + if (glfwGetWindowAttrib(window, GLFW_CONTEXT_DEBUG)) + printf(" debug"); + if (glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS) == GLFW_LOSE_CONTEXT_ON_RESET) + printf(" robustness"); + if (glfwGetWindowAttrib(window, GLFW_CONTEXT_NO_ERROR)) + printf(" no-error"); + putchar('\n'); + } + + if (major >= 4 || (major == 3 && minor >= 2)) + { + GLint mask; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + + printf("%s profile mask (0x%08x): %s\n", + get_api_name(client), + mask, + get_profile_name_gl(mask)); + + printf("%s profile mask parsed by GLFW: %s\n", + get_api_name(client), + get_profile_name_glfw(profile)); + } + + if (GLAD_GL_ARB_robustness) + { + const int robustness = glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS); + GLint strategy; + glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy); + + printf("%s robustness strategy (0x%08x): %s\n", + get_api_name(client), + strategy, + get_strategy_name_gl(strategy)); + + printf("%s robustness strategy parsed by GLFW: %s\n", + get_api_name(client), + get_strategy_name_glfw(robustness)); + } + } + + printf("%s context renderer string: \"%s\"\n", + get_api_name(client), + glGetString(GL_RENDERER)); + printf("%s context vendor string: \"%s\"\n", + get_api_name(client), + glGetString(GL_VENDOR)); + + if (major >= 2) + { + printf("%s context shading language version: \"%s\"\n", + get_api_name(client), + glGetString(GL_SHADING_LANGUAGE_VERSION)); + } + + printf("%s framebuffer:\n", get_api_name(client)); + + GLint redbits, greenbits, bluebits, alphabits, depthbits, stencilbits; + + if (client == GLFW_OPENGL_API && profile == GLFW_OPENGL_CORE_PROFILE) + { + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, + &redbits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, + &greenbits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, + &bluebits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + &alphabits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_DEPTH, + GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, + &depthbits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_STENCIL, + GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, + &stencilbits); + } + else + { + glGetIntegerv(GL_RED_BITS, &redbits); + glGetIntegerv(GL_GREEN_BITS, &greenbits); + glGetIntegerv(GL_BLUE_BITS, &bluebits); + glGetIntegerv(GL_ALPHA_BITS, &alphabits); + glGetIntegerv(GL_DEPTH_BITS, &depthbits); + glGetIntegerv(GL_STENCIL_BITS, &stencilbits); + } + + printf(" red: %u green: %u blue: %u alpha: %u depth: %u stencil: %u\n", + redbits, greenbits, bluebits, alphabits, depthbits, stencilbits); + + if (client == GLFW_OPENGL_ES_API || + GLAD_GL_ARB_multisample || + major > 1 || minor >= 3) + { + GLint samples, samplebuffers; + glGetIntegerv(GL_SAMPLES, &samples); + glGetIntegerv(GL_SAMPLE_BUFFERS, &samplebuffers); + + printf(" samples: %u sample buffers: %u\n", samples, samplebuffers); + } + + if (client == GLFW_OPENGL_API && profile != GLFW_OPENGL_CORE_PROFILE) + { + GLint accumredbits, accumgreenbits, accumbluebits, accumalphabits; + GLint auxbuffers; + + glGetIntegerv(GL_ACCUM_RED_BITS, &accumredbits); + glGetIntegerv(GL_ACCUM_GREEN_BITS, &accumgreenbits); + glGetIntegerv(GL_ACCUM_BLUE_BITS, &accumbluebits); + glGetIntegerv(GL_ACCUM_ALPHA_BITS, &accumalphabits); + glGetIntegerv(GL_AUX_BUFFERS, &auxbuffers); + + printf(" accum red: %u accum green: %u accum blue: %u accum alpha: %u aux buffers: %u\n", + accumredbits, accumgreenbits, accumbluebits, accumalphabits, auxbuffers); + } + + if (list_extensions) + list_context_extensions(client, major, minor); + + glfwDestroyWindow(window); } - printf("%s framebuffer:\n", get_api_name(client)); - - GLint redbits, greenbits, bluebits, alphabits, depthbits, stencilbits; - - if (client == GLFW_OPENGL_API && profile == GLFW_OPENGL_CORE_PROFILE) - { - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, - &redbits); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, - &greenbits); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, - &bluebits); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_BACK_LEFT, - GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, - &alphabits); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_DEPTH, - GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, - &depthbits); - glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, - GL_STENCIL, - GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, - &stencilbits); - } - else - { - glGetIntegerv(GL_RED_BITS, &redbits); - glGetIntegerv(GL_GREEN_BITS, &greenbits); - glGetIntegerv(GL_BLUE_BITS, &bluebits); - glGetIntegerv(GL_ALPHA_BITS, &alphabits); - glGetIntegerv(GL_DEPTH_BITS, &depthbits); - glGetIntegerv(GL_STENCIL_BITS, &stencilbits); - } - - printf(" red: %u green: %u blue: %u alpha: %u depth: %u stencil: %u\n", - redbits, greenbits, bluebits, alphabits, depthbits, stencilbits); - - if (client == GLFW_OPENGL_ES_API || - GLAD_GL_ARB_multisample || - major > 1 || minor >= 3) - { - GLint samples, samplebuffers; - glGetIntegerv(GL_SAMPLES, &samples); - glGetIntegerv(GL_SAMPLE_BUFFERS, &samplebuffers); - - printf(" samples: %u sample buffers: %u\n", samples, samplebuffers); - } - - if (client == GLFW_OPENGL_API && profile != GLFW_OPENGL_CORE_PROFILE) - { - GLint accumredbits, accumgreenbits, accumbluebits, accumalphabits; - GLint auxbuffers; - - glGetIntegerv(GL_ACCUM_RED_BITS, &accumredbits); - glGetIntegerv(GL_ACCUM_GREEN_BITS, &accumgreenbits); - glGetIntegerv(GL_ACCUM_BLUE_BITS, &accumbluebits); - glGetIntegerv(GL_ACCUM_ALPHA_BITS, &accumalphabits); - glGetIntegerv(GL_AUX_BUFFERS, &auxbuffers); - - printf(" accum red: %u accum green: %u accum blue: %u accum alpha: %u aux buffers: %u\n", - accumredbits, accumgreenbits, accumbluebits, accumalphabits, auxbuffers); - } - - if (list_extensions) - list_context_extensions(client, major, minor); - - glfwDestroyWindow(window); - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(200, 200, "Version", NULL, NULL);