From dfdb4e4b0e49034689277f3b29fac12d7c93ae35 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 29 Aug 2021 14:22:49 -0400 Subject: [PATCH] First implemenation of in rc.xml Can successfully parse the following XML and and implement the action: The XML parsing code for this looks A LOT different than the already existing XML parsing code. It may have to be reworked --- include/config/mousebind.h | 41 ++++++++ include/config/rcxml.h | 1 + src/config/meson.build | 1 + src/config/mousebind.c | 90 +++++++++++++++++ src/config/rcxml.c | 193 +++++++++++++++++++++++++++++++++++++ src/cursor.c | 10 +- 6 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 include/config/mousebind.h create mode 100644 src/config/mousebind.c diff --git a/include/config/mousebind.h b/include/config/mousebind.h new file mode 100644 index 00000000..6fb57ac4 --- /dev/null +++ b/include/config/mousebind.h @@ -0,0 +1,41 @@ +#ifndef __LABWC_MOUSEBIND_H +#define __LABWC_MOUSEBIND_H + +#include + +enum mouse_context { + MOUSE_CONTEXT_TITLEBAR, + MOUSE_CONTEXT_NONE +}; + +enum mouse_button { + /* + * These values match the values returned by button event->button and were + * obtained experimentally + */ + MOUSE_BUTTON_LEFT = 272, + MOUSE_BUTTON_RIGHT = 273, + MOUSE_BUTTON_MIDDLE = 274, + MOUSE_BUTTON_NONE = -1 +}; + +enum action_mouse_did { + MOUSE_ACTION_DOUBLECLICK, + MOUSE_ACTION_NONE +}; + +struct mousebind { + enum mouse_context context; /* ex: titlebar */ + enum mouse_button button; /* ex: left, right, middle */ + enum action_mouse_did mouse_action; /* ex: doubleclick, press, drag, etc */ + const char* action; /* what to do because mouse did previous action */ + const char* command; + struct wl_list link; +}; + +struct mousebind* +mousebind_create(const char* context_str, const char* mouse_button_str, + const char* action_mouse_did_str, const char* action, + const char* command); + +#endif /* __LABWC_MOUSEBIND_H */ diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 958aedfe..e2c60e35 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -19,6 +19,7 @@ struct rcxml { int font_size_activewindow; int font_size_menuitem; struct wl_list keybinds; + struct wl_list mousebinds; }; extern struct rcxml rc; diff --git a/src/config/meson.build b/src/config/meson.build index 9a42abb9..b5dcf50b 100644 --- a/src/config/meson.build +++ b/src/config/meson.build @@ -2,4 +2,5 @@ labwc_sources += files( 'rcxml.c', 'keybind.c', 'session.c', + 'mousebind.c', ) diff --git a/src/config/mousebind.c b/src/config/mousebind.c new file mode 100644 index 00000000..e3ffbb83 --- /dev/null +++ b/src/config/mousebind.c @@ -0,0 +1,90 @@ +#define _POSIX_C_SOURCE 200809L +#include "config/mousebind.h" +#include "config/rcxml.h" +#include +#include +#include + +static enum mouse_context +context_from_str(const char* str) +{ + if(str == NULL) { + return MOUSE_CONTEXT_NONE; + } + else if(strcasecmp(str, "Titlebar") == 0) { + return MOUSE_CONTEXT_TITLEBAR; + } else { + return MOUSE_CONTEXT_NONE; + } +} + +static enum mouse_button +mouse_button_from_str(const char* str) +{ + if(str == NULL) { + return MOUSE_BUTTON_NONE; + } + else if(strcasecmp(str, "Left") == 0) { + return MOUSE_BUTTON_LEFT; + } else if(strcasecmp(str, "Right") == 0) { + return MOUSE_BUTTON_RIGHT; + } else if(strcasecmp(str, "Middle") == 0) { + return MOUSE_BUTTON_MIDDLE; + } else { + return MOUSE_BUTTON_NONE; + } +} + +static enum action_mouse_did +action_mouse_did_from_str(const char* str) +{ + if(str == NULL) { + return MOUSE_ACTION_NONE; + } + else if(strcasecmp(str, "doubleclick") == 0) { + return MOUSE_ACTION_DOUBLECLICK; + } else { + return MOUSE_ACTION_NONE; + } +} + +struct mousebind* +mousebind_create(const char* context_str, const char* mouse_button_str, + const char* action_mouse_did_str, const char* action, + const char* command) +{ + struct mousebind* m = calloc(1, sizeof(struct mousebind)); + + enum mouse_context context = context_from_str(context_str); + enum mouse_button button = mouse_button_from_str(mouse_button_str); + enum action_mouse_did action_mouse_did = action_mouse_did_from_str(action_mouse_did_str); + + if(context == MOUSE_CONTEXT_NONE) { + wlr_log(WLR_ERROR, "unknown mouse context (%s)", context_str); + goto CREATE_ERROR; + } + if(button == MOUSE_BUTTON_NONE) { + wlr_log(WLR_ERROR, "unknown button (%s)", mouse_button_str); + goto CREATE_ERROR; + } + if(action_mouse_did == MOUSE_ACTION_NONE) { + wlr_log(WLR_ERROR, "unknown mouse action (%s)", action_mouse_did_str); + goto CREATE_ERROR; + } + if(action == NULL) { + wlr_log(WLR_ERROR, "action is NULL\n"); + goto CREATE_ERROR; + } + + m->context = context; + m->button = button; + m->mouse_action = action_mouse_did; + m->action = strdup(action); /* TODO: replace with strndup? */ + m->command = strdup(command); + + return m; + +CREATE_ERROR: + free(m); + return NULL; +} diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 2371482c..d13db7c0 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -16,11 +16,13 @@ #include "common/string-helpers.h" #include "common/zfree.h" #include "config/keybind.h" +#include "config/mousebind.h" #include "config/rcxml.h" static bool in_keybind = false; static bool is_attribute = false; static struct keybind *current_keybind; +static const char* current_mouse_context = ""; enum font_place { FONT_PLACE_UNKNOWN = 0, @@ -223,6 +225,184 @@ traverse(xmlNode *n) xml_tree_walk(n->children); } +static bool +is_ignorable_node(const xmlNode* n) +{ + if(n->type == XML_COMMENT_NODE) { + return true; + } + + if(n->type == XML_TEXT_NODE) { + for(const char* s = (const char*)n->content; s && (*s != '\0'); s++) { + if(!isspace(*s)) { + return false; + } + } + return true; + } + + return false; +} + +static void +traverse_mousebind_action(xmlNode* node, const char* button_str, const char* mouse_action) +{ + /* + * Right now, all we have implemented is: + * + * ] -- only supported attribute is "name" + * + * properties; attr; attr = attr->next) { + if(strcasecmp((const char*)attr->name, "name") == 0) { + action_to_do = (const char*)attr->children->content; + } else { + wlr_log(WLR_ERROR, "hit unexpected attr (%s) in mousebind action\n", (const char*) attr->name); + } + } + + if(strcasecmp((const char*)action_to_do, "Execute") == 0) { + for(xmlNode* n = node->children; n && n->name; n = n->next) { + if(strcasecmp((const char*)n->name, "command") == 0) { + for(xmlNode* t = n->children; t; t = t->next) { + if( (t->type == XML_TEXT_NODE) ) { + command = (const char*)t->content; + } + } + } + } + } + struct mousebind* mousebind = mousebind_create(current_mouse_context, + button_str, + mouse_action, + action_to_do, + command); + if(mousebind != NULL) { + wl_list_insert(&rc.mousebinds, &mousebind->link); + } else { + wlr_log(WLR_ERROR, "failed to create mousebind:\n" + " context: (%s)\n" + " button: (%s)\n" + " mouse action: (%s)\n" + " action: (%s)\n" + " command: (%s)\n", + current_mouse_context, + button_str, + mouse_action, + action_to_do, + command); + } +} + +static void +traverse_mousebind(xmlNode* node) +{ + /* + * Right now, all we have implemented is: + * + * this node + * | this node's only supported attributes are "button" and "action" + * | | | + * v v v + * -| + * properties; attr; attr = attr->next) { + if(strcasecmp((const char*)attr->name, "button") == 0) { + button_str = (const char*)attr->children->content; + } else if(strcasecmp((const char*)attr->name, "action") == 0) { + action_mouse_did_str = (const char*)attr->children->content; + } else { + wlr_log(WLR_ERROR, "hit unexpected attr (%s) in mousebind\n", (const char*)attr->name); + } + } + + for(xmlNode* n = node->children; n && n->name; n = n->next) { + if(strcasecmp((const char*)n->name, "action") == 0) { + traverse_mousebind_action(n, button_str, action_mouse_did_str); + } else if(is_ignorable_node(n)) { + continue; + } else { + wlr_log(WLR_ERROR, "hit unexpected node (%s) in mousebind\n", (const char*) n->name); + } + } +} + +static void +traverse_context(xmlNode* node) +{ + /* + * Right now, all we have implemented is: + * + * this node + * | this node's only supported attribute is "name" + * | | + * v v + * + * -| + * ... | -- This node's only supported child is mousebind + * -| + * + */ + for(xmlAttr* attr = node->properties; attr; attr = attr->next) { + if(strcasecmp((const char*)attr->name, "name") == 0) { + current_mouse_context = (const char*)attr->children->content; + } else { + wlr_log(WLR_ERROR, "hit unexpected attr (%s) in context\n", (const char*)attr->name); + } + } + + for(xmlNode* n = node->children; n && n->name; n = n->next) { + if(strcasecmp((const char*)n->name, "mousebind") == 0) { + traverse_mousebind(n); + } else if(is_ignorable_node(n)) { + continue; + } else { + wlr_log(WLR_ERROR, "hit unexpected node (%s) in context\n", (const char*)n->name); + } + } +} + +static void +traverse_mouse(xmlNode* node) +{ + /* + * Right now, all we have implemented is: + * + * this node + * | + * | + * v + * + * -| + * | + * ... | -- This node's only supported child is context + * | + * -| + * + */ + for(xmlNode* n = node->children; n && n->name; n = n->next) { + if(strcasecmp((const char*)n->name, "context") == 0) { + traverse_context(n); + } else if(is_ignorable_node(n)) { + continue; + } else { + wlr_log(WLR_ERROR, "hit unexpected node (%s) in mouse\n", (const char*)n->name); + } + } +} + static void xml_tree_walk(xmlNode *node) { @@ -235,6 +415,10 @@ xml_tree_walk(xmlNode *node) traverse(n); in_keybind = false; continue; + } + if(!strcasecmp((char *)n->name, "mouse")) { + traverse_mouse(n); + continue; } traverse(n); } @@ -265,6 +449,7 @@ rcxml_init() has_run = true; LIBXML_TEST_VERSION wl_list_init(&rc.keybinds); + wl_list_init(&rc.mousebinds); rc.xdg_shell_server_side_deco = true; rc.corner_radius = 8; rc.font_size_activewindow = 10; @@ -400,4 +585,12 @@ rcxml_finish(void) zfree(k->keysyms); zfree(k); } + + struct mousebind *m, *m_tmp; + wl_list_for_each_safe(m, m_tmp, &rc.mousebinds, link) { + wl_list_remove(&m->link); + zfree(m->command); + zfree(m->action); + zfree(m); + } } diff --git a/src/cursor.c b/src/cursor.c index 126b84f2..5770f542 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -5,6 +5,7 @@ #include "labwc.h" #include "menu/menu.h" #include "ssd.h" +#include "config/mousebind.h" #include static void @@ -343,7 +344,14 @@ cursor_button(struct wl_listener *listener, void *data) damage_all_outputs(server); if (is_double_click(500) && view_area == LAB_SSD_PART_TITLEBAR) { - view_toggle_maximize(view); + struct mousebind* mousebind; + wl_list_for_each_reverse(mousebind, &rc.mousebinds, link) { + if( (mousebind->context == MOUSE_CONTEXT_TITLEBAR) && + (mousebind->mouse_action == MOUSE_ACTION_DOUBLECLICK) && + (mousebind->button == (enum mouse_button)event->button) ) { + action(server, mousebind->action, mousebind->command); + } + } return; } -- 2.52.0