Add queries to If actions
This allows the If action to run queries against a client other than the target of the actions being run, for example to check state on the focused window while performing actions on another window during focus cycling. The syntax looks like <action name="If"> <query target="default"> <title>FooBar</title> <maximized>yes</maximized> </query> <query target="focus"> <desktop>3</desktop> </query> <then> <action name="NextDesktop"/> </then> </action> The above checks the client window that the actions will run on to verify that its title is "FooBar" and that it is maximized. If that is true, it also checks that the currently focused client window is on desktop 3. If that is true also, then it runs the NextDesktop action. The target="" option can be set to "default" which uses the client window that the actions will run on, or it can be "focus" which uses the client window that is currently focused. The <query> tag is optional, and the conditions inside the query can be placed directly inside the If <action> tag, as they were before this change. In that case, a default <query> tag is assumed with target="default" which matches the previous behaviour. Multiple <query> tags can be present, and they must all be true in order to run the actions in <then>. If any one is false, the actions in <else> will be run instead.
This commit is contained in:
parent
98c5205b9e
commit
881076d2a8
1 changed files with 209 additions and 69 deletions
|
@ -1,3 +1,22 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
|
||||
|
||||
if.c for the Openbox window manager
|
||||
Copyright (c) 2007 Mikael Magnusson
|
||||
Copyright (c) 2007 Dana Jansens
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
See the COPYING file for a copy of the GNU General Public License.
|
||||
*/
|
||||
|
||||
#include "openbox/actions.h"
|
||||
#include "openbox/misc.h"
|
||||
#include "openbox/client.h"
|
||||
|
@ -6,7 +25,13 @@
|
|||
#include "openbox/focus.h"
|
||||
#include <glib.h>
|
||||
|
||||
typedef enum {
|
||||
QUERY_TARGET_IS_ACTION_TARGET,
|
||||
QUERY_TARGET_IS_FOCUS_TARGET,
|
||||
} QueryTarget;
|
||||
|
||||
typedef struct {
|
||||
QueryTarget target;
|
||||
gboolean shaded_on;
|
||||
gboolean shaded_off;
|
||||
gboolean maxvert_on;
|
||||
|
@ -33,6 +58,10 @@ typedef struct {
|
|||
GPatternSpec *matchtitle;
|
||||
GRegex *regextitle;
|
||||
gchar *exacttitle;
|
||||
} Query;
|
||||
|
||||
typedef struct {
|
||||
GArray* queries;
|
||||
GSList *thenacts;
|
||||
GSList *elseacts;
|
||||
} Options;
|
||||
|
@ -61,37 +90,37 @@ static inline void set_bool(xmlNodePtr node,
|
|||
}
|
||||
}
|
||||
|
||||
static gpointer setup_func(xmlNodePtr node)
|
||||
{
|
||||
static void setup_query(Options* o, xmlNodePtr node, QueryTarget target) {
|
||||
Query *q = g_slice_new0(Query);
|
||||
g_array_append_val(o->queries, q);
|
||||
|
||||
q->target = target;
|
||||
|
||||
set_bool(node, "shaded", &q->shaded_on, &q->shaded_off);
|
||||
set_bool(node, "maximized", &q->maxfull_on, &q->maxfull_off);
|
||||
set_bool(node, "maximizedhorizontal", &q->maxhorz_on, &q->maxhorz_off);
|
||||
set_bool(node, "maximizedvertical", &q->maxvert_on, &q->maxvert_off);
|
||||
set_bool(node, "iconified", &q->iconic_on, &q->iconic_off);
|
||||
set_bool(node, "focused", &q->focused, &q->unfocused);
|
||||
set_bool(node, "urgent", &q->urgent_on, &q->urgent_off);
|
||||
set_bool(node, "undecorated", &q->decor_off, &q->decor_on);
|
||||
set_bool(node, "omnipresent", &q->omnipresent_on, &q->omnipresent_off);
|
||||
|
||||
xmlNodePtr n;
|
||||
Options *o;
|
||||
|
||||
o = g_slice_new0(Options);
|
||||
|
||||
set_bool(node, "shaded", &o->shaded_on, &o->shaded_off);
|
||||
set_bool(node, "maximized", &o->maxfull_on, &o->maxfull_off);
|
||||
set_bool(node, "maximizedhorizontal", &o->maxhorz_on, &o->maxhorz_off);
|
||||
set_bool(node, "maximizedvertical", &o->maxvert_on, &o->maxvert_off);
|
||||
set_bool(node, "iconified", &o->iconic_on, &o->iconic_off);
|
||||
set_bool(node, "focused", &o->focused, &o->unfocused);
|
||||
set_bool(node, "urgent", &o->urgent_on, &o->urgent_off);
|
||||
set_bool(node, "undecorated", &o->decor_off, &o->decor_on);
|
||||
set_bool(node, "omnipresent", &o->omnipresent_on, &o->omnipresent_off);
|
||||
|
||||
if ((n = obt_xml_find_node(node, "desktop"))) {
|
||||
gchar *s;
|
||||
if ((s = obt_xml_node_string(n))) {
|
||||
if (!g_ascii_strcasecmp(s, "current"))
|
||||
o->desktop_current = TRUE;
|
||||
q->desktop_current = TRUE;
|
||||
if (!g_ascii_strcasecmp(s, "other"))
|
||||
o->desktop_other = TRUE;
|
||||
q->desktop_other = TRUE;
|
||||
else
|
||||
o->desktop_number = atoi(s);
|
||||
q->desktop_number = atoi(s);
|
||||
g_free(s);
|
||||
}
|
||||
}
|
||||
if ((n = obt_xml_find_node(node, "activedesktop"))) {
|
||||
o->screendesktop_number = obt_xml_node_int(n);
|
||||
q->screendesktop_number = obt_xml_node_int(n);
|
||||
}
|
||||
if ((n = obt_xml_find_node(node, "title"))) {
|
||||
gchar *s, *type = NULL;
|
||||
|
@ -99,19 +128,31 @@ static gpointer setup_func(xmlNodePtr node)
|
|||
if (!obt_xml_attr_string(n, "type", &type) ||
|
||||
!g_ascii_strcasecmp(type, "pattern"))
|
||||
{
|
||||
o->matchtitle = g_pattern_spec_new(s);
|
||||
q->matchtitle = g_pattern_spec_new(s);
|
||||
} else if (type && !g_ascii_strcasecmp(type, "regex")) {
|
||||
o->regextitle = g_regex_new(s, 0, 0, NULL);
|
||||
q->regextitle = g_regex_new(s, 0, 0, NULL);
|
||||
} else if (type && !g_ascii_strcasecmp(type, "exact")) {
|
||||
o->exacttitle = g_strdup(s);
|
||||
q->exacttitle = g_strdup(s);
|
||||
}
|
||||
g_free(s);
|
||||
}
|
||||
}
|
||||
if ((n = obt_xml_find_node(node, "monitor"))) {
|
||||
o->client_monitor = obt_xml_node_int(n);
|
||||
q->client_monitor = obt_xml_node_int(n);
|
||||
}
|
||||
}
|
||||
|
||||
static gpointer setup_func(xmlNodePtr node)
|
||||
{
|
||||
Options *o = g_slice_new0(Options);
|
||||
|
||||
gboolean zero_terminated = FALSE;
|
||||
gboolean clear_to_zero_on_alloc = FALSE;
|
||||
o->queries = g_array_new(zero_terminated,
|
||||
clear_to_zero_on_alloc,
|
||||
sizeof(Query*));
|
||||
|
||||
xmlNodePtr n;
|
||||
if ((n = obt_xml_find_node(node, "then"))) {
|
||||
xmlNodePtr m;
|
||||
|
||||
|
@ -133,6 +174,25 @@ static gpointer setup_func(xmlNodePtr node)
|
|||
}
|
||||
}
|
||||
|
||||
xmlNodePtr query_node = obt_xml_find_node(node, "query");
|
||||
if (!query_node) {
|
||||
/* The default query if none is specified. It uses the conditions
|
||||
found in the action's node. */
|
||||
setup_query(o,
|
||||
node,
|
||||
QUERY_TARGET_IS_ACTION_TARGET);
|
||||
} else {
|
||||
while (query_node) {
|
||||
QueryTarget query_target = QUERY_TARGET_IS_ACTION_TARGET;
|
||||
if (obt_xml_attr_contains(query_node, "target", "focus"))
|
||||
query_target = QUERY_TARGET_IS_FOCUS_TARGET;
|
||||
|
||||
setup_query(o, query_node->children, query_target);
|
||||
|
||||
query_node = obt_xml_find_node(query_node->next, "query");
|
||||
}
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
@ -140,6 +200,20 @@ static void free_func(gpointer options)
|
|||
{
|
||||
Options *o = options;
|
||||
|
||||
guint i;
|
||||
for (i = 0; i < o->queries->len; ++i) {
|
||||
Query *q = g_array_index(o->queries, Query*, i);
|
||||
|
||||
if (q->matchtitle)
|
||||
g_pattern_spec_free(q->matchtitle);
|
||||
if (q->regextitle)
|
||||
g_regex_unref(q->regextitle);
|
||||
if (q->exacttitle)
|
||||
g_free(q->exacttitle);
|
||||
|
||||
g_slice_free(Query, q);
|
||||
}
|
||||
|
||||
while (o->thenacts) {
|
||||
actions_act_unref(o->thenacts->data);
|
||||
o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts);
|
||||
|
@ -148,13 +222,8 @@ static void free_func(gpointer options)
|
|||
actions_act_unref(o->elseacts->data);
|
||||
o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts);
|
||||
}
|
||||
if (o->matchtitle)
|
||||
g_pattern_spec_free(o->matchtitle);
|
||||
if (o->regextitle)
|
||||
g_regex_unref(o->regextitle);
|
||||
if (o->exacttitle)
|
||||
g_free(o->exacttitle);
|
||||
|
||||
g_array_unref(o->queries);
|
||||
g_slice_free(Options, o);
|
||||
}
|
||||
|
||||
|
@ -162,52 +231,123 @@ static void free_func(gpointer options)
|
|||
static gboolean run_func(ObActionsData *data, gpointer options)
|
||||
{
|
||||
Options *o = options;
|
||||
GSList *acts;
|
||||
ObClient *c = data->client;
|
||||
ObClient *action_target = data->client;
|
||||
gboolean is_true = TRUE;
|
||||
|
||||
guint i;
|
||||
for (i = 0; i < o->queries->len; ++i) {
|
||||
Query *q = g_array_index(o->queries, Query*, i);
|
||||
ObClient *query_target = NULL;
|
||||
|
||||
switch (q->target) {
|
||||
case QUERY_TARGET_IS_ACTION_TARGET:
|
||||
query_target = data->client;
|
||||
break;
|
||||
case QUERY_TARGET_IS_FOCUS_TARGET:
|
||||
query_target = focus_client;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If there's no client to query, then false. */
|
||||
is_true &= query_target != NULL;
|
||||
|
||||
if (q->shaded_on)
|
||||
is_true &= query_target->shaded;
|
||||
if (q->shaded_off)
|
||||
is_true &= !query_target->shaded;
|
||||
|
||||
if (q->iconic_on)
|
||||
is_true &= query_target->iconic;
|
||||
if (q->iconic_off)
|
||||
is_true &= !query_target->iconic;
|
||||
|
||||
if (q->maxhorz_on)
|
||||
is_true &= query_target->max_horz;
|
||||
if (q->maxhorz_off)
|
||||
is_true &= !query_target->max_horz;
|
||||
|
||||
if (q->maxvert_on)
|
||||
is_true &= query_target->max_vert;
|
||||
if (q->maxvert_off)
|
||||
is_true &= !query_target->max_vert;
|
||||
|
||||
gboolean is_max_full =
|
||||
query_target->max_vert && query_target->max_horz;
|
||||
if (q->maxfull_on)
|
||||
is_true &= is_max_full;
|
||||
if (q->maxfull_off)
|
||||
is_true &= !is_max_full;
|
||||
|
||||
if (q->focused)
|
||||
is_true &= query_target == focus_client;
|
||||
if (q->unfocused)
|
||||
is_true &= query_target != focus_client;
|
||||
|
||||
gboolean is_urgent =
|
||||
query_target->urgent || query_target->demands_attention;
|
||||
if (q->urgent_on)
|
||||
is_true &= is_urgent;
|
||||
if (q->urgent_off)
|
||||
is_true &= !is_urgent;
|
||||
|
||||
gboolean has_visible_title_bar =
|
||||
!query_target->undecorated &&
|
||||
(query_target->decorations & OB_FRAME_DECOR_TITLEBAR);
|
||||
if (q->decor_on)
|
||||
is_true &= has_visible_title_bar;
|
||||
if (q->decor_off)
|
||||
is_true &= !has_visible_title_bar;
|
||||
|
||||
if (q->omnipresent_on)
|
||||
is_true &= query_target->desktop == DESKTOP_ALL;
|
||||
if (q->omnipresent_off)
|
||||
is_true &= query_target->desktop != DESKTOP_ALL;
|
||||
|
||||
gboolean is_on_current_desktop =
|
||||
query_target->desktop == screen_desktop ||
|
||||
query_target->desktop == DESKTOP_ALL;
|
||||
if (q->desktop_current)
|
||||
is_true &= is_on_current_desktop;
|
||||
if (q->desktop_other)
|
||||
is_true &= !is_on_current_desktop;
|
||||
|
||||
if (q->desktop_number) {
|
||||
gboolean is_on_desktop =
|
||||
query_target->desktop == q->desktop_number - 1 ||
|
||||
query_target->desktop == DESKTOP_ALL;
|
||||
is_true &= is_on_desktop;
|
||||
}
|
||||
|
||||
if (q->screendesktop_number)
|
||||
is_true &= screen_desktop == q->screendesktop_number - 1;
|
||||
|
||||
if (q->matchtitle) {
|
||||
is_true &= g_pattern_match_string(q->matchtitle,
|
||||
query_target->original_title);
|
||||
}
|
||||
if (q->regextitle) {
|
||||
is_true &= g_regex_match(q->regextitle,
|
||||
query_target->original_title,
|
||||
0,
|
||||
NULL);
|
||||
}
|
||||
if (q->exacttitle)
|
||||
is_true &= !strcmp(q->exacttitle, query_target->original_title);
|
||||
|
||||
if (q->client_monitor)
|
||||
is_true &= client_monitor(query_target) == q->client_monitor - 1;
|
||||
|
||||
if (c &&
|
||||
(!o->shaded_on || c->shaded) &&
|
||||
(!o->shaded_off || !c->shaded) &&
|
||||
(!o->iconic_on || c->iconic) &&
|
||||
(!o->iconic_off || !c->iconic) &&
|
||||
(!o->maxhorz_on || c->max_horz) &&
|
||||
(!o->maxhorz_off || !c->max_horz) &&
|
||||
(!o->maxvert_on || c->max_vert) &&
|
||||
(!o->maxvert_off || !c->max_vert) &&
|
||||
(!o->maxfull_on || (c->max_vert && c->max_horz)) &&
|
||||
(!o->maxfull_off || !(c->max_vert && c->max_horz)) &&
|
||||
(!o->focused || (c == focus_client)) &&
|
||||
(!o->unfocused || !(c == focus_client)) &&
|
||||
(!o->urgent_on || (c->urgent || c->demands_attention)) &&
|
||||
(!o->urgent_off || !(c->urgent || c->demands_attention)) &&
|
||||
(!o->decor_off || (c->undecorated || !(c->decorations & OB_FRAME_DECOR_TITLEBAR))) &&
|
||||
(!o->decor_on || (!c->undecorated && (c->decorations & OB_FRAME_DECOR_TITLEBAR))) &&
|
||||
(!o->omnipresent_on || (c->desktop == DESKTOP_ALL)) &&
|
||||
(!o->omnipresent_off || (c->desktop != DESKTOP_ALL)) &&
|
||||
(!o->desktop_current || ((c->desktop == screen_desktop) ||
|
||||
(c->desktop == DESKTOP_ALL))) &&
|
||||
(!o->desktop_other || ((c->desktop != screen_desktop) &&
|
||||
(c->desktop != DESKTOP_ALL))) &&
|
||||
(!o->desktop_number || ((c->desktop == o->desktop_number - 1) ||
|
||||
(c->desktop == DESKTOP_ALL))) &&
|
||||
(!o->screendesktop_number || screen_desktop == o->screendesktop_number - 1) &&
|
||||
(!o->matchtitle ||
|
||||
(g_pattern_match_string(o->matchtitle, c->original_title))) &&
|
||||
(!o->regextitle ||
|
||||
(g_regex_match(o->regextitle, c->original_title, 0, NULL))) &&
|
||||
(!o->exacttitle ||
|
||||
(!strcmp(o->exacttitle, c->original_title))) &&
|
||||
(!o->client_monitor ||
|
||||
(o->client_monitor == client_monitor(c) + 1)))
|
||||
{
|
||||
acts = o->thenacts;
|
||||
}
|
||||
|
||||
GSList *acts;
|
||||
if (is_true)
|
||||
acts = o->thenacts;
|
||||
else
|
||||
acts = o->elseacts;
|
||||
|
||||
actions_run_acts(acts, data->uact, data->state,
|
||||
data->x, data->y, data->button,
|
||||
data->context, data->client);
|
||||
data->context, action_target);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue