]> git.mdlowis.com Git - proto/labwc.git/commitdiff
query: support additional conditions for matching clients
authorOrfeas <38209077+0xfea5@users.noreply.github.com>
Thu, 17 Oct 2024 23:07:52 +0000 (02:07 +0300)
committerAndrew J. Hesford <ajh@sideband.org>
Wed, 30 Oct 2024 18:30:26 +0000 (14:30 -0400)
Co-authored-by: Andrew J. Hesford <ajh@sideband.org>
Closes: #2245.
docs/labwc-actions.5.scd
include/ssd.h
include/view.h
src/action.c
src/config/rcxml.c
src/ssd/ssd.c
src/view.c

index 8f37fdcaf99634b69e3f938697b67d774213c0bb..89f3eb04becf82ca1189337e10ea17d8b1076901 100644 (file)
@@ -390,6 +390,41 @@ Actions that execute other actions. Used in keyboard/mouse bindings.
                        Internal heuristics for Wayland clients,
                        NET_WM_WINDOW_TYPE for XWayland clients.
 
+               *shaded* [yes|no]
+                       Whether or not the client is rolled up.
+
+               *maximized* [both|horizontal|vertical|none]
+                       Whether the client is maximized along both axes, the
+                       horizontal axis only, the vertical axis only, or neither
+                       axis (none).
+
+               *iconified* [yes|no]
+                       Whether or not the client is iconified.
+
+               *focused* [yes|no]
+                       Whether or not the client is focused.
+
+               *omnipresent* [yes|no]
+                       Whether or not the client is visible on all desktops.
+
+               *desktop*
+                       The desktop the client is currently on. This can be the
+                       number or name of a desktop, or special relative values
+                       "current", "other", "left", "right" or "last". The
+                       "left" and "right" directions will not wrap.
+
+               *tiled* [up|right|down|left|center]
+                       Whether the client is tiled (snapped) along the the
+                       indicated screen edge.
+
+               *tiled_region*
+                       Whether the client is tiled (snapped) to the indicated
+                       region.
+
+               *decoration* [full|border|none]
+                       Whether the client has full server-side decorations,
+                       borders only, or no server-side decorations.
+
                This argument is optional.
 
        *then*
index d5b66c087547e80161a079782c8b56d32cfdc81f..fe56be54c8a063d8818d4c0dc947c2fb84e04307 100644 (file)
@@ -62,9 +62,10 @@ enum ssd_part_type {
 };
 
 enum ssd_mode {
+       LAB_SSD_MODE_INVALID,
        LAB_SSD_MODE_NONE,
        LAB_SSD_MODE_BORDER,
-       LAB_SSD_MODE_FULL
+       LAB_SSD_MODE_FULL,
 };
 
 /* Forward declare arguments */
