From db550d6829fbe321622d7fa16ae49204da6cb55d Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Thu, 23 Apr 2026 09:30:51 +0200 Subject: [PATCH] use KDE server-decoration protocol for native Wayland frameless Replace xdg-decoration (needs xdg_toplevel, GTK3 doesn't expose it) with org_kde_kwin_server_decoration (works with wl_surface which GDK provides). On KDE Wayland: binds the manager from the registry, creates a per-surface decoration object, requests mode=None (0). Falls back gracefully on GNOME (protocol not present, GTK's set_decorated(FALSE) already works). X11 fallback via gdk_window_set_decorations(0) unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) --- linux/runner/CMakeLists.txt | 1 + linux/runner/my_application.cc | 50 ++-- .../server-decoration-client-protocol.h | 240 ++++++++++++++++++ .../protocols/server-decoration-protocol.c | 60 +++++ linux/runner/protocols/server-decoration.xml | 35 +++ 5 files changed, 365 insertions(+), 21 deletions(-) create mode 100644 linux/runner/protocols/server-decoration-client-protocol.h create mode 100644 linux/runner/protocols/server-decoration-protocol.c create mode 100644 linux/runner/protocols/server-decoration.xml diff --git a/linux/runner/CMakeLists.txt b/linux/runner/CMakeLists.txt index 3b2e0d3a..9cb04cd7 100644 --- a/linux/runner/CMakeLists.txt +++ b/linux/runner/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(${BINARY_NAME} "main.cc" "my_application.cc" "protocols/xdg-decoration-unstable-v1-protocol.c" + "protocols/server-decoration-protocol.c" "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" ) diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc index f715977d..d6ced1fa 100644 --- a/linux/runner/my_application.cc +++ b/linux/runner/my_application.cc @@ -8,7 +8,7 @@ #include #endif #ifdef HAS_WAYLAND_CLIENT -#include "protocols/xdg-decoration-unstable-v1-client-protocol.h" +#include "protocols/server-decoration-client-protocol.h" #endif #include "flutter/generated_plugin_registrant.h" @@ -20,17 +20,18 @@ struct _MyApplication { G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) -// D-057: xdg-decoration state. +// D-057: KDE server-decoration protocol — request no decorations. #ifdef HAS_WAYLAND_CLIENT -static struct zxdg_decoration_manager_v1* decoration_manager = nullptr; +static struct org_kde_kwin_server_decoration_manager* kde_deco_manager = nullptr; static void registry_handler(void* data, struct wl_registry* registry, uint32_t id, const char* interface, uint32_t version) { - if (g_strcmp0(interface, zxdg_decoration_manager_v1_interface.name) == 0) { - decoration_manager = (struct zxdg_decoration_manager_v1*) + if (g_strcmp0(interface, + org_kde_kwin_server_decoration_manager_interface.name) == 0) { + kde_deco_manager = (struct org_kde_kwin_server_decoration_manager*) wl_registry_bind(registry, id, - &zxdg_decoration_manager_v1_interface, 1); + &org_kde_kwin_server_decoration_manager_interface, 1); } } @@ -40,21 +41,31 @@ static void registry_remover(void* data, struct wl_registry* registry, static const struct wl_registry_listener registry_listener = { registry_handler, registry_remover}; -static void request_client_side_decorations(GtkWindow* window) { +static void kde_deco_default_mode(void* data, + struct org_kde_kwin_server_decoration_manager* mgr, uint32_t mode) {} + +static const struct org_kde_kwin_server_decoration_manager_listener + kde_deco_manager_listener = {kde_deco_default_mode}; + +static void request_no_server_decorations(GtkWindow* window) { GdkDisplay* display = gtk_widget_get_display(GTK_WIDGET(window)); if (!GDK_IS_WAYLAND_DISPLAY(display)) return; - struct wl_display* wl_display = + struct wl_display* wl_dpy = gdk_wayland_display_get_wl_display(display); - struct wl_registry* registry = wl_display_get_registry(wl_display); + struct wl_registry* registry = wl_display_get_registry(wl_dpy); wl_registry_add_listener(registry, ®istry_listener, nullptr); - wl_display_roundtrip(wl_display); + wl_display_roundtrip(wl_dpy); - if (decoration_manager == nullptr) { + if (kde_deco_manager == nullptr) { wl_registry_destroy(registry); return; } + org_kde_kwin_server_decoration_manager_add_listener( + kde_deco_manager, &kde_deco_manager_listener, nullptr); + wl_display_roundtrip(wl_dpy); + GdkWindow* gdk_win = gtk_widget_get_window(GTK_WIDGET(window)); if (gdk_win == nullptr) { wl_registry_destroy(registry); @@ -67,17 +78,14 @@ static void request_client_side_decorations(GtkWindow* window) { return; } - struct zxdg_toplevel_decoration_v1* toplevel_decoration = - zxdg_decoration_manager_v1_get_toplevel_decoration( - decoration_manager, surface); - - if (toplevel_decoration != nullptr) { - zxdg_toplevel_decoration_v1_set_mode( - toplevel_decoration, - ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); + struct org_kde_kwin_server_decoration* deco = + org_kde_kwin_server_decoration_manager_create(kde_deco_manager, surface); + if (deco != nullptr) { + // Mode 0 = None (no decorations at all) + org_kde_kwin_server_decoration_request_mode(deco, 0); + wl_display_roundtrip(wl_dpy); } - wl_display_roundtrip(wl_display); wl_registry_destroy(registry); } #endif @@ -88,7 +96,7 @@ static void on_window_realize(GtkWidget* widget, gpointer data) { gdk_window_set_decorations(gdk_win, (GdkWMDecoration)0); } #ifdef HAS_WAYLAND_CLIENT - request_client_side_decorations(GTK_WINDOW(widget)); + request_no_server_decorations(GTK_WINDOW(widget)); #endif } diff --git a/linux/runner/protocols/server-decoration-client-protocol.h b/linux/runner/protocols/server-decoration-client-protocol.h new file mode 100644 index 00000000..16d48b4c --- /dev/null +++ b/linux/runner/protocols/server-decoration-client-protocol.h @@ -0,0 +1,240 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef SERVER_DECORATION_CLIENT_PROTOCOL_H +#define SERVER_DECORATION_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_server_decoration The server_decoration protocol + * @section page_ifaces_server_decoration Interfaces + * - @subpage page_iface_org_kde_kwin_server_decoration_manager - + * - @subpage page_iface_org_kde_kwin_server_decoration - + * @section page_copyright_server_decoration Copyright + *
+ *
+ * SPDX-FileCopyrightText: 2015 Martin Gräßlin
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ * 
+ */ +struct org_kde_kwin_server_decoration; +struct org_kde_kwin_server_decoration_manager; +struct wl_surface; + +#ifndef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_INTERFACE +#define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_INTERFACE +/** + * @page page_iface_org_kde_kwin_server_decoration_manager org_kde_kwin_server_decoration_manager + * @section page_iface_org_kde_kwin_server_decoration_manager_api API + * See @ref iface_org_kde_kwin_server_decoration_manager. + */ +/** + * @defgroup iface_org_kde_kwin_server_decoration_manager The org_kde_kwin_server_decoration_manager interface + */ +extern const struct wl_interface org_kde_kwin_server_decoration_manager_interface; +#endif +#ifndef ORG_KDE_KWIN_SERVER_DECORATION_INTERFACE +#define ORG_KDE_KWIN_SERVER_DECORATION_INTERFACE +/** + * @page page_iface_org_kde_kwin_server_decoration org_kde_kwin_server_decoration + * @section page_iface_org_kde_kwin_server_decoration_api API + * See @ref iface_org_kde_kwin_server_decoration. + */ +/** + * @defgroup iface_org_kde_kwin_server_decoration The org_kde_kwin_server_decoration interface + */ +extern const struct wl_interface org_kde_kwin_server_decoration_interface; +#endif + +#ifndef ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_ENUM +#define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_ENUM +enum org_kde_kwin_server_decoration_manager_mode { + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_NONE = 0, + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT = 1, + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_SERVER = 2, +}; +#endif /* ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_ENUM */ + +/** + * @ingroup iface_org_kde_kwin_server_decoration_manager + * @struct org_kde_kwin_server_decoration_manager_listener + */ +struct org_kde_kwin_server_decoration_manager_listener { + /** + */ + void (*default_mode)(void *data, + struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager, + uint32_t mode); +}; + +/** + * @ingroup iface_org_kde_kwin_server_decoration_manager + */ +static inline int +org_kde_kwin_server_decoration_manager_add_listener(struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager, + const struct org_kde_kwin_server_decoration_manager_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) org_kde_kwin_server_decoration_manager, + (void (**)(void)) listener, data); +} + +#define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_CREATE 0 + +/** + * @ingroup iface_org_kde_kwin_server_decoration_manager + */ +#define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_DEFAULT_MODE_SINCE_VERSION 1 + +/** + * @ingroup iface_org_kde_kwin_server_decoration_manager + */ +#define ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_CREATE_SINCE_VERSION 1 + +/** @ingroup iface_org_kde_kwin_server_decoration_manager */ +static inline void +org_kde_kwin_server_decoration_manager_set_user_data(struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) org_kde_kwin_server_decoration_manager, user_data); +} + +/** @ingroup iface_org_kde_kwin_server_decoration_manager */ +static inline void * +org_kde_kwin_server_decoration_manager_get_user_data(struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager) +{ + return wl_proxy_get_user_data((struct wl_proxy *) org_kde_kwin_server_decoration_manager); +} + +static inline uint32_t +org_kde_kwin_server_decoration_manager_get_version(struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager) +{ + return wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_server_decoration_manager); +} + +/** @ingroup iface_org_kde_kwin_server_decoration_manager */ +static inline void +org_kde_kwin_server_decoration_manager_destroy(struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager) +{ + wl_proxy_destroy((struct wl_proxy *) org_kde_kwin_server_decoration_manager); +} + +/** + * @ingroup iface_org_kde_kwin_server_decoration_manager + */ +static inline struct org_kde_kwin_server_decoration * +org_kde_kwin_server_decoration_manager_create(struct org_kde_kwin_server_decoration_manager *org_kde_kwin_server_decoration_manager, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) org_kde_kwin_server_decoration_manager, + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_CREATE, &org_kde_kwin_server_decoration_interface, wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_server_decoration_manager), 0, NULL, surface); + + return (struct org_kde_kwin_server_decoration *) id; +} + +#ifndef ORG_KDE_KWIN_SERVER_DECORATION_MODE_ENUM +#define ORG_KDE_KWIN_SERVER_DECORATION_MODE_ENUM +enum org_kde_kwin_server_decoration_mode { + ORG_KDE_KWIN_SERVER_DECORATION_MODE_NONE = 0, + ORG_KDE_KWIN_SERVER_DECORATION_MODE_CLIENT = 1, + ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER = 2, +}; +#endif /* ORG_KDE_KWIN_SERVER_DECORATION_MODE_ENUM */ + +/** + * @ingroup iface_org_kde_kwin_server_decoration + * @struct org_kde_kwin_server_decoration_listener + */ +struct org_kde_kwin_server_decoration_listener { + /** + */ + void (*mode)(void *data, + struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, + uint32_t mode); +}; + +/** + * @ingroup iface_org_kde_kwin_server_decoration + */ +static inline int +org_kde_kwin_server_decoration_add_listener(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, + const struct org_kde_kwin_server_decoration_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) org_kde_kwin_server_decoration, + (void (**)(void)) listener, data); +} + +#define ORG_KDE_KWIN_SERVER_DECORATION_RELEASE 0 +#define ORG_KDE_KWIN_SERVER_DECORATION_REQUEST_MODE 1 + +/** + * @ingroup iface_org_kde_kwin_server_decoration + */ +#define ORG_KDE_KWIN_SERVER_DECORATION_MODE_SINCE_VERSION 1 + +/** + * @ingroup iface_org_kde_kwin_server_decoration + */ +#define ORG_KDE_KWIN_SERVER_DECORATION_RELEASE_SINCE_VERSION 1 +/** + * @ingroup iface_org_kde_kwin_server_decoration + */ +#define ORG_KDE_KWIN_SERVER_DECORATION_REQUEST_MODE_SINCE_VERSION 1 + +/** @ingroup iface_org_kde_kwin_server_decoration */ +static inline void +org_kde_kwin_server_decoration_set_user_data(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) org_kde_kwin_server_decoration, user_data); +} + +/** @ingroup iface_org_kde_kwin_server_decoration */ +static inline void * +org_kde_kwin_server_decoration_get_user_data(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration) +{ + return wl_proxy_get_user_data((struct wl_proxy *) org_kde_kwin_server_decoration); +} + +static inline uint32_t +org_kde_kwin_server_decoration_get_version(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration) +{ + return wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_server_decoration); +} + +/** @ingroup iface_org_kde_kwin_server_decoration */ +static inline void +org_kde_kwin_server_decoration_destroy(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration) +{ + wl_proxy_destroy((struct wl_proxy *) org_kde_kwin_server_decoration); +} + +/** + * @ingroup iface_org_kde_kwin_server_decoration + */ +static inline void +org_kde_kwin_server_decoration_release(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration) +{ + wl_proxy_marshal_flags((struct wl_proxy *) org_kde_kwin_server_decoration, + ORG_KDE_KWIN_SERVER_DECORATION_RELEASE, NULL, wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_server_decoration), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_org_kde_kwin_server_decoration + */ +static inline void +org_kde_kwin_server_decoration_request_mode(struct org_kde_kwin_server_decoration *org_kde_kwin_server_decoration, uint32_t mode) +{ + wl_proxy_marshal_flags((struct wl_proxy *) org_kde_kwin_server_decoration, + ORG_KDE_KWIN_SERVER_DECORATION_REQUEST_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_server_decoration), 0, mode); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/linux/runner/protocols/server-decoration-protocol.c b/linux/runner/protocols/server-decoration-protocol.c new file mode 100644 index 00000000..989c9db4 --- /dev/null +++ b/linux/runner/protocols/server-decoration-protocol.c @@ -0,0 +1,60 @@ +/* Generated by wayland-scanner 1.24.0 */ + +/* + * SPDX-FileCopyrightText: 2015 Martin Gräßlin + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface org_kde_kwin_server_decoration_interface; +extern const struct wl_interface wl_surface_interface; + +static const struct wl_interface *server_decoration_types[] = { + NULL, + &org_kde_kwin_server_decoration_interface, + &wl_surface_interface, +}; + +static const struct wl_message org_kde_kwin_server_decoration_manager_requests[] = { + { "create", "no", server_decoration_types + 1 }, +}; + +static const struct wl_message org_kde_kwin_server_decoration_manager_events[] = { + { "default_mode", "u", server_decoration_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface org_kde_kwin_server_decoration_manager_interface = { + "org_kde_kwin_server_decoration_manager", 1, + 1, org_kde_kwin_server_decoration_manager_requests, + 1, org_kde_kwin_server_decoration_manager_events, +}; + +static const struct wl_message org_kde_kwin_server_decoration_requests[] = { + { "release", "", server_decoration_types + 0 }, + { "request_mode", "u", server_decoration_types + 0 }, +}; + +static const struct wl_message org_kde_kwin_server_decoration_events[] = { + { "mode", "u", server_decoration_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface org_kde_kwin_server_decoration_interface = { + "org_kde_kwin_server_decoration", 1, + 2, org_kde_kwin_server_decoration_requests, + 1, org_kde_kwin_server_decoration_events, +}; + diff --git a/linux/runner/protocols/server-decoration.xml b/linux/runner/protocols/server-decoration.xml new file mode 100644 index 00000000..aed8560a --- /dev/null +++ b/linux/runner/protocols/server-decoration.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +