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,
43 GPatternSpec *pattern;
69 gboolean omnipresent_on;
70 gboolean omnipresent_off;
71 gboolean desktop_current;
72 gboolean desktop_other;
74 guint screendesktop_number;
90 static gpointer setup_func(xmlNodePtr node);
91 static void free_func(gpointer options);
92 static gboolean run_func_if(ObActionsData *data, gpointer options);
93 static gboolean run_func_continue(ObActionsData *data, gpointer options);
94 static gboolean run_func_stop(ObActionsData *data, gpointer options);
95 static gboolean run_func_foreach(ObActionsData *data, gpointer options);
96 static gboolean run_func_group(ObActionsData *data, gpointer options);
98 static gboolean foreach_stop;
100 void action_if_startup(void)
102 actions_register("If", setup_func, free_func, run_func_if);
103 actions_register("Stop", NULL, NULL, run_func_stop);
104 actions_register("Continue", NULL, NULL, run_func_continue);
105 actions_register("ForEach", setup_func, free_func, run_func_foreach);
106 //actions_register("GroupMembers", setup_func, free_func, run_func_group);
109 static inline void set_bool(xmlNodePtr node,
116 if ((n = obt_xml_find_node(node, name))) {
117 if (obt_xml_node_bool(n))
124 static void setup_typed_match(TypedMatch *tm, xmlNodePtr n)
127 if ((s = obt_xml_node_string(n))) {
129 if (!obt_xml_attr_string(n, "type", &type) ||
130 !g_ascii_strcasecmp(type, "pattern"))
132 tm->type = MATCH_TYPE_PATTERN;
133 tm->m.pattern = g_pattern_spec_new(s);
134 } else if (type && !g_ascii_strcasecmp(type, "regex")) {
135 tm->type = MATCH_TYPE_REGEX;
136 tm->m.regex = g_regex_new(s, 0, 0, NULL);
137 } else if (type && !g_ascii_strcasecmp(type, "exact")) {
138 tm->type = MATCH_TYPE_EXACT;
139 tm->m.exact = g_strdup(s);
146 static void free_typed_match(TypedMatch *tm)
149 case MATCH_TYPE_PATTERN:
150 g_pattern_spec_free(tm->m.pattern);
152 case MATCH_TYPE_REGEX:
153 g_regex_unref(tm->m.regex);
155 case MATCH_TYPE_EXACT:
158 case MATCH_TYPE_NONE:
163 static gboolean check_typed_match(TypedMatch *tm, const gchar *s)
166 case MATCH_TYPE_PATTERN:
167 return g_pattern_match_string(tm->m.pattern, s);
168 case MATCH_TYPE_REGEX:
169 return g_regex_match(tm->m.regex, s, 0, NULL);
170 case MATCH_TYPE_EXACT:
171 return !strcmp(tm->m.exact, s);
172 case MATCH_TYPE_NONE:
175 g_assert_not_reached();
178 static void setup_query(Options* o, xmlNodePtr node, QueryTarget target) {
179 Query *q = g_slice_new0(Query);
180 g_array_append_val(o->queries, q);
184 set_bool(node, "shaded", &q->shaded_on, &q->shaded_off);
185 set_bool(node, "maximized", &q->maxfull_on, &q->maxfull_off);
186 set_bool(node, "maximizedhorizontal", &q->maxhorz_on, &q->maxhorz_off);
187 set_bool(node, "maximizedvertical", &q->maxvert_on, &q->maxvert_off);
188 set_bool(node, "iconified", &q->iconic_on, &q->iconic_off);
189 set_bool(node, "locked", &q->locked_on, &q->locked_off);
190 set_bool(node, "focused", &q->focused, &q->unfocused);
191 set_bool(node, "urgent", &q->urgent_on, &q->urgent_off);
192 set_bool(node, "undecorated", &q->decor_off, &q->decor_on);
193 set_bool(node, "omnipresent", &q->omnipresent_on, &q->omnipresent_off);
196 if ((n = obt_xml_find_node(node, "desktop"))) {
198 if ((s = obt_xml_node_string(n))) {
199 if (!g_ascii_strcasecmp(s, "current"))
200 q->desktop_current = TRUE;
201 if (!g_ascii_strcasecmp(s, "other"))
202 q->desktop_other = TRUE;
204 q->desktop_number = atoi(s);
208 if ((n = obt_xml_find_node(node, "screendesktop"))) {
210 if ((s = obt_xml_node_string(n))) {
211 q->screendesktop_number = atoi(s);
215 if ((n = obt_xml_find_node(node, "activedesktop"))) {
216 q->screendesktop_number = obt_xml_node_int(n);
218 if ((n = obt_xml_find_node(node, "title"))) {
219 setup_typed_match(&q->title, n);
221 if ((n = obt_xml_find_node(node, "class"))) {
222 setup_typed_match(&q->class, n);
224 if ((n = obt_xml_find_node(node, "name"))) {
225 setup_typed_match(&q->name, n);
227 if ((n = obt_xml_find_node(node, "role"))) {
228 setup_typed_match(&q->role, n);
230 if ((n = obt_xml_find_node(node, "type"))) {
231 setup_typed_match(&q->type, n);
233 if ((n = obt_xml_find_node(node, "monitor"))) {
234 q->client_monitor = obt_xml_node_int(n);
238 static gpointer setup_func(xmlNodePtr node)
240 Options *o = g_slice_new0(Options);
242 gboolean zero_terminated = FALSE;
243 gboolean clear_to_zero_on_alloc = FALSE;
244 o->queries = g_array_new(zero_terminated,
245 clear_to_zero_on_alloc,
249 if ((n = obt_xml_find_node(node, "then"))) {
252 m = obt_xml_find_node(n->children, "action");
254 ObActionsAct *action = actions_parse(m);
255 if (action) o->thenacts = g_slist_append(o->thenacts, action);
256 m = obt_xml_find_node(m->next, "action");
259 if ((n = obt_xml_find_node(node, "else"))) {
262 m = obt_xml_find_node(n->children, "action");
264 ObActionsAct *action = actions_parse(m);
265 if (action) o->elseacts = g_slist_append(o->elseacts, action);
266 m = obt_xml_find_node(m->next, "action");
270 xmlNodePtr query_node = obt_xml_find_node(node, "query");
272 /* The default query if none is specified. It uses the conditions
273 found in the action's node. */
276 QUERY_TARGET_IS_ACTION_TARGET);
279 QueryTarget query_target = QUERY_TARGET_IS_ACTION_TARGET;
280 if (obt_xml_attr_contains(query_node, "target", "focus"))
281 query_target = QUERY_TARGET_IS_FOCUS_TARGET;
283 setup_query(o, query_node->children, query_target);
285 query_node = obt_xml_find_node(query_node->next, "query");
292 static void free_func(gpointer options)
294 Options *o = options;
297 for (i = 0; i < o->queries->len; ++i) {
298 Query *q = g_array_index(o->queries, Query*, i);
300 free_typed_match(&q->title);
301 free_typed_match(&q->class);
302 free_typed_match(&q->name);
303 free_typed_match(&q->role);
304 free_typed_match(&q->type);
306 g_slice_free(Query, q);
309 while (o->thenacts) {
310 actions_act_unref(o->thenacts->data);
311 o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts);
313 while (o->elseacts) {
314 actions_act_unref(o->elseacts->data);
315 o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts);
318 g_array_unref(o->queries);
319 g_slice_free(Options, o);
322 /* Always return FALSE because its not interactive */
323 static gboolean run_func_if(ObActionsData *data, gpointer options)
325 Options *o = options;
326 ObClient *action_target = data->client;
327 gboolean is_true = TRUE;
330 for (i = 0; is_true && i < o->queries->len; ++i) {
331 Query *q = g_array_index(o->queries, Query*, i);
332 ObClient *query_target = NULL;
335 case QUERY_TARGET_IS_ACTION_TARGET:
336 query_target = data->client;
338 case QUERY_TARGET_IS_FOCUS_TARGET:
339 query_target = focus_client;
343 /* If there's no client to query, then false. */
350 is_true &= query_target->shaded;
352 is_true &= !query_target->shaded;
355 is_true &= query_target->iconic;
357 is_true &= !query_target->iconic;
360 is_true &= query_target->locked;
362 is_true &= !query_target->locked;
365 is_true &= query_target->max_horz;
367 is_true &= !query_target->max_horz;
370 is_true &= query_target->max_vert;
372 is_true &= !query_target->max_vert;
374 gboolean is_max_full =
375 query_target->max_vert && query_target->max_horz;
377 is_true &= is_max_full;
379 is_true &= !is_max_full;
382 is_true &= query_target == focus_client;
384 is_true &= query_target != focus_client;
387 query_target->urgent || query_target->demands_attention;
389 is_true &= is_urgent;
391 is_true &= !is_urgent;
393 gboolean has_visible_title_bar =
394 !query_target->undecorated &&
395 (query_target->decorations & OB_FRAME_DECOR_TITLEBAR);
397 is_true &= has_visible_title_bar;
399 is_true &= !has_visible_title_bar;
401 if (q->omnipresent_on)
402 is_true &= query_target->desktop == DESKTOP_ALL;
403 if (q->omnipresent_off)
404 is_true &= query_target->desktop != DESKTOP_ALL;
406 gboolean is_on_current_desktop =
407 query_target->desktop == screen_desktop ||
408 query_target->desktop == DESKTOP_ALL;
409 if (q->desktop_current)
410 is_true &= is_on_current_desktop;
411 if (q->desktop_other)
412 is_true &= !is_on_current_desktop;
414 if (q->desktop_number) {
415 gboolean is_on_desktop =
416 query_target->desktop == q->desktop_number - 1 ||
417 query_target->desktop == DESKTOP_ALL;
418 is_true &= is_on_desktop;
421 if (q->screendesktop_number)
422 is_true &= screen_desktop == q->screendesktop_number - 1;
424 is_true &= check_typed_match(&q->title, query_target->original_title);
425 is_true &= check_typed_match(&q->class, query_target->class);
426 is_true &= check_typed_match(&q->name, query_target->name);
427 is_true &= check_typed_match(&q->role, query_target->role);
428 is_true &= check_typed_match(&q->type,
429 client_type_to_string(query_target));
431 if (q->client_monitor)
432 is_true &= client_monitor(query_target) == q->client_monitor - 1;
442 actions_run_acts(acts, data->uact, data->state,
443 data->x, data->y, data->button,
444 data->context, action_target);
449 static gboolean run_func_foreach(ObActionsData *data, gpointer options)
452 Options *o = options;
456 for (it = client_list; it; it = g_list_next(it)) {
457 data->client = it->data;
458 run_func_if(data, options);
467 static gboolean run_func_continue(ObActionsData *data, gpointer options)
469 actions_stop_running();
473 static gboolean run_func_group(ObActionsData *data, gpointer acts)
475 GSList *it, *a = acts;
476 ObClient *c = data->client;
479 for (it = c->group->members; it; it = g_slist_next(it)) {
480 ObClient *member = it->data;
481 if (actions_run_acts(a, data->uact, data->state,
482 data->x, data->y, data->button,
483 data->context, member))
491 static gboolean run_func_stop(ObActionsData *data, gpointer options)
493 Options *o = options;
495 /* This stops the loop above so we don't invoke actions on any more
499 /* TRUE causes actions_run_acts to not run further actions on the current