index 7a1b5dde520754cbc6a8731311a0e84facb0417c..6fc870e76f90e8e572813a200ea5c82195191fb7 100644 (file)
@@ -57,6 +57,11 @@ enum view_axis {
        VIEW_AXIS_HORIZONTAL = (1 << 0),
        VIEW_AXIS_VERTICAL = (1 << 1),
        VIEW_AXIS_BOTH = (VIEW_AXIS_HORIZONTAL | VIEW_AXIS_VERTICAL),
+       /*
+        * If view_axis is treated as a bitfield, INVALID should never
+        * set the HORIZONTAL or VERTICAL bits.
+        */
+       VIEW_AXIS_INVALID = (1 << 2),
 };
 
 enum view_edge {
@@ -290,6 +295,16 @@ struct view_query {
        int window_type;
        char *sandbox_engine;
        char *sandbox_app_id;
+       enum three_state shaded;
+       enum view_axis maximized;
+       enum three_state iconified;
+       enum three_state focused;
+       enum three_state omnipresent;
+       enum view_edge tiled;
+       char *tiled_region;
+       char *desktop;
+       enum ssd_mode decoration;
+       char *monitor;
 };
 
 struct xdg_toplevel_view {
index 5c0f2a8da21bbc1ca9510d1d3887ed493b0ef1bd..0afce12675220c4103a7e0625ef5bc47082324d3 100644 (file)
@@ -366,7 +366,7 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
        case ACTION_TYPE_UNMAXIMIZE:
                if (!strcmp(argument, "direction")) {
                        enum view_axis axis = view_axis_parse(content);
-                       if (axis == VIEW_AXIS_NONE) {
+                       if (axis == VIEW_AXIS_NONE || axis == VIEW_AXIS_INVALID) {
                                wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
                                        action_names[action->type], argument, content);
                        } else {
@@ -378,7 +378,12 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
        case ACTION_TYPE_SET_DECORATIONS:
                if (!strcmp(argument, "decorations")) {
                        enum ssd_mode mode = ssd_mode_parse(content);
-                       action_arg_add_int(action, argument, mode);
+                       if (mode != LAB_SSD_MODE_INVALID) {
+                               action_arg_add_int(action, argument, mode);
+                       } else {
+                               wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)",
+                                       action_names[action->type], argument, content);
+                       }
                        goto cleanup;
                }
                if (!strcasecmp(argument, "forceSSD")) {
index d6d9c76fcd4a331bd37f915a070472ea2b8b30ea..198fe38b9b79d5fab70357b37d4fc2f3d75e5735 100644 (file)
@@ -473,6 +473,26 @@ fill_action_query(char *nodename, char *content, struct action *action)
                current_view_query->sandbox_engine = xstrdup(content);
        } else if (!strcasecmp(nodename, "sandboxAppId")) {
                current_view_query->sandbox_app_id = xstrdup(content);
+       } else if (!strcasecmp(nodename, "shaded")) {
+               current_view_query->shaded = parse_bool(content, -1);
+       } else if (!strcasecmp(nodename, "maximized")) {
+               current_view_query->maximized = view_axis_parse(content);
+       } else if (!strcasecmp(nodename, "iconified")) {
+               current_view_query->iconified = parse_bool(content, -1);
+       } else if (!strcasecmp(nodename, "focused")) {
+               current_view_query->focused = parse_bool(content, -1);
+       } else if (!strcasecmp(nodename, "omnipresent")) {
+               current_view_query->omnipresent = parse_bool(content, -1);
+       } else if (!strcasecmp(nodename, "tiled")) {
+               current_view_query->tiled = view_edge_parse(content);
+       } else if (!strcasecmp(nodename, "tiled_region")) {
+               current_view_query->tiled_region = xstrdup(content);
+       } else if (!strcasecmp(nodename, "desktop")) {
+               current_view_query->desktop = xstrdup(content);
+       } else if (!strcasecmp(nodename, "decoration")) {
+               current_view_query->decoration = ssd_mode_parse(content);
+       } else if (!strcasecmp(nodename, "monitor")) {
+               current_view_query->monitor = xstrdup(content);
        }
 }
 
index c1e4e0e512d3d88329b438177066460e6b614cf4..a9fc1abc7df594984f9cd8d0a853d7643a859d1a 100644 (file)
@@ -350,14 +350,16 @@ enum ssd_mode
 ssd_mode_parse(const char *mode)
 {
        if (!mode) {
-               return LAB_SSD_MODE_FULL;
+               return LAB_SSD_MODE_INVALID;
        }
        if (!strcasecmp(mode, "none")) {
                return LAB_SSD_MODE_NONE;
        } else if (!strcasecmp(mode, "border")) {
                return LAB_SSD_MODE_BORDER;
-       } else {
+       } else if (!strcasecmp(mode, "full")) {
                return LAB_SSD_MODE_FULL;
+       } else {
+               return LAB_SSD_MODE_INVALID;
        }
 }
 
index 720a9cd5faecd3b585b6796d9e1feec33d29d1d7..e80b6b331c27314fa7d52d63efc47e01224483c7 100644 (file)
@@ -8,6 +8,7 @@
 #include "common/macros.h"
 #include "common/match.h"
 #include "common/mem.h"
+#include "common/parse-bool.h"
 #include "common/scene-helpers.h"
 #include "input/keyboard.h"
 #include "labwc.h"
@@ -22,6 +23,7 @@
 #include "ssd.h"
 #include "view.h"
 #include "window-rules.h"
+#include "wlr/util/log.h"
 #include "workspaces.h"
 #include "xwayland.h"
 
@@ -76,53 +78,154 @@ void
 view_query_free(struct view_query *query)
 {
        wl_list_remove(&query->link);
-       free(query->identifier);
-       free(query->title);
-       free(query->sandbox_engine);
-       free(query->sandbox_app_id);
-       free(query);
+       zfree(query->identifier);
+       zfree(query->title);
+       zfree(query->sandbox_engine);
+       zfree(query->sandbox_app_id);
+       zfree(query->tiled_region);
+       zfree(query->desktop);
+       zfree(query->monitor);
+       zfree(query);
+}
+
+static enum three_state
+bool_to_tristate(bool b)
+{
+       return b ? LAB_STATE_ENABLED : LAB_STATE_DISABLED;
+}
+
+static enum three_state
+match_tristate(enum three_state desired, bool actual, enum three_state old_match)
+{
+       switch (desired) {
+       case LAB_STATE_ENABLED:
+               return bool_to_tristate(actual);
+       case LAB_STATE_DISABLED:
+               return bool_to_tristate(!actual);
+       default:
+               return old_match;
+       }
 }
 
 bool
 view_matches_query(struct view *view, struct view_query *query)
 {
-       bool match = true;
-       bool empty = true;
+       enum three_state match = LAB_STATE_UNSPECIFIED;
 
-       const char *identifier = view_get_string_prop(view, "app_id");
-       if (match && query->identifier) {
-               empty = false;
-               match &= identifier && match_glob(query->identifier, identifier);
+       if (query->identifier) {
+               const char *identifier = view_get_string_prop(view, "app_id");
+               if (!(identifier && match_glob(query->identifier, identifier))) {
+                       return false;
+               }
        }
 
-       const char *title = view_get_string_prop(view, "title");
-       if (match && query->title) {
-               empty = false;
-               match &= title && match_glob(query->title, title);
+       if (query->title) {
+               const char *title = view_get_string_prop(view, "title");
+               if (!(title && match_glob(query->title, title))) {
+                       return false;
+               }
        }
 
-       if (match && query->window_type >= 0) {
-               empty = false;
-               match &= view_contains_window_type(view, query->window_type);
+       if (query->window_type >= 0) {
+               if (!view_contains_window_type(view, query->window_type)) {
+                       return false;
+               }
        }
 
-       if (match && query->sandbox_engine) {
+       if (query->sandbox_engine) {
                const struct wlr_security_context_v1_state *security_context =
                        security_context_from_view(view);
-               empty = false;
-               match &= security_context && security_context->sandbox_engine
-                       && match_glob(query->sandbox_engine, security_context->sandbox_engine);
+               if (!(security_context && security_context->sandbox_engine &&
+                       match_glob(query->sandbox_engine, security_context->sandbox_engine))) {
+                       return false;
+               }
        }
 
-       if (match && query->sandbox_app_id) {
+       if (query->sandbox_app_id) {
                const struct wlr_security_context_v1_state *security_context =
                        security_context_from_view(view);
-               empty = false;
-               match &= security_context && security_context->app_id
-                       && match_glob(query->sandbox_app_id, security_context->app_id);
+               if (!(security_context && security_context->app_id &&
+                       match_glob(query->sandbox_app_id, security_context->app_id))) {
+                       return false;
+               }
+       }
+
+       match = match_tristate(query->shaded, view->shaded, match);
+       if (match  == LAB_STATE_DISABLED) {
+               return false;
+       }
+
+       if (query->maximized != VIEW_AXIS_INVALID) {
+               match = bool_to_tristate(view->maximized == query->maximized);
+               if (match == LAB_STATE_DISABLED) {
+                       return false;
+               }
+       }
+
+       match = match_tristate(query->iconified, view->minimized, match);
+       if (match == LAB_STATE_DISABLED) {
+               return false;
+       }
+
+       match = match_tristate(query->focused, view->server->active_view == view, match);
+       if (match == LAB_STATE_DISABLED) {
+               return false;
+       }
+
+       match = match_tristate(query->omnipresent, view->visible_on_all_workspaces, match);
+       if (match == LAB_STATE_DISABLED) {
+               return false;
+       }
+
+       if (query->tiled != VIEW_EDGE_INVALID) {
+               match = bool_to_tristate(query->tiled == view->tiled);
+               if (match == LAB_STATE_DISABLED) {
+                       return false;
+               }
+       }
+
+       if (query->tiled_region) {
+               match = bool_to_tristate(view->tiled_region &&
+                       !strcasecmp(query->tiled_region, view->tiled_region->name));
+               if (match == LAB_STATE_DISABLED) {
+                       return false;
+               }
+       }
+
+       if (query->desktop) {
+               if (!strcasecmp(query->desktop, "other")) {
+                       struct workspace *current = view->server->workspaces.current;
+                       match = bool_to_tristate(strcasecmp(view->workspace->name, current->name));
+               } else {
+                       // TODO: perhaps allow wrapping for "left" and "right" workspaces
+                       struct workspace *target =
+                               workspaces_find(view->server->workspaces.current,
+                                               query->desktop, false);
+                       match = bool_to_tristate(target &&
+                                       !strcasecmp(view->workspace->name, target->name));
+               }
+               if (match == LAB_STATE_DISABLED) {
+                       return false;
+               }
+       }
+
+       enum ssd_mode decoration = view_get_ssd_mode(view);
+       if (query->decoration != LAB_SSD_MODE_INVALID) {
+               match = bool_to_tristate(query->decoration == decoration);
+               if (match == LAB_STATE_DISABLED) {
+                       return false;
+               }
        }
 
-       return !empty && match;
+       if (query->monitor) {
+               struct output *target = output_from_name(view->server, query->monitor);
+               match = bool_to_tristate(target == view->output);
+               if (match == LAB_STATE_DISABLED) {
+                       return false;
+               }
+       }
+
+       return match == LAB_STATE_ENABLED;
 }
 
 static bool
@@ -1949,7 +2052,7 @@ enum view_axis
 view_axis_parse(const char *direction)
 {
        if (!direction) {
-               return VIEW_AXIS_NONE;
+               return VIEW_AXIS_INVALID;
        }
        if (!strcasecmp(direction, "horizontal")) {
                return VIEW_AXIS_HORIZONTAL;
@@ -1957,8 +2060,10 @@ view_axis_parse(const char *direction)
                return VIEW_AXIS_VERTICAL;
        } else if (!strcasecmp(direction, "both")) {
                return VIEW_AXIS_BOTH;
-       } else {
+       } else if (!strcasecmp(direction, "none")) {
                return VIEW_AXIS_NONE;
+       } else {
+               return VIEW_AXIS_INVALID;
        }
 }