1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 if.c for the Openbox window manager
4 Copyright (c) 2007 Mikael Magnusson
5 Copyright (c) 2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
20 #include "openbox/actions.h"
21 #include "openbox/misc.h"
22 #include "openbox/client.h"
23 #include "openbox/frame.h"
24 #include "openbox/screen.h"
25 #include "openbox/focus.h"
29 QUERY_TARGET_IS_ACTION_TARGET,
30 QUERY_TARGET_IS_FOCUS_TARGET,
51 gboolean omnipresent_on;
52 gboolean omnipresent_off;
53 gboolean desktop_current;
54 gboolean desktop_other;
56 guint screendesktop_number;
58 GPatternSpec *matchtitle;
70 static gpointer setup_func(xmlNodePtr node);
71 static void free_func(gpointer options);
72 static gboolean run_func_if(ObActionsData *data, gpointer options);
73 static gboolean run_func_stop(ObActionsData *data, gpointer options);
74 static gboolean run_func_foreach(ObActionsData *data, gpointer options);
76 void action_if_startup(void)
78 actions_register("If", setup_func, free_func, run_func_if);
79 actions_register("Stop", NULL, NULL, run_func_stop);
80 actions_register("ForEach", setup_func, free_func, run_func_foreach);
82 actions_set_can_stop("Stop", TRUE);
85 static inline void set_bool(xmlNodePtr node,
92 if ((n = obt_xml_find_node(node, name))) {
93 if (obt_xml_node_bool(n))
100 static void setup_query(Options* o, xmlNodePtr node, QueryTarget target) {
101 Query *q = g_slice_new0(Query);
102 g_array_append_val(o->queries, q);
106 set_bool(node, "shaded", &q->shaded_on, &q->shaded_off);
107 set_bool(node, "maximized", &q->maxfull_on, &q->maxfull_off);
108 set_bool(node, "maximizedhorizontal", &q->maxhorz_on, &q->maxhorz_off);
109 set_bool(node, "maximizedvertical", &q->maxvert_on, &q->maxvert_off);
110 set_bool(node, "iconified", &q->iconic_on, &q->iconic_off);
111 set_bool(node, "focused", &q->focused, &q->unfocused);
112 set_bool(node, "urgent", &q->urgent_on, &q->urgent_off);
113 set_bool(node, "undecorated", &q->decor_off, &q->decor_on);
114 set_bool(node, "omnipresent", &q->omnipresent_on, &q->omnipresent_off);
117 if ((n = obt_xml_find_node(node, "desktop"))) {
119 if ((s = obt_xml_node_string(n))) {
120 if (!g_ascii_strcasecmp(s, "current"))
121 q->desktop_current = TRUE;
122 if (!g_ascii_strcasecmp(s, "other"))
123 q->desktop_other = TRUE;
125 q->desktop_number = atoi(s);
129 if ((n = obt_xml_find_node(node, "activedesktop"))) {
130 q->screendesktop_number = obt_xml_node_int(n);
132 if ((n = obt_xml_find_node(node, "title"))) {
133 gchar *s, *type = NULL;
134 if ((s = obt_xml_node_string(n))) {
135 if (!obt_xml_attr_string(n, "type", &type) ||
136 !g_ascii_strcasecmp(type, "pattern"))
138 q->matchtitle = g_pattern_spec_new(s);
139 } else if (type && !g_ascii_strcasecmp(type, "regex")) {
140 q->regextitle = g_regex_new(s, 0, 0, NULL);
141 } else if (type && !g_ascii_strcasecmp(type, "exact")) {
142 q->exacttitle = g_strdup(s);
147 if ((n = obt_xml_find_node(node, "monitor"))) {
148 q->client_monitor = obt_xml_node_int(n);
152 static gpointer setup_func(xmlNodePtr node)
154 Options *o = g_slice_new0(Options);
156 gboolean zero_terminated = FALSE;
157 gboolean clear_to_zero_on_alloc = FALSE;
158 o->queries = g_array_new(zero_terminated,
159 clear_to_zero_on_alloc,
163 if ((n = obt_xml_find_node(node, "then"))) {
166 m = obt_xml_find_node(n->children, "action");
168 ObActionsAct *action = actions_parse(m);
169 if (action) o->thenacts = g_slist_append(o->thenacts, action);
170 m = obt_xml_find_node(m->next, "action");
173 if ((n = obt_xml_find_node(node, "else"))) {
176 m = obt_xml_find_node(n->children, "action");
178 ObActionsAct *action = actions_parse(m);
179 if (action) o->elseacts = g_slist_append(o->elseacts, action);
180 m = obt_xml_find_node(m->next, "action");
184 xmlNodePtr query_node = obt_xml_find_node(node, "query");
186 /* The default query if none is specified. It uses the conditions
187 found in the action's node. */
190 QUERY_TARGET_IS_ACTION_TARGET);
193 QueryTarget query_target = QUERY_TARGET_IS_ACTION_TARGET;
194 if (obt_xml_attr_contains(query_node, "target", "focus"))
195 query_target = QUERY_TARGET_IS_FOCUS_TARGET;
197 setup_query(o, query_node->children, query_target);
199 query_node = obt_xml_find_node(query_node->next, "query");
206 static void free_func(gpointer options)
208 Options *o = options;
211 for (i = 0; i < o->queries->len; ++i) {
212 Query *q = g_array_index(o->queries, Query*, i);
215 g_pattern_spec_free(q->matchtitle);
217 g_regex_unref(q->regextitle);
219 g_free(q->exacttitle);
221 g_slice_free(Query, q);
224 while (o->thenacts) {
225 actions_act_unref(o->thenacts->data);
226 o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts);
228 while (o->elseacts) {
229 actions_act_unref(o->elseacts->data);
230 o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts);
233 g_array_unref(o->queries);
234 g_slice_free(Options, o);
237 /* Always return FALSE because its not interactive */
238 static gboolean run_func_if(ObActionsData *data, gpointer options)
240 Options *o = options;
241 ObClient *action_target = data->client;
242 gboolean is_true = TRUE;
245 for (i = 0; i < o->queries->len; ++i) {
246 Query *q = g_array_index(o->queries, Query*, i);
247 ObClient *query_target = NULL;
250 case QUERY_TARGET_IS_ACTION_TARGET:
251 query_target = data->client;
253 case QUERY_TARGET_IS_FOCUS_TARGET:
254 query_target = focus_client;
258 /* If there's no client to query, then false. */
259 is_true &= query_target != NULL;
262 is_true &= query_target->shaded;
264 is_true &= !query_target->shaded;
267 is_true &= query_target->iconic;
269 is_true &= !query_target->iconic;
272 is_true &= query_target->max_horz;
274 is_true &= !query_target->max_horz;
277 is_true &= query_target->max_vert;
279 is_true &= !query_target->max_vert;
281 gboolean is_max_full =
282 query_target->max_vert && query_target->max_horz;
284 is_true &= is_max_full;
286 is_true &= !is_max_full;
289 is_true &= query_target == focus_client;
291 is_true &= query_target != focus_client;
294 query_target->urgent || query_target->demands_attention;
296 is_true &= is_urgent;
298 is_true &= !is_urgent;
300 gboolean has_visible_title_bar =
301 !query_target->undecorated &&
302 (query_target->decorations & OB_FRAME_DECOR_TITLEBAR);
304 is_true &= has_visible_title_bar;
306 is_true &= !has_visible_title_bar;
308 if (q->omnipresent_on)
309 is_true &= query_target->desktop == DESKTOP_ALL;
310 if (q->omnipresent_off)
311 is_true &= query_target->desktop != DESKTOP_ALL;
313 gboolean is_on_current_desktop =
314 query_target->desktop == screen_desktop ||
315 query_target->desktop == DESKTOP_ALL;
316 if (q->desktop_current)
317 is_true &= is_on_current_desktop;
318 if (q->desktop_other)
319 is_true &= !is_on_current_desktop;
321 if (q->desktop_number) {
322 gboolean is_on_desktop =
323 query_target->desktop == q->desktop_number - 1 ||
324 query_target->desktop == DESKTOP_ALL;
325 is_true &= is_on_desktop;
328 if (q->screendesktop_number)
329 is_true &= screen_desktop == q->screendesktop_number - 1;
332 is_true &= g_pattern_match_string(q->matchtitle,
333 query_target->original_title);
336 is_true &= g_regex_match(q->regextitle,
337 query_target->original_title,
342 is_true &= !strcmp(q->exacttitle, query_target->original_title);
344 if (q->client_monitor)
345 is_true &= client_monitor(query_target) == q->client_monitor - 1;
355 actions_run_acts(acts, data->uact, data->state,
356 data->x, data->y, data->button,
357 data->context, action_target);
362 static gboolean run_func_foreach(ObActionsData *data, gpointer options)
365 Options *o = options;
369 for (it = client_list; it; it = g_list_next(it)) {
370 data->client = it->data;
371 run_func_if(data, options);
380 static gboolean run_func_stop(ObActionsData *data, gpointer options)
382 Options *o = options;
384 /* This stops the loop above so we don't invoke actions on any more
388 /* TRUE causes actions_run_acts to not run further actions on the current