Configure hyprbaric
Global menu
How hyprbaric reads application menus, the two session settings the setup guide will not write, and the extra steps Firefox, Java, and traditional GTK apps need.
The global menu moves the focused application's menu bar onto hyprbaric, the way a macOS menu bar does. Windows that export no menu keep showing their title instead. The feature is opt-in because owning the desktop's AppMenu registrar name tells applications to stop drawing their own menu bar: if hyprbaric claimed that name without rendering the menus, those applications would have no menu bar anywhere.
TOML keys and the compositor plugin live in Configuration reference. This page is the end-to-end path: how a menu reaches the bar, which toolkits just work, and which ones still need a setting in the application or in your session.
Navigating menus
Submenus can open further submenus without a fixed depth limit. Long panels scroll vertically; cascades wider than the window scroll horizontally and reveal the newly opened panel. Choosing a different ancestor closes its descendants.
Menus belong to the window that exported them. Switching windows replaces the session and closes its popups, including when both windows belong to the same application. A delayed response or click from an old session cannot act on the newly focused window.
A window with no exported menu and requests overtaken by a focus change are normal outcomes, logged at debug level. Actual discovery or protocol failures remain visible in the logs.
Turn it on
Both halves share one switch. Enable the module, and hyprbaric loads the companion plugin before claiming com.canonical.AppMenu.Registrar. If installation or compatibility checks fail, it leaves the registrar unowned so applications can retain their own menu bars. The plugin checks its build hash against the running compositor before accessing compositor state:
[global_menu]
enabled = true
[modules.global_menu]
enabled = true
The Modules tab in Settings writes the second table. The setup guide can turn the same switch on during first launch. It does not write Hyprland config: click-to-focus and the GTK AppMenu module stay in hyprland.conf, on purpose. Restart applications that were already running: they decide whether to export a menu at startup, and they keep whichever menu bar they started with.
Turning the feature off and restarting hyprbaric releases the registrar name. Applications started after that draw their own menu bars again. Do not leave the registrar owned by a session that is not rendering menus.
User-driven session changes
hyprbaric does not write your compositor config. The setup guide only flips the module switch; it will not set these. Skip them and the feature feels broken even when the bar is doing its job:
| If you skip | What you see |
|---|---|
Click-to-focus (follow_mouse = 0) | The menu vanished on the way to the bar. Hover-focus steals the window as the pointer crosses tiles and floating palettes. |
appmenu-gtk-module in the session | GIMP and other classic GtkMenuBar apps keep their in-window bar, and hyprbaric shows the title. Nothing was published for the companion to capture. |
Firefox prefs stay in the browser; they are listed under Firefox. Java needs a per-JVM agent, listed under Java / JAyatana. JetBrains IDEs on current Wayland builds do not export a menu; that is under JetBrains.
Click to focus
The bar shows the focused application's menu. Hyprland's default is that the window under the cursor becomes focused as soon as the mouse moves onto it. Reaching a top bar then focuses whatever tiled window or floating palette sits between you and the headings, and the menu you were aiming for disappears.
Turn hover-focus off so focus only changes when you click a window or use a keyboard shortcut:
input {
follow_mouse = 0
float_switch_override_focus = 0
}
hl.config({
input = {
follow_mouse = 0,
float_switch_override_focus = 0,
},
})
follow_mouse = 0 stops ordinary hover from stealing keyboard focus. float_switch_override_focus = 0 is required as well: Hyprland otherwise still refocuses when the cursor crosses between a tiled window and a floating one, which is the usual path up to the bar past a dialog or a tool window.
Apply immediately without a compositor restart:
# hyprlang (.conf)
hyprctl keyword input:follow_mouse 0
hyprctl keyword input:float_switch_override_focus 0
# Lua
hyprctl eval 'hl.config({ input = { follow_mouse = 0, float_switch_override_focus = 0 } })'
Treat this as part of turning the global menu on. Hover-focus and a menu bar that tracks the focused window fight each other; click-to-focus is the model the bar is built for.
GTK AppMenu module
GIMP is the usual report. It packs a classic GtkMenuBar into the window and does not speak gtk_shell1 on its own. Until appmenu-gtk-module is in that process, the bar can only show GIMP's title. Install a GTK module that hijacks the menubar and exports DBusMenu / gtk-shell properties, then inject it into every GTK process Hyprland starts.
The module name in GTK_MODULES is always appmenu-gtk-module (GTK loads libappmenu-gtk-module.so). That is not the package name.
| Distro | Package | Notes |
|---|---|---|
| Arch extra | appmenu-gtk-module | Current builds enable Wayland (run-on-wayland defaults to true) and talk to gtk_shell1. |
| AUR | appmenu-gtk-module-wayland | Alternative that speaks org_kde_kwin_appmenu instead. Still set GTK_MODULES to appmenu-gtk-module. |
| Debian / Ubuntu | appmenu-gtk3-module | XWayland path unless you also have a Wayland port. |
Add both variables to the compositor environment. UBUNTU_MENUPROXY=1 is what tells the module to actually steal the menubar; without it the module loads and does nothing. If GTK_MODULES is already set, append :appmenu-gtk-module rather than replacing the existing list.
env = GTK_MODULES,appmenu-gtk-module
env = UBUNTU_MENUPROXY,1
hl.env("GTK_MODULES", "appmenu-gtk-module")
hl.env("UBUNTU_MENUPROXY", "1")
Hyprland applies env / hl.env when the compositor starts, not on hyprctl reload. Restart the session, then restart the GTK application. A working Wayland capture is a dbusmenu or gtk row for that window in hyprctl -j hyprbaric-appmenu.
Push the same pair into systemd user activation if you launch GTK apps from .desktop files or other user services:
exec-once = systemctl --user set-environment GTK_MODULES=appmenu-gtk-module UBUNTU_MENUPROXY=1
exec-once = dbus-update-activation-environment --systemd GTK_MODULES UBUNTU_MENUPROXY
The stock X11-only module is not enough for a Wayland-native GTK window: it registers with com.canonical.AppMenu.Registrar using an X11 window id, and never calls org_kde_kwin_appmenu. Arch appmenu-gtk-module 25.04 and the AUR Wayland port both know about Wayland. Pick one; do not install both over the same .so.
How a menu reaches the bar
Three pieces have to agree, in this order:
- The application exports a menu on the session bus, either as
com.canonical.dbusmenuor as GTK'sorg.gtk.Menus. Whether it does that, and which protocol it uses, is a property of the toolkit and of settings inside the app. hyprbaric cannot invent a menu that was never published. - The compositor companion (
hyprbaric-appmenu) associates that export with a Hyprland window. Wayland-native Qt, Firefox, and GtkApplication menubars call intoorg_kde_kwin_appmenuorgtk_shell1, which stock Hyprland does not implement; the companion does. GTK and Qt often publish the D-Bus endpoint before Hyprland has aCWindowfor that surface, so the companion does not freeze the window address at that first request: it resolves the surface again when you queryhyprctl hyprbaric-appmenu, and GTK also retries onpresent. If a row still has noaddress, hyprbaric will use it anyway when it is the only exported menu belonging to the focused process. Traditional GTK menus injected byappmenu-gtk-moduleon X11 never make that Wayland call. Hyprbaric first joins acom.canonical.AppMenu.Registrarentry by X11 window id. If the module did not register there, it reads_GTK_UNIQUE_BUS_NAMEand_GTK_MENUBAR_OBJECT_PATH(or_GTK_APP_MENU_OBJECT_PATH) from that X11 window withxprop. Additional_NAME_OBJECT_PATHproperties advertise the correspondingname.action scope; the bar validates their paths and uses them as scope-to-object mappings for the GTK action client. A registrar process-id lookup remains a fallback when Hyprland cannot name an X11 window. A Save / Open / Preferences dialog that takes focus is walked to its mapped parent (kind: parentin that JSON); File / Edit stay on the bar when the document window still owns the menu. - The bar reads the focused window's endpoint over D-Bus, lazily. Headings appear with the window; rows load when you open a heading. Qt fills those rows in response to
AboutToShow. Firefox also needs the D-BusMenuopenedevent, and it then rebuilds its menu tree under new identifiers. The bar keeps the heading labels from the last read and opens the live node by that path, so a second click still reaches File after the first click caused a rebuild. Dismissing a heading or flyout sends the matchingclosedevent so Firefox and Qt drop that popup state. Toggling a checkmark or radio leaves the menu open; while it is up, D-BusMenu layout and property updates re-read that menu so the marks redraw in place.
A successful Wayland capture looks like this, with the focused window's Hyprland address in address:
hyprctl -j hyprbaric-appmenu
[
{
"address": "0x123456789abc",
"kind": "dbusmenu",
"service": "org.kde.kate-12345",
"path": "/MenuBar/1"
}
]
kind is gtk for GtkApplication menubars and dbusmenu for Qt, Firefox, and GTK menus that went through AppMenu. XWayland windows also appear as kind: x11 records so the registrar can be matched when the application never spoke the Wayland protocol. Dialogs appear as kind: parent pointing at the document window.
What works without extra setup
Restart the application after enabling the module. That is the whole user step for:
| Kind | Examples | Protocol |
|---|---|---|
| Qt and KDE | Kate, KWrite, Dolphin, Qt Creator | org_kde_kwin_appmenu + DBusMenu |
GTK GtkApplication menubars | GIMP 3, LibreOffice with SAL_USE_VCLPLUGIN=gtk3, and other apps that call gtk_application_set_menubar() | gtk_shell1 + org.gtk.Menus |
Qt asks whether com.canonical.AppMenu.Registrar is owned on the session bus, on Wayland as well as on X11. That is why hyprbaric owns the name even though Wayland clients publish their endpoint through the companion rather than through the registrar.
GtkApplication menus are joined to org.gtk.Actions the way GTK's own menu tracker joins them in-process. Disabled actions are disabled, boolean state is a checkmark, a target that matches state is the selected radio, and Activate sends that target so parameterized rows do something. Keyboard shortcuts appear when the menu item carries the accel attribute (<Primary>q becomes Ctrl+Q). Shortcuts registered only with gtk_application_set_accels_for_action are not on the bus, so they do not appear.
GTK3 can publish both an application menu (gtk_application_set_app_menu) and a menubar. Those are two org.gtk.Menus objects. When the companion sees both, the bar prepends the application menu as one heading named from the window, then File / Edit / … GTK4 removed set_app_menu; a menubar-only or app-menu-only export stays a single path, as before. The bar does not invent a stub Application menu when the toolkit exported nothing.
Firefox
Firefox does not export a menu until you turn the feature on in the browser. The prefs are false by default.
- Confirm Firefox is Wayland-native, not XWayland.
about:supportshould list Window Protocol aswayland. If it saysx11, start Firefox without forcing XWayland (MOZ_ENABLE_WAYLAND=1is the usual override). - Open
about:config. - Set both of these to
true:widget.gtk.global-menu.enabledwidget.gtk.global-menu.wayland.enabled
- Restart Firefox.
After that, focusing a Firefox window should produce a kind: dbusmenu row in hyprctl -j hyprbaric-appmenu, and File / Edit / View should appear on the bar. Opening a heading asks Firefox to fill that menu; Firefox then issues new native identifiers for the whole tree. The bar keeps opening by heading label, not by the id from the first read, so File still works after History has been opened. A third pref, widget.gtk.native-context-menus, is unrelated: it changes in-page context menus, not the application menu bar.
These prefs are Mozilla's experimental GTK global-menu path. Some Firefox versions have been laggy with them enabled; if the browser stutters after the restart, turn both prefs back to false and restart again. The bar will show the window title instead, which is the same fallback used for any app that exports no menu.
Java / JAyatana
A Swing JMenuBar does not speak org_kde_kwin_appmenu or gtk_shell1. JAyatana is an X11-only agent that reads that bar, publishes com.canonical.dbusmenu at /com/canonical/menu/<xid>, and registers the X11 window id with com.canonical.AppMenu.Registrar. hyprbaric already consumes that table as kind: x11. The jars are not part of hyprbaric; install them yourself and inject the agent into the JVM that draws the menu.
- Install JAyatana. On Arch that is the AUR package
vala-panel-appmenu-jayatana-git. Upstream is thesubprojects/jayatanatree of vala-panel-appmenu. Distro installs put the agent at/usr/share/java/jayatanaag.jar. - Force that JVM onto X11. JAyatana looks up an X window id through JAWT; a native Wayland toolkit has none. For a JetBrains Runtime used as a plain JVM (not the IDE), that is
-Dawt.toolkit.name=XToolkit.hyprctl activewindow -jshould then report"xwayland": true. - Inject the agent into that JVM, then restart the application:
JAYATANA_FORCE=1 java \
-Dawt.toolkit.name=XToolkit \
--enable-native-access=ALL-UNNAMED \
-javaagent:/usr/share/java/jayatanaag.jar \
-jar your-app.jar
JAYATANA_FORCE=1 is what makes the agent run outside Ubuntu's old Unity session checks. --enable-native-access=ALL-UNNAMED is required on recent JBRs; older OpenJDKs ignore it. Do not put the agent in _JAVA_OPTIONS or JAVA_TOOL_OPTIONS for the whole session: a missing jar then breaks every java on the machine, including the one that builds the agent.
After a successful registration, busctl --user call com.canonical.AppMenu.Registrar /com/canonical/AppMenu/Registrar com.canonical.AppMenu.Registrar GetMenus grows a (xid, destination, /com/canonical/menu/…) row, hyprctl -j hyprbaric-appmenu shows a matching kind: x11 record for that xid, and File / Edit appear on the bar once that window is focused. Opening a heading still goes through D-BusMenu AboutToShow, the same as Qt.
JetBrains
JetBrains IDEs from 2026.1 default to native Wayland (-Dawt.toolkit.name=auto becomes WLToolkit when a Wayland display is available). That window has no X11 id, so JAyatana cannot register it, and the IDE does not call org_kde_kwin_appmenu either. Focusing IntelliJ or RustRover then leaves the bar on the window title. That is expected, and you do not need JAyatana installed to confirm it: hyprctl activewindow -j reports "xwayland": false, GetMenus does not grow a row, and hyprctl -j hyprbaric-appmenu has no dbusmenu / gtk record for that address.
Forcing X11 without JAyatana does not recover File / Edit. -Dawt.toolkit.name=XToolkit makes the welcome window XWayland, so the companion lists kind: x11 the way it does for every XWayland client, but the registrar still has no matching xid. The old linux.native.menu / libdbm64.so Unity exporter is not in the 2026.1 install; idea.log never mentions it. linux.native.menu=true in idea.properties is a no-op on that build.
JAyatana is a Swing exporter for apps that still draw a JMenuBar. Current JetBrains chrome is not that path. Do not add the agent to idea64.vmoptions expecting the 2026.1 menu bar to move onto hyprbaric.
Electron and Chromium
hyprbaric already speaks the two protocols a global menu needs: KDE's org_kde_kwin_appmenu on Wayland, and Canonical's AppMenu registrar on X11. Electron does not use either of those on a Wayland session. Its Linux menu export still lives in an X11-only libdbusmenu path keyed by an X window id (electron#34335, still open). Restarting hyprbaric or the Electron app does not change that.
Chromium itself grew Wayland AppMenu support upstream. Chrome or Ungoogled Chromium on a recent enough build can show up as a dbusmenu row. Electron has not shipped that Chromium work, so VS Code, Discord, Slack, Signal, Obsidian, Spotify, and other Electron shells stay on the title fallback while they run as native Wayland windows.
Two separate ceilings:
| App | Native Wayland | XWayland |
|---|---|---|
Electron with a real Menu.setApplicationMenu (VS Code with the native title bar, some IDEs) | No export. Title on the bar is expected. | Registers with the AppMenu registrar. The bar can show File / Edit / … after a restart of that app, the same as X11 GTK. |
| Electron with in-app chrome only (Discord, Slack, Spotify, most messengers) | Nothing to export. | Still nothing to export. There is no menubar in the process. |
Force an Electron app onto XWayland only if that app actually has a menubar you want on the bar, for example:
# one run
ELECTRON_OZONE_PLATFORM_HINT=x11 code
# or
code --ozone-platform=x11
hyprctl activewindow -j should then report "xwayland": true. If File still does not appear, the app is not calling Menu.setApplicationMenu (VS Code's custom title bar is the usual reason) and there is nothing hyprbaric can read.
Do not treat this as a missing hyprbaric protocol. When Electron binds org_kde_kwin_appmenu the way Firefox and Qt already do, those apps will show up without a bar change.
Traditional GTK menus
GIMP 3 and LibreOffice (gtk3 VCL) are GtkApplications: they publish org.gtk.Menus through gtk_shell1, and File / Edit should appear once the companion has a kind: gtk row for that window. LibreOffice on Wayland often defaults to a VCL that does not; start Writer as SAL_USE_VCLPLUGIN=gtk3 soffice --writer and restart it after the companion is loaded.
Older programs that pack a classic GtkMenuBar into the window do not speak gtk_shell1 on their own. GTK only publishes org.gtk.Menus for GMenuModel menubars. A GtkMenuBar stays in the window unless a GTK module hijacks it and exports it.
That injection is a compositor environment change, not a hyprbaric TOML key. Package, GTK_MODULES, and UBUNTU_MENUPROXY are under GTK AppMenu module. After those are in the session, restart the GTK application.
XWayland GTK
Applications running through XWayland can use the same GTK_MODULES=appmenu-gtk-module line. Hyprbaric matches a Canonical AppMenu registration to the focused X11 id when one exists. Some applications, including Unity Editor, instead publish org.gtk.Menus through X11 _GTK_* properties without a registrar entry. Hyprbaric reads those properties when the registrar lookup misses, then derives any additional action scope from the published _NAME_OBJECT_PATH property name. This is the same lookup for every XWayland GTK exporter; Unity Editor is one example. This fallback needs xprop (xorg-xprop on Arch, x11-utils on Debian/Ubuntu) installed on the bar host. If neither export exists, the window title remains visible.
The application must actually be an XWayland client (xwayland: true in hyprctl activewindow -j); a Wayland-native GTK app will not use the X11 registrar path.
Prefer a Wayland-capable module (Arch extra appmenu-gtk-module 25.04+, or the AUR Wayland port) for apps that can run natively. The XWayland registrar path is the fallback for toolkits or versions that never learned AppMenu.
Headerbars have nothing to export
Nautilus, Evince, most libadwaita applications, and anything that replaced its menubar with a headerbar or an in-window hamburger do not publish a menubar at all. No module can invent one. The bar keeps the window title, and that is expected.
Verify a window
Focus the application, then:
# Companion capture, including XWayland window ids
hyprctl -j hyprbaric-appmenu
# Registrar name Qt (and the GTK X11 module) look for
busctl --user status com.canonical.AppMenu.Registrar
# Whether the focused window is Wayland or XWayland
hyprctl activewindow -j
| Observation | Meaning |
|---|---|
| Headings change as the pointer travels to the bar | Hover-focus stole the window. This is click-to-focus, not a missing export. |
GIMP 2 or another classic GtkMenuBar app still shows its title | appmenu-gtk-module is not in that process. See GTK AppMenu module, then restart the application. |
| GIMP 3 or LibreOffice Writer still shows its title | The companion has no kind: gtk row for that window. Restart the app after the plugin is loaded. Writer also needs SAL_USE_VCLPLUGIN=gtk3 on Wayland. |
| A Swing app still shows its title | JAyatana is not in that JVM, or the JVM is native Wayland. See Java / JAyatana. |
| A Save / Open / Preferences dialog shows its title | The dialog took focus and its parent did not export a menu, or Hyprland has no mapped parent for it. |
hyprctl hyprbaric-appmenu is unknown request | The companion plugin is not loaded. See how the companion is obtained. |
The JSON is [], or has no row for this window | The application is not exporting a menu. Check toolkit, Firefox prefs, GTK_MODULES, a Java agent, and that the app was started after those were set. |
| A JetBrains IDE shows its title | Native Wayland, or X11 without a registrar match. See JetBrains. A kind: x11 row alone is the XWayland window, not a menu. |
A dbusmenu or gtk row exists, but has no address | The application published before Hyprland named the window. The bar still binds when the companion can resolve the surface, or when that row is the only menu from the focused process. Two address-less windows from the same process cannot be told apart. |
A dbusmenu or gtk row exists with an address that is not the focused window | The export belongs to another window of that application. Restart the application so it publishes against the live companion, then focus the window that owns the menubar. |
busctl says the registrar name has no owner | hyprbaric is not running with the module enabled, or another step failed before the registrar started. Qt will draw its own menu bar. |
busctl shows an owner that is not hyprbaric | Another desktop already claimed the registrar. Qt still exports, but X11 GTK registrations go to that owner and hyprbaric cannot read them. |
Logs for the Rust half:
RUST_LOG=hyprbaric::global_menu=debug,info hyprbaric
Applications already running
Every path above is decided when the process starts:
- Qt checks for the registrar name at startup.
- Firefox reads
about:configat startup. - GTK modules are loaded from
GTK_MODULESat startup. - JAyatana is a
-javaagenton the JVM that starts the window.
Enabling the bar module, flipping a Firefox pref, adding env = GTK_MODULES,…, or injecting a Java agent does nothing to a process that is already open. Restart that application.
Related configuration
The [global_menu] table only chooses where the companion plugin comes from. Showing the menu on the bar is [modules.global_menu]. Both are documented with their defaults in the configuration reference.
