mirror of
https://github.com/labwc/labwc.git
synced 2026-04-11 08:21:13 -04:00
[experiment] support menu.yaml
This commit is contained in:
parent
85b6e25484
commit
52a1c1c880
4 changed files with 119 additions and 33 deletions
|
|
@ -106,13 +106,23 @@ ID attributes are unique. Duplicates are ignored.
|
||||||
When writing pipe menu scripts, make sure to escape XML special characters such
|
When writing pipe menu scripts, make sure to escape XML special characters such
|
||||||
as "&" ("&"), "<" ("<"), and ">" (">").
|
as "&" ("&"), "<" ("<"), and ">" (">").
|
||||||
|
|
||||||
|
|
||||||
# LOCALISATION
|
# LOCALISATION
|
||||||
|
|
||||||
Available localisation for the default "client-menu" is only shown if no
|
Available localisation for the default "client-menu" is only shown if no
|
||||||
"client-menu" is present in menu.xml. Any menu definition in menu.xml is
|
"client-menu" is present in menu.xml. Any menu definition in menu.xml is
|
||||||
interpreted as a user-override.
|
interpreted as a user-override.
|
||||||
|
|
||||||
|
# YAML SUPPORT
|
||||||
|
|
||||||
|
Like rc.yaml, labwc supports menu.yaml instead of menu.xml. See labwc-config(5)
|
||||||
|
for its syntax. Note that following keys in singular form can be expressed as
|
||||||
|
plural form in menu.yaml:
|
||||||
|
|
||||||
|
- *menus* (converted to *menu*)
|
||||||
|
- *items* (converted to *item*)
|
||||||
|
|
||||||
|
See /usr/share/docs/labwc/menu.yaml for an example menu configuration in YAML.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
labwc(1), labwc-actions(5), labwc-config(5), labwc-theme(5)
|
labwc(1), labwc-actions(5), labwc-config(5), labwc-theme(5)
|
||||||
|
|
|
||||||
33
docs/menu.yaml
Normal file
33
docs/menu.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
menu:
|
||||||
|
- id: client-menu
|
||||||
|
item:
|
||||||
|
label: Minimize
|
||||||
|
action: { name: Iconify }
|
||||||
|
item:
|
||||||
|
label: Maximize
|
||||||
|
action: { name: ToggleMaximize }
|
||||||
|
menu:
|
||||||
|
id: workspaces
|
||||||
|
label: Workspace
|
||||||
|
item:
|
||||||
|
label: Move Left
|
||||||
|
action: { name: SendToDesktop, to: left }
|
||||||
|
item:
|
||||||
|
label: Move Right
|
||||||
|
action: { name: SendToDesktop, to: right }
|
||||||
|
separator:
|
||||||
|
item:
|
||||||
|
label: Always on Visible Workspace
|
||||||
|
action: { name: ToggleOmnipresent }
|
||||||
|
item:
|
||||||
|
label: Close
|
||||||
|
action: { name: Close }
|
||||||
|
|
||||||
|
- id: root-menu
|
||||||
|
items:
|
||||||
|
- label: Terminal
|
||||||
|
action: { name: Execute, command: alacritty }
|
||||||
|
- label: Reconfigure
|
||||||
|
action: { name: Reconfigure }
|
||||||
|
- label: Exit
|
||||||
|
action: { name: Exit }
|
||||||
|
|
@ -82,6 +82,10 @@ process_value(yaml_parser_t *parser, struct buf *b,
|
||||||
key_name = "font";
|
key_name = "font";
|
||||||
} else if (!strcasecmp(key_name, "contexts")) {
|
} else if (!strcasecmp(key_name, "contexts")) {
|
||||||
key_name = "context";
|
key_name = "context";
|
||||||
|
} else if (!strcasecmp(key_name, "items")) {
|
||||||
|
key_name = "item";
|
||||||
|
} else if (!strcasecmp(key_name, "menus")) {
|
||||||
|
key_name = "menu";
|
||||||
}
|
}
|
||||||
if (parent_name) {
|
if (parent_name) {
|
||||||
buf_add_fmt(b, "<%s>", parent_name);
|
buf_add_fmt(b, "<%s>", parent_name);
|
||||||
|
|
|
||||||
103
src/menu/menu.c
103
src/menu/menu.c
|
|
@ -26,6 +26,10 @@
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
|
||||||
|
#if HAVE_LIBYAML
|
||||||
|
#include "common/yaml2xml.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */
|
#define PIPEMENU_MAX_BUF_SIZE 1048576 /* 1 MiB */
|
||||||
#define PIPEMENU_TIMEOUT_IN_MS 4000 /* 4 seconds */
|
#define PIPEMENU_TIMEOUT_IN_MS 4000 /* 4 seconds */
|
||||||
|
|
||||||
|
|
@ -566,6 +570,35 @@ is_toplevel_static_menu_definition(xmlNode *n, char *id)
|
||||||
return id && nr_parents(n) == 2;
|
return id && nr_parents(n) == 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_property(xmlNode *n, const char *name)
|
||||||
|
{
|
||||||
|
/* First, search from attributes */
|
||||||
|
char *prop = (char *)xmlGetProp(n, (const xmlChar *)name);
|
||||||
|
if (prop) {
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then search from child nodes */
|
||||||
|
xmlNode *child;
|
||||||
|
for (child = n->children; child && child->name;
|
||||||
|
child = child->next) {
|
||||||
|
if (!strcmp((char *)child->name, name)) {
|
||||||
|
goto found_child_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
found_child_node:
|
||||||
|
for (child = child->children; child && child->name;
|
||||||
|
child = child->next) {
|
||||||
|
if (child->type == XML_TEXT_NODE) {
|
||||||
|
return xstrdup((char *)child->content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* <menu> elements have three different roles:
|
* <menu> elements have three different roles:
|
||||||
* * Definition of (sub)menu - has ID, LABEL and CONTENT
|
* * Definition of (sub)menu - has ID, LABEL and CONTENT
|
||||||
|
|
@ -575,9 +608,9 @@ is_toplevel_static_menu_definition(xmlNode *n, char *id)
|
||||||
static void
|
static void
|
||||||
handle_menu_element(xmlNode *n, struct server *server)
|
handle_menu_element(xmlNode *n, struct server *server)
|
||||||
{
|
{
|
||||||
char *label = (char *)xmlGetProp(n, (const xmlChar *)"label");
|
char *label = get_property(n, "label");
|
||||||
char *execute = (char *)xmlGetProp(n, (const xmlChar *)"execute");
|
char *execute = get_property(n, "execute");
|
||||||
char *id = (char *)xmlGetProp(n, (const xmlChar *)"id");
|
char *id = get_property(n, "id");
|
||||||
|
|
||||||
if (execute && label && id) {
|
if (execute && label && id) {
|
||||||
wlr_log(WLR_DEBUG, "pipemenu '%s:%s:%s'", id, label, execute);
|
wlr_log(WLR_DEBUG, "pipemenu '%s:%s:%s'", id, label, execute);
|
||||||
|
|
@ -676,7 +709,7 @@ error:
|
||||||
static void
|
static void
|
||||||
handle_separator_element(xmlNode *n)
|
handle_separator_element(xmlNode *n)
|
||||||
{
|
{
|
||||||
char *label = (char *)xmlGetProp(n, (const xmlChar *)"label");
|
char *label = get_property(n, "label");
|
||||||
current_item = separator_create(current_menu, label);
|
current_item = separator_create(current_menu, label);
|
||||||
free(label);
|
free(label);
|
||||||
}
|
}
|
||||||
|
|
@ -725,36 +758,13 @@ parse_buf(struct server *server, struct buf *buf)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static bool
|
||||||
* @stream can come from either of the following:
|
parse_menu_file(const char *filename, struct server *server)
|
||||||
* - fopen() in the case of reading a file such as menu.xml
|
|
||||||
* - popen() when processing pipemenus
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
parse_stream(struct server *server, FILE *stream)
|
|
||||||
{
|
|
||||||
char *line = NULL;
|
|
||||||
size_t len = 0;
|
|
||||||
struct buf b = BUF_INIT;
|
|
||||||
|
|
||||||
while (getline(&line, &len, stream) != -1) {
|
|
||||||
char *p = strrchr(line, '\n');
|
|
||||||
if (p) {
|
|
||||||
*p = '\0';
|
|
||||||
}
|
|
||||||
buf_add(&b, line);
|
|
||||||
}
|
|
||||||
free(line);
|
|
||||||
parse_buf(server, &b);
|
|
||||||
buf_reset(&b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
parse_xml(const char *filename, struct server *server)
|
|
||||||
{
|
{
|
||||||
struct wl_list paths;
|
struct wl_list paths;
|
||||||
paths_config_create(&paths, filename);
|
paths_config_create(&paths, filename);
|
||||||
|
|
||||||
|
bool file_found = false;
|
||||||
bool should_merge_config = rc.merge_config;
|
bool should_merge_config = rc.merge_config;
|
||||||
struct wl_list *(*iter)(struct wl_list *list);
|
struct wl_list *(*iter)(struct wl_list *list);
|
||||||
iter = should_merge_config ? paths_get_prev : paths_get_next;
|
iter = should_merge_config ? paths_get_prev : paths_get_next;
|
||||||
|
|
@ -765,14 +775,37 @@ parse_xml(const char *filename, struct server *server)
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
file_found = true;
|
||||||
wlr_log(WLR_INFO, "read menu file %s", path->string);
|
wlr_log(WLR_INFO, "read menu file %s", path->string);
|
||||||
parse_stream(server, stream);
|
|
||||||
|
struct buf b = BUF_INIT;
|
||||||
|
if (HAVE_LIBYAML && str_endswith(path->string, ".yaml")) {
|
||||||
|
#if HAVE_LIBYAML
|
||||||
|
b = yaml_to_xml(stream, "openbox_menu");
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
while (getline(&line, &len, stream) != -1) {
|
||||||
|
char *p = strrchr(line, '\n');
|
||||||
|
if (p) {
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
buf_add(&b, line);
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
fclose(stream);
|
fclose(stream);
|
||||||
|
if (b.len > 0) {
|
||||||
|
parse_buf(server, &b);
|
||||||
|
}
|
||||||
|
buf_reset(&b);
|
||||||
if (!should_merge_config) {
|
if (!should_merge_config) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
paths_destroy(&paths);
|
paths_destroy(&paths);
|
||||||
|
return file_found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -983,7 +1016,13 @@ void
|
||||||
menu_init(struct server *server)
|
menu_init(struct server *server)
|
||||||
{
|
{
|
||||||
wl_list_init(&server->menus);
|
wl_list_init(&server->menus);
|
||||||
parse_xml("menu.xml", server);
|
bool file_found = parse_menu_file("menu.xml", server);
|
||||||
|
(void)file_found;
|
||||||
|
#if HAVE_LIBYAML
|
||||||
|
if (!file_found) {
|
||||||
|
parse_menu_file("menu.yaml", server);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
init_rootmenu(server);
|
init_rootmenu(server);
|
||||||
init_windowmenu(server);
|
init_windowmenu(server);
|
||||||
post_processing(server);
|
post_processing(server);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue