From 1dca4233bbb7c75c39af70513214b7c843595c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Wed, 21 Jan 2026 20:08:21 +0100 Subject: [PATCH 1/5] object: add explicit API to remove a property --- src/api/wayfire/object.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/api/wayfire/object.hpp b/src/api/wayfire/object.hpp index 59705c188..f7956bb75 100644 --- a/src/api/wayfire/object.hpp +++ b/src/api/wayfire/object.hpp @@ -162,6 +162,11 @@ class object_base_t return true; } + void erase_property(std::string name) + { + erase_data(name); + } + object_base_t(const object_base_t &) = delete; object_base_t(object_base_t &&) = delete; object_base_t& operator =(const object_base_t&) = delete; From 586e040862749eb09fd787a3e225f6386ec369c1 Mon Sep 17 00:00:00 2001 From: Daniel Kondor Date: Sun, 9 Feb 2025 14:19:07 +0100 Subject: [PATCH 2/5] KDE Appmenu: new protocol implementation This allows apps to link their implementation of the com.canonical.dbusmenu DBus interface to their open views, implementing global menus. --- metadata/kde-appmenu.xml | 8 +++ metadata/meson.build | 1 + plugins/protocols/kde-appmenu.cpp | 114 ++++++++++++++++++++++++++++++ plugins/protocols/meson.build | 2 +- proto/kde-appmenu.xml | 40 +++++++++++ proto/meson.build | 3 +- 6 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 metadata/kde-appmenu.xml create mode 100644 plugins/protocols/kde-appmenu.cpp create mode 100644 proto/kde-appmenu.xml diff --git a/metadata/kde-appmenu.xml b/metadata/kde-appmenu.xml new file mode 100644 index 000000000..2f5497b33 --- /dev/null +++ b/metadata/kde-appmenu.xml @@ -0,0 +1,8 @@ + + + + <_short>KDE Appmenu support + <_long>An implementation of the KDE Appmenu protocol for global menus. + Utility + + diff --git a/metadata/meson.build b/metadata/meson.build index 3cb2112a2..73b50c9ed 100644 --- a/metadata/meson.build +++ b/metadata/meson.build @@ -19,6 +19,7 @@ install_data('input-method-v1.xml', install_dir: conf_data.get('PLUGIN_XML_DIR') install_data('invert.xml', install_dir: conf_data.get('PLUGIN_XML_DIR')) install_data('ipc.xml', install_dir: conf_data.get('PLUGIN_XML_DIR')) install_data('ipc-rules.xml', install_dir: conf_data.get('PLUGIN_XML_DIR')) +install_data('kde-appmenu.xml', install_dir: conf_data.get('PLUGIN_XML_DIR')) install_data('move.xml', install_dir: conf_data.get('PLUGIN_XML_DIR')) install_data('oswitch.xml', install_dir: conf_data.get('PLUGIN_XML_DIR')) install_data('output.xml', install_dir: conf_data.get('PLUGIN_XML_DIR')) diff --git a/plugins/protocols/kde-appmenu.cpp b/plugins/protocols/kde-appmenu.cpp new file mode 100644 index 000000000..e0d629f35 --- /dev/null +++ b/plugins/protocols/kde-appmenu.cpp @@ -0,0 +1,114 @@ +#include "kde-appmenu-protocol.h" +#include "wayfire/core.hpp" +#include "wayfire/object.hpp" +#include +#include +#include + +#define KDE_APPMENU_VERSION 2 + +struct wf_kde_appmenu_surface +{ + wl_resource *resource; + wl_resource *wl_surface; +}; + +static void handle_kde_appmenu_set_address(wl_client *client, + wl_resource *resource, const char *service_name, + const char *object_path) +{ + auto surface = static_cast(wl_resource_get_user_data(resource)); + wayfire_view view = wf::wl_surface_to_wayfire_view(surface->wl_surface); + if (!view) + { + LOGE("Could not get view"); + return; + } else + { + if (service_name && *service_name) + { + view->set_property("kde-appmenu-service-name", service_name); + } else + { + view->erase_property("kde-appmenu-service-name"); + } + + if (object_path && *object_path) + { + view->set_property("kde-appmenu-object-path", object_path); + } else + { + view->erase_property("kde-appmenu-object-path"); + } + } +} + +static void handle_kde_appmenu_release(wl_client*, wl_resource*) +{ + /* no-op */ +} + +static void handle_kde_appmenu_destroy(wl_resource *resource) +{ + auto surface = static_cast(wl_resource_get_user_data(resource)); + delete surface; +} + +const struct org_kde_kwin_appmenu_interface kde_appmenu_impl = { + .set_address = handle_kde_appmenu_set_address, + .release = handle_kde_appmenu_release +}; + + +static void handle_kde_appmenu_manager_create(wl_client *client, + wl_resource *resource, uint32_t id, wl_resource *surface) +{ + wf_kde_appmenu_surface *kde_appmenu_surface = new wf_kde_appmenu_surface; + kde_appmenu_surface->resource = wl_resource_create(client, + &org_kde_kwin_appmenu_interface, wl_resource_get_version(resource), id); + kde_appmenu_surface->wl_surface = surface; + wl_resource_set_implementation(kde_appmenu_surface->resource, &kde_appmenu_impl, + kde_appmenu_surface, handle_kde_appmenu_destroy); +} + +static void handle_kde_appmenu_manager_release(wl_client*, wl_resource*) +{ + /* no-op */ +} + +static void handle_kde_appmenu_manager_destroy(wl_resource*) +{ + /* no-op */ +} + +static const struct org_kde_kwin_appmenu_manager_interface kde_appmenu_manager_impl = { + .create = handle_kde_appmenu_manager_create, + .release = handle_kde_appmenu_manager_release +}; + + +static void bind_kde_appmenu(wl_client *client, void *data, uint32_t version, uint32_t id) +{ + auto resource = wl_resource_create(client, &org_kde_kwin_appmenu_manager_interface, + KDE_APPMENU_VERSION, id); + wl_resource_set_implementation(resource, &kde_appmenu_manager_impl, + data, handle_kde_appmenu_manager_destroy); +} + +class wayfire_kde_appmenu_impl : public wf::plugin_interface_t +{ + public: + void init() override + { + auto display = wf::get_core().display; + wl_global_create(display, &org_kde_kwin_appmenu_manager_interface, + KDE_APPMENU_VERSION, NULL, bind_kde_appmenu); + } + + bool is_unloadable() override + { + return false; + } +}; + +DECLARE_WAYFIRE_PLUGIN(wayfire_kde_appmenu_impl); diff --git a/plugins/protocols/meson.build b/plugins/protocols/meson.build index be87ef092..c2c4299b8 100644 --- a/plugins/protocols/meson.build +++ b/plugins/protocols/meson.build @@ -1,6 +1,6 @@ protocol_plugins = [ 'ext-toplevel', 'foreign-toplevel', 'gtk-shell', 'wayfire-shell', 'xdg-activation', 'shortcuts-inhibit', - 'input-method-v1', 'session-lock', 'security-context-v1', + 'input-method-v1', 'session-lock', 'security-context-v1', 'kde-appmenu', ] all_include_dirs = [wayfire_api_inc, wayfire_conf_inc, plugins_common_inc, ipc_include_dirs] diff --git a/proto/kde-appmenu.xml b/proto/kde-appmenu.xml new file mode 100644 index 000000000..7b2d7fb0b --- /dev/null +++ b/proto/kde-appmenu.xml @@ -0,0 +1,40 @@ + + + + + + This interface allows a client to link a window (or wl_surface) to an com.canonical.dbusmenu + interface registered on DBus. + + + + + + + + + + + + + The DBus service name and object path where the appmenu interface is present + The object should be registered on the session bus before sending this request. + If not applicable, clients should remove this object. + + + + Set or update the service name and object path. + Strings should be formatted in Latin-1 matching the relevant DBus specifications. + + + + + + + + + diff --git a/proto/meson.build b/proto/meson.build index 338e4daaa..be28122e9 100644 --- a/proto/meson.build +++ b/proto/meson.build @@ -33,7 +33,8 @@ server_protocols = [ 'wayfire-shell-unstable-v2.xml', 'gtk-shell.xml', 'wlr-layer-shell-unstable-v1.xml', - 'wlr-output-power-management-unstable-v1.xml' + 'wlr-output-power-management-unstable-v1.xml', + 'kde-appmenu.xml' ] wl_protos_src = [] From 442eb28c5f709d9e05b663d99c7d7b430b3b1772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Sun, 18 Jan 2026 00:16:26 +0100 Subject: [PATCH 3/5] gtk-shell: store DBus properties This allows relaying these to clients --- plugins/protocols/gtk-shell.cpp | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/plugins/protocols/gtk-shell.cpp b/plugins/protocols/gtk-shell.cpp index 1bf4adfb7..0283cb59e 100644 --- a/plugins/protocols/gtk-shell.cpp +++ b/plugins/protocols/gtk-shell.cpp @@ -45,6 +45,54 @@ static void handle_gtk_surface_set_dbus_properties(wl_client *client, { wf::get_core().get_data_safe()->surface_app_id[surface->wl_surface] = application_id; } + + wayfire_view view = wf::wl_surface_to_wayfire_view(surface->wl_surface); + if (!view) + { + LOGE("Could not get view"); + return; + } else + { + if (app_menu_path && *app_menu_path) + { + view->set_property("gtk-shell-app-menu-path", app_menu_path); + } else + { + view->erase_property("gtk-shell-app-menu-path"); + } + + if (application_object_path && *application_object_path) + { + view->set_property("gtk-shell-application-object-path", application_object_path); + } else + { + view->erase_property("gtk-shell-application-object-path"); + } + + if (menubar_path && *menubar_path) + { + view->set_property("gtk-shell-menubar-path", menubar_path); + } else + { + view->erase_property("gtk-shell-menubar-path"); + } + + if (unique_bus_name && *unique_bus_name) + { + view->set_property("gtk-shell-unique-bus-name", unique_bus_name); + } else + { + view->erase_property("gtk-shell-unique-bus-name"); + } + + if (window_object_path && *window_object_path) + { + view->set_property("gtk-shell-window-object-path", window_object_path); + } else + { + view->erase_property("gtk-shell-window-object-path"); + } + } } /** From 0c30d3cf993299219e9c383816cf207a95457190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Wed, 21 Jan 2026 21:19:07 +0100 Subject: [PATCH 4/5] gtk-shell: optionally advertise the "global_menu_bar" capability This will trigger GTK3 apps to export their menus via DBus. --- metadata/gtk-shell.xml | 5 +++++ plugins/protocols/gtk-shell.cpp | 3 +++ 2 files changed, 8 insertions(+) diff --git a/metadata/gtk-shell.xml b/metadata/gtk-shell.xml index ab445d707..4ae809c0c 100644 --- a/metadata/gtk-shell.xml +++ b/metadata/gtk-shell.xml @@ -4,5 +4,10 @@ <_short>GTK Shell Protocol <_long>An implementation of the gtk-shell protocol. Utility + diff --git a/plugins/protocols/gtk-shell.cpp b/plugins/protocols/gtk-shell.cpp index 0283cb59e..883420937 100644 --- a/plugins/protocols/gtk-shell.cpp +++ b/plugins/protocols/gtk-shell.cpp @@ -379,6 +379,9 @@ void bind_gtk_shell1(wl_client *client, void *data, uint32_t version, uint32_t i { auto resource = wl_resource_create(client, >k_shell1_interface, GTK_SHELL_VERSION, id); wl_resource_set_implementation(resource, >k_shell1_impl, data, handle_gtk_shell1_destroy); + wf::option_wrapper_t global_menu_bar{"gtk-shell/global_menu_bar"}; + // Note: we also have the "global_app_menu" capability, but it does not seem to have any effect + gtk_shell1_send_capabilities(resource, global_menu_bar ? GTK_SHELL1_CAPABILITY_GLOBAL_MENU_BAR : 0); } class wayfire_gtk_shell_impl : public wf::plugin_interface_t From 86aceed15fb9a62fa495082ac242d191ba0732dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kondor=20D=C3=A1niel?= Date: Thu, 22 Jan 2026 23:32:42 +0100 Subject: [PATCH 5/5] bump ABI version --- src/api/wayfire/plugin.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/wayfire/plugin.hpp b/src/api/wayfire/plugin.hpp index bcbe9f51f..6ea456fd2 100644 --- a/src/api/wayfire/plugin.hpp +++ b/src/api/wayfire/plugin.hpp @@ -107,7 +107,7 @@ using wayfire_plugin_load_func = wf::plugin_interface_t * (*)(); /** * The version is defined as macro as well, to allow conditional compilation. */ -#define WAYFIRE_API_ABI_VERSION_MACRO 2025'12'26 +#define WAYFIRE_API_ABI_VERSION_MACRO 2026'01'22 /** * The version of Wayfire's API/ABI