]> git.mdlowis.com Git - proto/labwc.git/commitdiff
First implemenation of <mouse> in rc.xml
authoralex <alex@compy-sid>
Sun, 29 Aug 2021 18:22:49 +0000 (14:22 -0400)
committerJohan Malm <johanmalm@users.noreply.github.com>
Wed, 1 Sep 2021 06:05:37 +0000 (07:05 +0100)
Can successfully parse the following XML and and implement the action:

<mouse>
    <context name="TitleBar">
        <mousebind button="Left" action="DoubleClick">
            <action name="ToggleMaximize"/>
        </mousebind>
    </context>
</mouse>

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 [new file with mode: 0644]
include/config/rcxml.h
src/config/meson.build
src/config/mousebind.c [new file with mode: 0644]
src/config/rcxml.c
src/cursor.c

diff --git a/include/config/mousebind.h b/include/config/mousebind.h
new file mode 100644 (file)
index 0000000..6fb57ac
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __LABWC_MOUSEBIND_H
+#define __LABWC_MOUSEBIND_H
+
+#include <wayland-util.h>
+
+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 */
index 958aedfea908941090167492282df7720ecd5044..e2c60e35ceca94699fd5611eb57e4c61e7d4851f 100644 (file)
@@ -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;
index 9a42abb9b1a542026850b0f603d18346090a0ddc..b5dcf50b57238a75669e4170022ffe7731cd3cd5 100644 (file)
@@ -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 (file)
index 0000000..e3ffbb8
--- /dev/null
@@ -0,0 +1,90 @@
+#define _POSIX_C_SOURCE 200809L
+#include "config/mousebind.h"
+#include "config/rcxml.h"
+#include <wlr/util/log.h>
+#include <strings.h>
+#include <unistd.h>
+
+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;
+}
index 2371482c6a41a676fc66ac1e98e13d78ee409aca..d13db7c0e46e9ecffffd6e6523410ede8891e100 100644 (file)
 #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:
+        *
+        * <action name="ToggleMaximize"/>             ] -- only supported attribute is "name"
+        *
+        * <action name="Execute>                    
+        *      <command>command text here</command>   ] -- if name is execute, we support a child node of name command
+        * </action>                                
+        */
+       const char* action_to_do = "";
+       const char* command = "";
+       for(xmlAttr* attr = node->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
+     * <mousebind button="Left" action="DoubleClick>                 
+     *     <action name="ToggleMaximize"/>           -| 
+     *     <action name="Execute>                     | -- This node's only supported children are actions
+     *          <command>command text here</command>  |   
+     *     </action>                                 -|       
+     * </context>               
+     */
+       const char* button_str = "";
+       const char* action_mouse_did_str = "";
+       for(xmlAttr* attr = node->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
+     * <context name="TitleBar">
+     *           <mousebind....>     -|
+     *          ...            | -- This node's only supported child is mousebind
+     *           </mousebind>        -|
+     * </context>
+     */
+       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
+     * <mouse>
+     *     <context name="TitleBar"> -|
+     *         <mousebind....>           |
+     *           ...                  | -- This node's only supported child is context
+     *         </mousebind>              |
+     *     </context>                -|
+     * </mouse>
+     */
+       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);
+       }
 }
index 126b84f21eccce981808392e22b8d69d018b6305..5770f5425b0c92d910a4113586ba92db72456198 100644 (file)
@@ -5,6 +5,7 @@
 #include "labwc.h"
 #include "menu/menu.h"
 #include "ssd.h"
+#include "config/mousebind.h"
 #include <wlr/types/wlr_primary_selection.h>
 
 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;
        }