X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fopenbox;a=blobdiff_plain;f=openbox%2Factions%2Fif.c;fp=openbox%2Factions%2Fif.c;h=a083d4853b23d0747d80dac20fe64590590e9d7a;hp=0e055a98f0099ea95828388f100febbf17525a37;hb=d67b818ae90c5574166d08422d3e9a68d8d65e16;hpb=9325c92056ff4268db2263cdf8c07bb529e158aa diff --git a/openbox/actions/if.c b/openbox/actions/if.c index 0e055a98..a083d485 100644 --- a/openbox/actions/if.c +++ b/openbox/actions/if.c @@ -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,29 @@ #include "openbox/focus.h" #include +typedef enum { + QUERY_TARGET_IS_ACTION_TARGET, + QUERY_TARGET_IS_FOCUS_TARGET, +} QueryTarget; + +typedef enum { + MATCH_TYPE_NONE = 0, + MATCH_TYPE_PATTERN, + MATCH_TYPE_REGEX, + MATCH_TYPE_EXACT, +} MatchType; + typedef struct { + MatchType type; + union m { + GPatternSpec *pattern; + GRegex *regex; + gchar *exact; + } m; +} TypedMatch; + +typedef struct { + QueryTarget target; gboolean shaded_on; gboolean shaded_off; gboolean maxvert_on; @@ -28,101 +69,169 @@ typedef struct { gboolean desktop_current; gboolean desktop_other; guint desktop_number; - GPatternSpec *matchtitle; + guint screendesktop_number; + guint client_monitor; + TypedMatch title; + TypedMatch class; + TypedMatch name; + TypedMatch role; + TypedMatch type; +} Query; + +typedef struct { + GArray* queries; GSList *thenacts; GSList *elseacts; + gboolean stop; } Options; static gpointer setup_func(xmlNodePtr node); static void free_func(gpointer options); -static gboolean run_func(ObActionsData *data, gpointer options); +static gboolean run_func_if(ObActionsData *data, gpointer options); +static gboolean run_func_stop(ObActionsData *data, gpointer options); +static gboolean run_func_foreach(ObActionsData *data, gpointer options); void action_if_startup(void) { - actions_register("If", setup_func, free_func, run_func); + actions_register("If", setup_func, free_func, run_func_if); + actions_register("Stop", NULL, NULL, run_func_stop); + actions_register("ForEach", setup_func, free_func, run_func_foreach); + + actions_set_can_stop("Stop", TRUE); } -static gpointer setup_func(xmlNodePtr node) +static inline void set_bool(xmlNodePtr node, + const char *name, + gboolean *on, + gboolean *off) { xmlNodePtr n; - Options *o; - - o = g_slice_new0(Options); - if ((n = obt_xml_find_node(node, "shaded"))) { + if ((n = obt_xml_find_node(node, name))) { if (obt_xml_node_bool(n)) - o->shaded_on = TRUE; + *on = TRUE; else - o->shaded_off = TRUE; + *off = TRUE; } - if ((n = obt_xml_find_node(node, "maximized"))) { - if (obt_xml_node_bool(n)) - o->maxfull_on = TRUE; - else - o->maxfull_off = TRUE; - } - if ((n = obt_xml_find_node(node, "maximizedhorizontal"))) { - if (obt_xml_node_bool(n)) - o->maxhorz_on = TRUE; - else - o->maxhorz_off = TRUE; - } - if ((n = obt_xml_find_node(node, "maximizedvertical"))) { - if (obt_xml_node_bool(n)) - o->maxvert_on = TRUE; - else - o->maxvert_off = TRUE; - } - if ((n = obt_xml_find_node(node, "iconified"))) { - if (obt_xml_node_bool(n)) - o->iconic_on = TRUE; - else - o->iconic_off = TRUE; - } - if ((n = obt_xml_find_node(node, "focused"))) { - if (obt_xml_node_bool(n)) - o->focused = TRUE; - else - o->unfocused = TRUE; +} + +static void setup_typed_match(TypedMatch *tm, xmlNodePtr n) +{ + gchar *s; + if ((s = obt_xml_node_string(n))) { + gchar *type = NULL; + if (!obt_xml_attr_string(n, "type", &type) || + !g_ascii_strcasecmp(type, "pattern")) + { + tm->type = MATCH_TYPE_PATTERN; + tm->m.pattern = g_pattern_spec_new(s); + } else if (type && !g_ascii_strcasecmp(type, "regex")) { + tm->type = MATCH_TYPE_REGEX; + tm->m.regex = g_regex_new(s, 0, 0, NULL); + } else if (type && !g_ascii_strcasecmp(type, "exact")) { + tm->type = MATCH_TYPE_EXACT; + tm->m.exact = g_strdup(s); + } + g_free(s); + g_free(type); } - if ((n = obt_xml_find_node(node, "urgent"))) { - if (obt_xml_node_bool(n)) - o->urgent_on = TRUE; - else - o->urgent_off = TRUE; +} + +static void free_typed_match(TypedMatch *tm) +{ + switch (tm->type) { + case MATCH_TYPE_PATTERN: + g_pattern_spec_free(tm->m.pattern); + break; + case MATCH_TYPE_REGEX: + g_regex_unref(tm->m.regex); + break; + case MATCH_TYPE_EXACT: + g_free(tm->m.exact); + break; + case MATCH_TYPE_NONE: + break; } - if ((n = obt_xml_find_node(node, "undecorated"))) { - if (obt_xml_node_bool(n)) - o->decor_off = TRUE; - else - o->decor_on = TRUE; +} + +static gboolean check_typed_match(TypedMatch *tm, const gchar *s) +{ + switch (tm->type) { + case MATCH_TYPE_PATTERN: + return g_pattern_match_string(tm->m.pattern, s); + case MATCH_TYPE_REGEX: + return g_regex_match(tm->m.regex, s, 0, NULL); + case MATCH_TYPE_EXACT: + return !strcmp(tm->m.exact, s); + case MATCH_TYPE_NONE: + return TRUE; } + g_assert_not_reached(); +} + +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; 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; - if (!g_ascii_strcasecmp(s, "other")) - o->desktop_other = TRUE; - else - o->desktop_number = atoi(s); - g_free(s); + if (!g_ascii_strcasecmp(s, "current")) + q->desktop_current = TRUE; + if (!g_ascii_strcasecmp(s, "other")) + q->desktop_other = TRUE; + else + q->desktop_number = atoi(s); + g_free(s); } } - if ((n = obt_xml_find_node(node, "omnipresent"))) { - if (obt_xml_node_bool(n)) - o->omnipresent_on = TRUE; - else - o->omnipresent_off = TRUE; + if ((n = obt_xml_find_node(node, "activedesktop"))) { + q->screendesktop_number = obt_xml_node_int(n); } if ((n = obt_xml_find_node(node, "title"))) { - gchar *s; - if ((s = obt_xml_node_string(n))) { - o->matchtitle = g_pattern_spec_new(s); - g_free(s); - } + setup_typed_match(&q->title, n); + } + if ((n = obt_xml_find_node(node, "class"))) { + setup_typed_match(&q->class, n); + } + if ((n = obt_xml_find_node(node, "name"))) { + setup_typed_match(&q->name, n); } + if ((n = obt_xml_find_node(node, "role"))) { + setup_typed_match(&q->role, n); + } + if ((n = obt_xml_find_node(node, "type"))) { + setup_typed_match(&q->type, n); + } + if ((n = obt_xml_find_node(node, "monitor"))) { + 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; @@ -144,6 +253,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; } @@ -151,6 +279,19 @@ 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); + + free_typed_match(&q->title); + free_typed_match(&q->class); + free_typed_match(&q->name); + free_typed_match(&q->role); + free_typed_match(&q->type); + + g_slice_free(Query, q); + } + while (o->thenacts) { actions_act_unref(o->thenacts->data); o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts); @@ -159,55 +300,157 @@ 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); + g_array_unref(o->queries); g_slice_free(Options, o); } /* Always return FALSE because its not interactive */ -static gboolean run_func(ObActionsData *data, gpointer options) +static gboolean run_func_if(ObActionsData *data, gpointer options) { Options *o = options; + 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; + + is_true &= check_typed_match(&q->title, query_target->original_title); + is_true &= check_typed_match(&q->class, query_target->class); + is_true &= check_typed_match(&q->name, query_target->name); + is_true &= check_typed_match(&q->role, query_target->role); + is_true &= check_typed_match(&q->type, + client_type_to_string(query_target)); + + if (q->client_monitor) + is_true &= client_monitor(query_target) == q->client_monitor - 1; + + } + GSList *acts; - ObClient *c = data->client; - - 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->matchtitle || - (g_pattern_match_string(o->matchtitle, c->original_title)))) - { + 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; +} + +static gboolean run_func_foreach(ObActionsData *data, gpointer options) +{ + GList *it; + Options *o = options; + + o->stop = FALSE; + + for (it = client_list; it; it = g_list_next(it)) { + data->client = it->data; + run_func_if(data, options); + if (o->stop) { + break; + } + } return FALSE; } + +static gboolean run_func_stop(ObActionsData *data, gpointer options) +{ + Options *o = options; + + /* This stops the loop above so we don't invoke actions on any more + clients */ + o->stop = TRUE; + + /* TRUE causes actions_run_acts to not run further actions on the current + client */ + return TRUE; +}