diff --git a/README.md b/README.md
index 3a6f765..ced8dbb 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ Because \*box-style compositors are minimalist, most functionality is left to ex
* [wl-clipboard](https://wayland.emersion.fr/mako/): Access the clipboard in scripts (also used by [neovim](https://neovim.io/))
* Screenshots: [grim](https://git.sr.ht/~emersion/grim) and [slurp](https://github.com/emersion/slurp)
* Screen recording: [wf-recorder](https://github.com/ammen99/wf-recorder)
+* Menu support: [rofi-wayland](https://github.com/lbonn/rofi)
### Contact
I can be found as wiz on Rizon and wizbright on Libera.
diff --git a/data/menu b/data/menu
new file mode 100644
index 0000000..4c74c10
--- /dev/null
+++ b/data/menu
@@ -0,0 +1,44 @@
+#!/bin/env python
+
+import os, subprocess, sys
+import xml.dom.minidom as dom
+
+def get_waybox_pid(name='waybox'):
+ lines = subprocess.Popen(['pgrep', '-x', name], stdout=subprocess.PIPE).stdout.read().splitlines()
+ if lines:
+ return int(lines[0])
+ else:
+ return 0
+
+def get_menu_items(menu, indent):
+ print("%s⇢ %s\0nonselectable\x1ftrue\x1ficon\x1fgo-next-symbolic" % (indent, menu.getAttribute('label').replace('_', '') or menu.getAttribute('id')))
+ indent = indent + " "
+ children = menu.childNodes
+ for i in range(0, children.length):
+ if children[i].nodeType == dom.Node.ELEMENT_NODE and children[i].tagName == 'item':
+ action = children[i].getElementsByTagName('action')[0].getAttribute('name')
+ cmd = ""
+ icon = children[i].getAttribute('icon')
+ label = children[i].getAttribute('label').replace('_', '')
+ if action == 'Execute':
+ cmd = children[i].getElementsByTagName('execute')[0].firstChild.nodeValue
+ elif action == 'Exit':
+ cmd = "kill -s SIGTERM %d" % get_waybox_pid()
+ elif action == 'Reconfigure':
+ cmd = "kill -s SIGUSR2 %d" % get_waybox_pid()
+ print("%s %s\0info\x1f%s\x1ficon\x1f%s" % (indent, label, cmd, icon))
+ elif children[i].nodeType == dom.Node.ELEMENT_NODE and children[i].tagName == 'menu':
+ get_menu_items(children[i], indent)
+
+menu_xml = os.getenv("WB_MENU_XML") or "menu.xml"
+# If ran as a rofi script (not possible with wofi)
+if not os.getenv('ROFI_RETV') is None:
+ import shlex
+ print("\0message\x1f%s" % os.path.abspath(menu_xml))
+ if not (info := os.getenv('ROFI_INFO')) is None:
+ subprocess.Popen(shlex.split(info), stdout=subprocess.PIPE)
+ sys.exit(0)
+
+document = dom.parse(menu_xml)
+root_menu = document.getElementsByTagName('menu')[0]
+get_menu_items(root_menu, "")
diff --git a/data/meson.build b/data/meson.build
index a5a233b..c092cba 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -16,6 +16,7 @@ configure_file(
scripts = files(
'autostart',
'environment',
+ 'menu',
'xdg-autostart',
)
diff --git a/data/rc.xml b/data/rc.xml
index ea2b38a..5bff008 100644
--- a/data/rc.xml
+++ b/data/rc.xml
@@ -69,6 +69,11 @@
grim -g "$(slurp -d)" "$(xdg-user-dir PICTURES)/$(date +'%Y-%m-%d-%H%M%S_grim_sel.png')"
+
+
+ rofi -show menu:$WB_CONF_DIR/menu
+
+
obrun l
diff --git a/data/waybox.sh b/data/waybox.sh
index 23c2391..01448ce 100644
--- a/data/waybox.sh
+++ b/data/waybox.sh
@@ -72,6 +72,26 @@ then
WB_XDG_AUTOSTART="@libexecdir@/openbox-autostart OPENBOX";
fi
+if test -f $WB_USER_CONF_DIR/menu.xml;
+then
+ WB_MENU_XML=$WB_USER_CONF_DIR/menu.xml
+elif test -f $WB_SYS_CONF_DIR/menu.xml;
+then
+ WB_MENU_XML=$WB_SYS_CONF_DIR/menu.xml
+elif test -f $OB_USER_CONF_DIR/menu.xml;
+then
+ _ "WARNING: Using files from Openbox. These may not work correctly."
+ WB_MENU_XML=$OB_USER_CONF_DIR/menu.xml
+elif test -f $OB_SYS_CONF_DIR/menu.xml;
+then
+ _ "WARNING: Using files from Openbox. These may not work correctly."
+ WB_MENU_XML=$OB_SYS_CONF_DIR/menu.xml;
+else
+ _ "ERROR: No menu file found." >&2
+ exit 1
+fi
+export WB_MENU_XML
+
if test -f $WB_USER_CONF_DIR/rc.xml;
then
WB_RC_XML=$WB_USER_CONF_DIR/rc.xml
diff --git a/waybox/layer_shell.c b/waybox/layer_shell.c
index 78ec531..c204099 100644
--- a/waybox/layer_shell.c
+++ b/waybox/layer_shell.c
@@ -310,6 +310,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
if (layer_surface->output == NULL) {
struct wb_server *server =
wl_container_of(listener, server, new_layer_surface);
+ if (wl_list_length(&server->toplevels) == 0) return;
struct wb_toplevel *toplevel =
wl_container_of(server->toplevels.next, toplevel, link);
layer_surface->output = get_active_output(toplevel);