Aaron Franke
Aaron Franke

Reputation: 4062

How to implement global menu support in C++ on Linux?

I am working on developing a cross-platform application in C++. We have a menu which uses the global menu on macOS. I wish to use the system-provided global menu on all platforms that have it. KDE has a global menu, here is an example of it showing a menu for VS Code:

How can I implement this in C++? I have heard that this can be done using dbus, but I have no idea how to do that, which headers to include, what methods to call, etc. A good answer to this question would include links to documentation that describe how to use the global menu APIs on Linux, and a great answer would include an example.

Upvotes: 1

Views: 894

Answers (1)

lestcape
lestcape

Reputation: 269

A global menu is a compromise between a client application and a server application, where both must previously agree on what a menu is and how it can be represented in a serialized way.

In Linux, as it is not difficult to guess, there is no single convention for handling the information of a global menu, although luckily there are only two protocols currently that can be used. One of the protocols is implemented by the Qt toolkit and the other by the Gtk toolkit.

Both use DBus as a means of communication between the applications, but the protocols are not at all the same and both are dynamic, so both require update cycles every time the menu changes in the client application.

I think the most notable thing is that Qt's protocol uses menu item identifiers, while Gtk's doesn't, so with Qt's implementation, you can reuse some menu items from one update cycle to the next, whereas with the Gtk implementation, even if the items will not change, you are required to recreate them.

Actually, you can have a Qt application and use the Gtk protocol or you can have a Gtk application and use the Qt protocol. But this is only possible if you use the protocol in its most basic and primitive form, because as is to be expected then the toolkits create abstraction layers on top of the basic protocol, to simplify the work for the user and then if you intend to use these layers of abstraction, because if you need to define once and for all what your toolkit is, which you have not done in your question.

Both protocols can be decomposed into two basic tasks, the first task is to implement a menu provider (DBus) service from the client application (or relying on the client application's toolkit, if it already has one) and the second part consists of in informing to the server application (the one that consumes the global menu) where the service that provides the menu is.

Since the server application is by default the windows manager, the protocols for each window to share the location of its menu service can go from another DBUS service where each client registers its menu, to passing the path of where the service is to the windows manager directly.

Here the matter is complicated again because the protocols for X11 are based on the xid of each window, while in Wayland there is no standard way to pass the menu data and for example in Gtk, you have to directly call a function of the window manager. That from the GDK backend (https://gitlab.gnome.org/rmader/gtk/-/blob/16ac1a12fc93000124050bc4ee56a10374865d14/gdk/wayland/gdkwindow-wayland.c#L5016).

I am only going to mention then the case of Qt, in which an external interface to the window manager is used where the applications register their menu. This DBus service is that of com.canonical.AppMenu.Registrar: https://github.com/KDE/appmenu-runner/blob/master/src/com.canonical.AppMenu.Registrar.xml An example of a service implementation can be found here: https://github.com/SeptemberHX/dde-globalmenu-service

Regarding the two menu services:

For Gtk you can check: https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI https://developer-old.gnome.org/gio/stable/gio-GMenuModel-exporter.html

For Qt you can check: https://lazka.github.io/pgi-docs/Dbusmenu-0.4/index.html

Here is much more information that can be consulted: https://hellosystem.github.io/docs/developer/menu.html

Here is also an example of how to get the menu bar exported in a gtk application: https://developer-old.gnome.org/gtk4/stable/GtkApplicationWindow.html

Unfortunately, in Linux few developers have tried to provide the global menu themselves, which implies that to have a global menu what has been done is to hack the applications with techniques similar to this one: https://linuxhint.com/what-is-ld-library-path/ and insert the code into it to convert the window widget menu into a serialized menu which is then can be exported to DBus using the mentioned techniques.

The good part of all this is that in theory you don't have to do anything weird to export the menu, because in theory you only have to provide a menu bar and the modules that have been made to hack the applications and export the menu will take care of everything else.

Examples of modules:

Some other modules have already been integrated into the toolkit: https://codereview.qt-project.org/c/qt/qtbase/+/146234/

Upvotes: 2

Related Questions