Wayland: Add initial support for libdecor

This is partly based on the implementation of libdecor support in
PR #1693 by @ christianrauch.

Where available, the libdecor library is loaded at init and becomes the
preferred method for window decorations.  On compositors that support
XDG decorations, libdecor in turn uses those.  If not, libdecor has
a plug-in archtecture and may load additional libraries to either use
compositor-specific decorations or draw its own.

If necessary, support for libdecor can be disabled with the
GLFW_WAYLAND_LIBDECOR init hint.  This is mostly in case some part of
the dynamic loading or duplication of header material added here turns
out to cause problems with future versions of libdecor-0.so.0.

Fixes #1639
Closes #1693
Related to #1725

This was adapted to 3.3-stable from
fbdb53b9ca.
This commit is contained in:
Camilla Löwy 2022-12-27 14:37:27 +01:00
parent 1ad5df8032
commit 0c2db4a23e
10 changed files with 620 additions and 19 deletions

View File

@ -123,6 +123,7 @@ information on what to include when reporting a bug.
## Changelog
- [Wayland] Added improved fallback window decorations via libdecor (#1639,#1693)
- [Wayland] Bugfix: Connecting a mouse after `glfwInit` would segfault (#1450)
- [Wayland] Disabled alpha channel for opaque windows on systems lacking
`EGL_EXT_present_opaque` (#1895)

View File

@ -122,6 +122,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

View File

@ -102,6 +102,30 @@ a nib or manually, when the first window is created, which is when AppKit is
initialized. Set this with @ref glfwInitHint.
@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.
@note This init hint was added in 3.3.9 and is not present in earlier patch releases. It
is safe to attempt to set this hint on earlier versions of GLFW 3.3 but it will emit
a harmless @ref GLFW_INVALID_ENUM error. If you need to avoid causing any errors, you can
check the library version first with @ref glfwGetVersion.
@note To set this hint while also building against earlier versions of GLFW 3.3, you can
use the numerical constants directly.
@note @code
int minor, patch;
glfwGetVersion(NULL, &minor, &patch);
if (minor > 3 || (minor == 3 && patch >= 9))
glfwInitHint(0x00053001 /*GLFW_WAYLAND_LIBDECOR*/, 0x00038002 /*GLFW_WAYLAND_DISABLE_LIBDECOR*/);
@endcode
@subsubsection init_hints_values Supported and default values
Initialization hint | Default value | Supported values
@ -109,6 +133,7 @@ Initialization hint | Default value | Supported values
@ref GLFW_JOYSTICK_HAT_BUTTONS | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE`
@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_WAYLAND_LIBDECOR | `GLFW_WAYLAND_PREFER_LIBDECOR` | `GLFW_WAYLAND_PREFER_LIBDECOR` or `GLFW_WAYLAND_DISABLE_LIBDECOR`
@subsection intro_init_terminate Terminating GLFW

View File

@ -37,6 +37,16 @@ SDK](https://vulkan.lunarg.com/).
For more information see @ref vulkan_guide.
@subsubsection wayland_libdecor_33 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 content_scale_33 Content scale queries for DPI-aware rendering
GLFW now provides content scales for windows and monitors, i.e. the ratio
@ -443,6 +453,9 @@ Use Wayland or X11 instead.
- @ref GLFWwindowmaximizefun
- @ref GLFWwindowcontentscalefun
- @ref GLFWgamepadstate
- @ref GLFW_WAYLAND_LIBDECOR
- @ref GLFW_WAYLAND_PREFER_LIBDECOR
- @ref GLFW_WAYLAND_DISABLE_LIBDECOR
@subsubsection constants_33 New constants in version 3.3

View File

@ -1063,6 +1063,9 @@ extern "C" {
#define GLFW_EGL_CONTEXT_API 0x00036002
#define GLFW_OSMESA_CONTEXT_API 0x00036003
#define GLFW_WAYLAND_PREFER_LIBDECOR 0x00038001
#define GLFW_WAYLAND_DISABLE_LIBDECOR 0x00038002
/*! @defgroup shapes Standard cursor shapes
* @brief Standard system cursor shapes.
*
@ -1123,6 +1126,11 @@ extern "C" {
* macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint).
*/
#define GLFW_COCOA_MENUBAR 0x00051002
/*! @brief Wayland specific init hint.
*
* Wayland specific [init hint](@ref GLFW_WAYLAND_LIBDECOR_hint).
*/
#define GLFW_WAYLAND_LIBDECOR 0x00053001
/*! @} */
#define GLFW_DONT_CARE -1

View File

@ -54,7 +54,10 @@ static _GLFWinitconfig _glfwInitHints =
{
GLFW_TRUE, // macOS menu bar
GLFW_TRUE // macOS bundle chdir
}
},
{
GLFW_WAYLAND_PREFER_LIBDECOR // Wayland libdecor mode
},
};
// Terminate the library
@ -367,6 +370,9 @@ GLFWAPI void glfwInitHint(int hint, int value)
case GLFW_COCOA_MENUBAR:
_glfwInitHints.ns.menubar = value;
return;
case GLFW_WAYLAND_LIBDECOR:
_glfwInitHints.wl.libdecorMode = value;
return;
}
_glfwInputError(GLFW_INVALID_ENUM,

View File

@ -244,6 +244,9 @@ struct _GLFWinitconfig
GLFWbool menubar;
GLFWbool chdir;
} ns;
struct {
int libdecorMode;
} wl;
};
// Window configuration

View File

@ -163,6 +163,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)
@ -433,6 +447,93 @@ int _glfwPlatformInit(void)
return GLFW_FALSE;
}
if (_glfw.hints.init.wl.libdecorMode == GLFW_WAYLAND_PREFER_LIBDECOR)
_glfw.wl.libdecor.handle = _glfw_dlopen("libdecor-0.so.0");
if (_glfw.wl.libdecor.handle)
{
_glfw.wl.libdecor.libdecor_new_ = (PFN_libdecor_new)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_new");
_glfw.wl.libdecor.libdecor_unref_ = (PFN_libdecor_unref)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_unref");
_glfw.wl.libdecor.libdecor_get_fd_ = (PFN_libdecor_get_fd)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_get_fd");
_glfw.wl.libdecor.libdecor_dispatch_ = (PFN_libdecor_dispatch)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_dispatch");
_glfw.wl.libdecor.libdecor_decorate_ = (PFN_libdecor_decorate)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_decorate");
_glfw.wl.libdecor.libdecor_frame_unref_ = (PFN_libdecor_frame_unref)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_unref");
_glfw.wl.libdecor.libdecor_frame_set_app_id_ = (PFN_libdecor_frame_set_app_id)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_app_id");
_glfw.wl.libdecor.libdecor_frame_set_title_ = (PFN_libdecor_frame_set_title)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_title");
_glfw.wl.libdecor.libdecor_frame_set_minimized_ = (PFN_libdecor_frame_set_minimized)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_minimized");
_glfw.wl.libdecor.libdecor_frame_set_fullscreen_ = (PFN_libdecor_frame_set_fullscreen)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_fullscreen");
_glfw.wl.libdecor.libdecor_frame_unset_fullscreen_ = (PFN_libdecor_frame_unset_fullscreen)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_unset_fullscreen");
_glfw.wl.libdecor.libdecor_frame_map_ = (PFN_libdecor_frame_map)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_map");
_glfw.wl.libdecor.libdecor_frame_commit_ = (PFN_libdecor_frame_commit)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_commit");
_glfw.wl.libdecor.libdecor_frame_set_min_content_size_ = (PFN_libdecor_frame_set_min_content_size)
_glfw_dlsym(_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)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_max_content_size");
_glfw.wl.libdecor.libdecor_frame_set_maximized_ = (PFN_libdecor_frame_set_maximized)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_maximized");
_glfw.wl.libdecor.libdecor_frame_unset_maximized_ = (PFN_libdecor_frame_unset_maximized)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_unset_maximized");
_glfw.wl.libdecor.libdecor_frame_set_capabilities_ = (PFN_libdecor_frame_set_capabilities)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_capabilities");
_glfw.wl.libdecor.libdecor_frame_unset_capabilities_ = (PFN_libdecor_frame_unset_capabilities)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_unset_capabilities");
_glfw.wl.libdecor.libdecor_frame_set_visibility_ = (PFN_libdecor_frame_set_visibility)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_set_visibility");
_glfw.wl.libdecor.libdecor_frame_get_xdg_toplevel_ = (PFN_libdecor_frame_get_xdg_toplevel)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_frame_get_xdg_toplevel");
_glfw.wl.libdecor.libdecor_configuration_get_content_size_ = (PFN_libdecor_configuration_get_content_size)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_configuration_get_content_size");
_glfw.wl.libdecor.libdecor_configuration_get_window_state_ = (PFN_libdecor_configuration_get_window_state)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_configuration_get_window_state");
_glfw.wl.libdecor.libdecor_state_new_ = (PFN_libdecor_state_new)
_glfw_dlsym(_glfw.wl.libdecor.handle, "libdecor_state_new");
_glfw.wl.libdecor.libdecor_state_free_ = (PFN_libdecor_state_free)
_glfw_dlsym(_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_)
{
_glfw_dlclose(_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, &registryListener, NULL);
@ -459,6 +560,15 @@ int _glfwPlatformInit(void)
_glfwInitTimerPOSIX();
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 (wl_seat_get_version(_glfw.wl.seat) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
{
@ -503,6 +613,15 @@ void _glfwPlatformTerminate(void)
_glfwTerminateEGL();
_glfwTerminateOSMesa();
if (_glfw.wl.libdecor.context)
libdecor_unref(_glfw.wl.libdecor.context);
if (_glfw.wl.libdecor.handle)
{
_glfw_dlclose(_glfw.wl.libdecor.handle);
_glfw.wl.libdecor.handle = NULL;
}
if (_glfw.wl.egl.handle)
{
_glfw_dlclose(_glfw.wl.egl.handle);

View File

@ -29,6 +29,8 @@
#include <xkbcommon/xkbcommon-compose.h>
#include <dlfcn.h>
#include <stdbool.h>
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
typedef struct VkWaylandSurfaceCreateInfoKHR
@ -146,6 +148,122 @@ 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
{
GLFW_MAIN_WINDOW,
@ -208,6 +326,11 @@ typedef struct _GLFWwindowWayland
uint32_t decorationMode;
} xdg;
struct {
struct libdecor_frame* frame;
int mode;
} libdecor;
_GLFWcursor* currentCursor;
double cursorPosX, cursorPosY;
@ -340,6 +463,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

View File

@ -486,7 +486,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);
@ -502,12 +507,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);
@ -650,7 +658,187 @@ 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.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))
_glfwPlatformWaitEvents();
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);
@ -743,14 +931,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);
@ -760,6 +961,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;
@ -926,13 +1128,17 @@ static void inputText(_GLFWwindow* window, uint32_t scancode)
static void handleEvents(double* timeout)
{
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)
@ -954,7 +1160,7 @@ static void handleEvents(double* timeout)
return;
}
if (!waitForData(fds, 3, timeout))
if (!waitForData(fds, sizeof(fds) / sizeof(fds[0]), timeout))
{
wl_display_cancel_read(_glfw.wl.display);
return;
@ -999,6 +1205,9 @@ static void handleEvents(double* timeout)
event = GLFW_TRUE;
}
}
if (fds[3].revents & POLLIN)
libdecor_dispatch(_glfw.wl.libdecor.context, 0);
}
}
@ -1942,7 +2151,10 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
if (window->wl.title)
free(window->wl.title);
window->wl.title = _glfw_strdup(title);
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);
}
@ -1990,6 +2202,13 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
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);
}
@ -1999,7 +2218,20 @@ void _glfwPlatformSetWindowSizeLimits(_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;
@ -2053,6 +2285,13 @@ void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window,
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)
@ -2098,7 +2337,9 @@ void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
void _glfwPlatformIconifyWindow(_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);
}
@ -2115,7 +2356,9 @@ void _glfwPlatformRestoreWindow(_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;
@ -2125,7 +2368,9 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
void _glfwPlatformMaximizeWindow(_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;
@ -2133,7 +2378,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
if (!window->wl.xdg.toplevel)
if (!window->wl.libdecor.frame && !window->wl.xdg.toplevel)
{
// NOTE: The XDG/shell surface is created here so command-line applications
// with off-screen windows do not appear in for example the Unity dock
@ -2224,14 +2469,34 @@ int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled)
{
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_PLATFORM_ERROR,
"Wayland: Window attribute setting not implemented yet");
}
}
void _glfwPlatformSetWindowDecorated(_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;
@ -2242,7 +2507,7 @@ void _glfwPlatformSetWindowDecorated(_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);