diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 4dbf6cd8..3f6ec6df 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -81,7 +81,9 @@ mousebind_create(const char* context_str, const char* mouse_button_str, m->button = button; m->mouse_action = action_mouse_did; m->action = strdup(action); /* TODO: replace with strndup? */ - m->command = strdup(command); + if(command && !strcasecmp(action, "Execute")) { + m->command = strdup(command); + } return m; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 9e6d0298..4d4034db 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -21,10 +21,12 @@ #include "config/rcxml.h" static bool in_keybind = false; +static bool in_mousebind = false; static bool is_attribute = false; static struct keybind *current_keybind; static const char* current_mouse_context = ""; + enum font_place { FONT_PLACE_UNKNOWN = 0, FONT_PLACE_ACTIVEWINDOW, @@ -32,6 +34,27 @@ enum font_place { /* TODO: Add all places based on Openbox's rc.xml */ }; +/* + * unchecked mousebind params. we fill these out one at a time, then pass them + * all to mousebind_create() once we are ready + */ +static const char* current_mouse_button = ""; +static const char* current_action_mouse_did= ""; +struct mouse_action { + const char* action; + const char* command; +}; +/* + * A given mousebind can have multiple actions associated with it. + * This array is a list of the actions for the currently-being-parsed mousebind + * + * TODO: make it a linked list? + */ +#define MAX_MOUSE_ACTIONS 32 +static struct mouse_action mouse_actions[MAX_MOUSE_ACTIONS] = {{0}}; +static int num_mouse_actions = 0; + + static void load_default_key_bindings(void); static void @@ -63,6 +86,75 @@ fill_keybind(char *nodename, char *content) } } +static void +add_new_mousebinds(void) +{ + for(int i = 0; i < num_mouse_actions; i++) { + struct mousebind* m = mousebind_create(current_mouse_context, + current_mouse_button, + current_action_mouse_did, + mouse_actions[i].action, + mouse_actions[i].command); + if(m != NULL) { + wl_list_insert(&rc.mousebinds, &m->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, + current_mouse_button, + current_action_mouse_did, + mouse_actions[i].action, + mouse_actions[i].command); + } + } + + num_mouse_actions = 0; + memset(mouse_actions, 0, sizeof(struct mouse_action) * MAX_MOUSE_ACTIONS); +} + +static void +fill_mousebind(char* nodename, char* content) +{ + /* + * Example of what we're parsing: + * + * + * + * + */ + if(!content) { + return; + } + + string_truncate_at_pattern(nodename, ".mousebind.context.mouse"); + + if(is_attribute && !strcmp(nodename, "button")) { + current_mouse_button = content; + } else if(!strcmp(nodename, "action")) { + /* + * checking for is_attribute fails even though we are looking for the + * attribute of mousebind named action. initial thoughts were to check + * for is_attribute to distinguish the attribute of mousebind named + * action from the child of mousebind named action. since the child of + * mousebind named action doesn't have any content, I don't think we + * need to make this distinction since we already filtered out nodes that + * don't have content + */ + current_action_mouse_did = content; + } else if(is_attribute && !strcmp(nodename, "name.action")) { + if(num_mouse_actions < MAX_MOUSE_ACTIONS) { + num_mouse_actions++; + mouse_actions[num_mouse_actions-1].action = content; + } + } else if(!strcmp(nodename, "command.action")) { + mouse_actions[num_mouse_actions-1].command = content; + } +} + static bool get_bool(const char *s) { @@ -169,6 +261,10 @@ entry(xmlNode *node, char *nodename, char *content) fill_keybind(nodename, content); } + if (in_mousebind) { + fill_mousebind(nodename, content); + } + if (is_attribute && !strcmp(nodename, "place.font.theme")) { font_place = enum_font_place(content); } @@ -194,6 +290,26 @@ entry(xmlNode *node, char *nodename, char *content) } else if (!strcasecmp(nodename, "RaiseOnFocus.focus")) { rc.focus_follow_mouse = true; rc.raise_on_focus = get_bool(content); + } else if(!strcasecmp(nodename, "doubleClickTime.mouse")) { + long doubleclick_time_parsed = strtol(content, NULL, 10); + + /* + * There are 2 possible sources for a bad doubleclicktime value: + * - user gave a value of 0 (which doesn't make sense) + * - user gave a negative value (which doesn't make sense) + * - user gave a value which strtol couldn't parse + * + * since strtol() returns 0 on error, all we have to do is check + * for to see if strtol() returned 0 or less to handle the error + * cases. in case of error, we just choose not to override the + * default value and everything should be fine + */ + bool valid_doubleclick_time = doubleclick_time_parsed > 0; + if(valid_doubleclick_time) { + rc.doubleclick_time = doubleclick_time_parsed; + } + } else if(is_attribute && !strcasecmp(nodename, "name.context.mouse")) { + current_mouse_context = content; } } @@ -226,220 +342,6 @@ 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_doubleclick_time(xmlNode* node) -{ - /* - * this node - * | - * | - * v - * 200 - */ - for(xmlNode* n = node->children; n && n->name; n = n->next) { - if(n->type == XML_TEXT_NODE) { - long doubleclick_time_parsed = strtol((const char*)n->content, NULL, 10); - - /* - * There are 2 possible sources for a bad doubleclicktime value: - * - user gave a value of 0 (which doesn't make sense) - * - user gave a negative value (which doesn't make sense) - * - user gave a value which strtol couldn't parse - * - * since strtol() returns 0 on error, all we have to do is check - * for to see if strtol() returned 0 or less to handle the error - * cases. in case of error, we just choose not to override the - * default value and everything should be fine - */ - bool valid_doubleclick_time = doubleclick_time_parsed > 0; - if(valid_doubleclick_time) { - rc.doubleclick_time = doubleclick_time_parsed; - } - } - } -} - -static void -traverse_mouse(xmlNode* node) -{ - /* - * Right now, all we have implemented is: - * - * this node - * | - * | - * v - * - * 200 ] - * -| - * | - * ... | -- This node's only supported children - * | are doubleClickTime and 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(strcasecmp((const char*)n->name, "doubleClickTime") == 0) { - traverse_doubleclick_time(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) { @@ -453,8 +355,11 @@ xml_tree_walk(xmlNode *node) in_keybind = false; continue; } - if(!strcasecmp((char *)n->name, "mouse")) { - traverse_mouse(n); + if(!strcasecmp((char *)n->name, "mousebind")) { + in_mousebind = true; + traverse(n); + in_mousebind = false; + add_new_mousebinds(); continue; } traverse(n);