query: support additional conditions for matching clients

Co-authored-by: Andrew J. Hesford <ajh@sideband.org>

Closes: #2245.
This commit is contained in:
Orfeas 2024-10-18 02:07:52 +03:00 committed by Andrew J. Hesford
parent f394d03600
commit 96da82c085
7 changed files with 217 additions and 34 deletions

View 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")) {

View 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);
}
}

View 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;
}
}

View 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;
}
}
return !empty && match;
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;
}
}
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;
}
}