From 855f21b1b311e99cbe933a9667d67891555adf86 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 17 Feb 2021 20:38:16 +0000 Subject: [PATCH] menu.c: parse menu.xml root-menu --- README.md | 13 ++-- src/menu/menu.c | 186 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 185 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ee13705c..5686627c 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,13 @@ ## 1. What is this? -Labwc is a [WIP] free, stacking compositor for Wayland based on wlroots. +Labwc is a [WIP] free, wlroots-based stacking compositor for Wayland. It has the following aims: - Be light-weight, small and fast - Have the look and feel of [openbox](https://github.com/danakj/openbox) albeit - with smaller feature set + with a smaller feature set - Where practicable, use clients to show wall-paper, take screenshots, and so on - Stay in keeping with wlroots and sway in terms of approach and coding style @@ -24,8 +24,6 @@ It is in early development, so expect bugs and missing features. Labwc has been inspired and influenced by [openbox](https://github.com/danakj/openbox), [sway](https://github.com/swaywm/sway), [cage](https://www.hjdskes.nl/blog/cage-01/), [wio](https://wio-project.org/) and [rootston](https://github.com/swaywm/rootston) -The following were considered before choosing wlroots: [qtwayland](https://github.com/qt/qtwayland), [grefsen](https://github.com/ec1oud/grefsen), [mir](https://mir-server.io) and [egmde](https://github.com/AlanGriffiths/egmde). - ![](https://raw.githubusercontent.com/wiki/johanmalm/labwc/images/scrot2.png) ## 2. Build @@ -64,7 +62,9 @@ See full details in the following: ## 4. Run - ./build/labwc -s + ./build/labwc [-s ] + +Click on the background to launch a menu. If you have not created an rc.xml configuration file, default keybinds will be: @@ -96,7 +96,7 @@ No acceptance criteria exists, but the following list indicates the inteded high - [x] Catch SIGHUP to re-load config file and theme - [x] Support layer-shell protocol ('exclusive' not yet implemented) - [x] Support damage tracking to reduce CPU usage -- [ ] Support root-menu and parse menu.xml (very simple implementation, no submenus yet) +- [x] Support root-menu and parse menu.xml (very simple implementation - no submenus, separators or titles) - [ ] Support 'maximize' - [ ] Support wlr-output-management protocol and [kanshi](https://github.com/emersion/kanshi.git) - [ ] Support foreign-toplevel protocol (e.g. to integrate with wlroots panels/bars) @@ -129,4 +129,5 @@ The following items are out-of-scope: - Any theme option (probably at least half of them) not required to reasonably render common themes - Any configuration option not required to provide a simple openbox-like experience - Multiple desktops +- Pipe-menus diff --git a/src/menu/menu.c b/src/menu/menu.c index bc260f8c..213ccfa3 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -1,17 +1,33 @@ #define _POSIX_C_SOURCE 200809L +#include #include +#include +#include +#include #include #include #include #include +#include +#include "common/buf.h" +#include "common/dir.h" #include "common/font.h" +#include "common/nodename.h" +#include "common/string-helpers.h" #include "labwc.h" #include "menu/menu.h" #include "theme/theme.h" static const char font[] = "Sans 8"; -#define MENUWIDTH (100) +static struct server *g_server; +static struct menu *g_menu; + +/* state-machine variables for processing */ +static bool in_item = false; +static struct menuitem *current_item; + +#define MENUWIDTH (120) #define MENUHEIGHT (25) #define MENU_PADDING_WIDTH (7) @@ -57,15 +73,12 @@ texture_create(struct server *server, struct wlr_box *geo, const char *text, } struct menuitem * -menuitem_create(struct server *server, struct menu *menu, const char *text, - const char *action, const char *command) +menuitem_create(struct server *server, struct menu *menu, const char *text) { struct menuitem *menuitem = calloc(1, sizeof(struct menuitem)); if (!menuitem) { return NULL; } - menuitem->action = action ? strdup(action) : NULL; - menuitem->command = command ? strdup(command) : NULL; menuitem->geo_box.width = MENUWIDTH; menuitem->geo_box.height = MENUHEIGHT; menuitem->active_texture = texture_create(server, &menuitem->geo_box, @@ -77,13 +90,170 @@ menuitem_create(struct server *server, struct menu *menu, const char *text, return menuitem; } +static void fill_item(char *nodename, char *content) +{ + string_truncate_at_pattern(nodename, ".item.menu"); + + if (getenv("LABWC_DEBUG_MENU_NODENAMES")) { + printf("%s: %s\n", nodename, content); + } + + /* + * Handle the following: + * + * + * + * + * + */ + if (!strcmp(nodename, "label")) { + current_item = menuitem_create(g_server, g_menu, content); + } + assert(current_item); + if (!strcmp(nodename, "name.action")) { + current_item->action = strdup(content); + } else if (!strcmp(nodename, "command.action")) { + current_item->command = strdup(content); + } +} + +static void +entry(xmlNode *node, char *nodename, char *content) +{ + static bool in_root_menu = false; + + if (!nodename) { + return; + } + string_truncate_at_pattern(nodename, ".openbox_menu"); + if (!content) { + return; + } + if (!strcmp(nodename, "id.menu")) { + in_root_menu = !strcmp(content, "root-menu") ? true : false; + } + + /* We only handle the root-menu for the time being */ + if (!in_root_menu) { + return; + } + if (in_item) { + fill_item(nodename, content); + } +} + +static void +process_node(xmlNode *node) +{ + char *content; + static char buffer[256]; + char *name; + + content = (char *)node->content; + if (xmlIsBlankNode(node)) { + return; + } + name = nodename(node, buffer, sizeof(buffer)); + entry(node, name, content); +} + +static void xml_tree_walk(xmlNode *node); + +static void +traverse(xmlNode *n) +{ + process_node(n); + for (xmlAttr *attr = n->properties; attr; attr = attr->next) { + xml_tree_walk(attr->children); + } + xml_tree_walk(n->children); +} + +static void +xml_tree_walk(xmlNode *node) +{ + for (xmlNode *n = node; n && n->name; n = n->next) { + if (!strcasecmp((char *)n->name, "comment")) { + continue; + } + if (!strcasecmp((char *)n->name, "item")) { + in_item = true; + traverse(n); + in_item = false; + continue; + } + traverse(n); + } +} + +static void +parse_xml(struct buf *b) +{ + xmlDoc *d = xmlParseMemory(b->buf, b->len); + if (!d) { + warn("xmlParseMemory()"); + exit(EXIT_FAILURE); + } + xml_tree_walk(xmlDocGetRootElement(d)); + xmlFreeDoc(d); + xmlCleanupParser(); +} + +static void +menu_read(void) +{ + FILE *stream; + char *line = NULL; + size_t len = 0; + struct buf b; + static char menuxml[4096] = { 0 }; + + if (!strlen(config_dir())) { + return; + } + snprintf(menuxml, sizeof(menuxml), "%s/menu.xml", config_dir()); + + stream = fopen(menuxml, "r"); + if (!stream) { + warn("cannot read (%s)", menuxml); + return; + } + info("read menu file (%s)", menuxml); + buf_init(&b); + while (getline(&line, &len, stream) != -1) { + char *p = strrchr(line, '\n'); + if (p) + *p = '\0'; + buf_add(&b, line); + } + free(line); + fclose(stream); + parse_xml(&b); + free(b.buf); +} + void menu_init(struct server *server, struct menu *menu) { + static bool has_run; + + if (has_run) { + goto not_first_run; + } + LIBXML_TEST_VERSION wl_list_init(&menu->menuitems); - menuitem_create(server, menu, "Terminal", "Execute", "sakura"); - menuitem_create(server, menu, "Reconfigure", "Reconfigure", NULL); - menuitem_create(server, menu, "Exit", "Exit", NULL); + g_server = server; + g_menu = menu; + +not_first_run: + menu_read(); + /* Default menu if no menu.xml found */ + if (!current_item) { + current_item = menuitem_create(server, menu, "Reconfigure"); + current_item->action = strdup("Reconfigure"); + current_item = menuitem_create(server, menu, "Exit"); + current_item->action = strdup("Exit"); + } menu_move(menu, 100, 100); }