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
;
67 gboolean omnipresent_on
;
68 gboolean omnipresent_off
;
69 gboolean desktop_current
;
70 gboolean desktop_other
;
72 guint screendesktop_number
;
87 static gpointer
setup_func(xmlNodePtr node
);
88 static void free_func(gpointer options
);
89 static gboolean
run_func_if(ObActionsData
*data
, gpointer options
);
90 static gboolean
run_func_stop(ObActionsData
*data
, gpointer options
);
91 static gboolean
run_func_foreach(ObActionsData
*data
, gpointer options
);
93 void action_if_startup(void)
95 actions_register("If", setup_func
, free_func
, run_func_if
);
96 actions_register("Stop", NULL
, NULL
, run_func_stop
);
97 actions_register("ForEach", setup_func
, free_func
, run_func_foreach
);
99 actions_set_can_stop("Stop", TRUE
);
102 static inline void set_bool(xmlNodePtr node
,
109 if ((n
= obt_xml_find_node(node
, name
))) {
110 if (obt_xml_node_bool(n
))
117 static void setup_typed_match(TypedMatch
*tm
, xmlNodePtr n
)
120 if ((s
= obt_xml_node_string(n
))) {
122 if (!obt_xml_attr_string(n
, "type", &type
) ||
123 !g_ascii_strcasecmp(type
, "pattern"))
125 tm
->type
= MATCH_TYPE_PATTERN
;
126 tm
->m
.pattern
= g_pattern_spec_new(s
);
127 } else if (type
&& !g_ascii_strcasecmp(type
, "regex")) {
128 tm
->type
= MATCH_TYPE_REGEX
;
129 tm
->m
.regex
= g_regex_new(s
, 0, 0, NULL
);
130 } else if (type
&& !g_ascii_strcasecmp(type
, "exact")) {
131 tm
->type
= MATCH_TYPE_EXACT
;
132 tm
->m
.exact
= g_strdup(s
);
139 static void free_typed_match(TypedMatch
*tm
)
142 case MATCH_TYPE_PATTERN
:
143 g_pattern_spec_free(tm
->m
.pattern
);
145 case MATCH_TYPE_REGEX
:
146 g_regex_unref(tm
->m
.regex
);
148 case MATCH_TYPE_EXACT
:
151 case MATCH_TYPE_NONE
:
156 static gboolean
check_typed_match(TypedMatch
*tm
, gchar
*s
)
159 case MATCH_TYPE_PATTERN
:
160 return g_pattern_match_string(tm
->m
.pattern
, s
);
161 case MATCH_TYPE_REGEX
:
162 return g_regex_match(tm
->m
.regex
, s
, 0, NULL
);
163 case MATCH_TYPE_EXACT
:
164 return !strcmp(tm
->m
.exact
, s
);
165 case MATCH_TYPE_NONE
:
168 g_assert_not_reached();
171 static void setup_query(Options
* o
, xmlNodePtr node
, QueryTarget target
) {
172 Query
*q
= g_slice_new0(Query
);
173 g_array_append_val(o
->queries
, q
);
177 set_bool(node
, "shaded", &q
->shaded_on
, &q
->shaded_off
);
178 set_bool(node
, "maximized", &q
->maxfull_on
, &q
->maxfull_off
);
179 set_bool(node
, "maximizedhorizontal", &q
->maxhorz_on
, &q
->maxhorz_off
);
180 set_bool(node
, "maximizedvertical", &q
->maxvert_on
, &q
->maxvert_off
);
181 set_bool(node
, "iconified", &q
->iconic_on
, &q
->iconic_off
);
182 set_bool(node
, "focused", &q
->focused
, &q
->unfocused
);
183 set_bool(node
, "urgent", &q
->urgent_on
, &q
->urgent_off
);
184 set_bool(node
, "undecorated", &q
->decor_off
, &q
->decor_on
);
185 set_bool(node
, "omnipresent", &q
->omnipresent_on
, &q
->omnipresent_off
);
188 if ((n
= obt_xml_find_node(node
, "desktop"))) {
190 if ((s
= obt_xml_node_string(n
))) {
191 if (!g_ascii_strcasecmp(s
, "current"))
192 q
->desktop_current
= TRUE
;
193 if (!g_ascii_strcasecmp(s
, "other"))
194 q
->desktop_other
= TRUE
;
196 q
->desktop_number
= atoi(s
);
200 if ((n
= obt_xml_find_node(node
, "activedesktop"))) {
201 q
->screendesktop_number
= obt_xml_node_int(n
);
203 if ((n
= obt_xml_find_node(node
, "title"))) {
204 setup_typed_match(&q
->title
, n
);
206 if ((n
= obt_xml_find_node(node
, "class"))) {
207 setup_typed_match(&q
->class, n
);
209 if ((n
= obt_xml_find_node(node
, "name"))) {
210 setup_typed_match(&q
->name
, n
);
212 if ((n
= obt_xml_find_node(node
, "role"))) {
213 setup_typed_match(&q
->role
, n
);
215 if ((n
= obt_xml_find_node(node
, "monitor"))) {
216 q
->client_monitor
= obt_xml_node_int(n
);
220 static gpointer
setup_func(xmlNodePtr node
)
222 Options
*o
= g_slice_new0(Options
);
224 gboolean zero_terminated
= FALSE
;
225 gboolean clear_to_zero_on_alloc
= FALSE
;
226 o
->queries
= g_array_new(zero_terminated
,
227 clear_to_zero_on_alloc
,
231 if ((n
= obt_xml_find_node(node
, "then"))) {
234 m
= obt_xml_find_node(n
->children
, "action");
236 ObActionsAct
*action
= actions_parse(m
);
237 if (action
) o
->thenacts
= g_slist_append(o
->thenacts
, action
);
238 m
= obt_xml_find_node(m
->next
, "action");
241 if ((n
= obt_xml_find_node(node
, "else"))) {
244 m
= obt_xml_find_node(n
->children
, "action");
246 ObActionsAct
*action
= actions_parse(m
);
247 if (action
) o
->elseacts
= g_slist_append(o
->elseacts
, action
);
248 m
= obt_xml_find_node(m
->next
, "action");
252 xmlNodePtr query_node
= obt_xml_find_node(node
, "query");
254 /* The default query if none is specified. It uses the conditions
255 found in the action's node. */
258 QUERY_TARGET_IS_ACTION_TARGET
);
261 QueryTarget query_target
= QUERY_TARGET_IS_ACTION_TARGET
;
262 if (obt_xml_attr_contains(query_node
, "target", "focus"))
263 query_target
= QUERY_TARGET_IS_FOCUS_TARGET
;
265 setup_query(o
, query_node
->children
, query_target
);
267 query_node
= obt_xml_find_node(query_node
->next
, "query");
274 static void free_func(gpointer options
)
276 Options
*o
= options
;
279 for (i
= 0; i
< o
->queries
->len
; ++i
) {
280 Query
*q
= g_array_index(o
->queries
, Query
*, i
);
282 free_typed_match(&q
->title
);
283 free_typed_match(&q
->class);
284 free_typed_match(&q
->name
);
285 free_typed_match(&q
->role
);
287 g_slice_free(Query
, q
);
290 while (o
->thenacts
) {
291 actions_act_unref(o
->thenacts
->data
);
292 o
->thenacts
= g_slist_delete_link(o
->thenacts
, o
->thenacts
);
294 while (o
->elseacts
) {
295 actions_act_unref(o
->elseacts
->data
);
296 o
->elseacts
= g_slist_delete_link(o
->elseacts
, o
->elseacts
);
299 g_array_unref(o
->queries
);
300 g_slice_free(Options
, o
);
303 /* Always return FALSE because its not interactive */
304 static gboolean
run_func_if(ObActionsData
*data
, gpointer options
)
306 Options
*o
= options
;
307 ObClient
*action_target
= data
->client
;
308 gboolean is_true
= TRUE
;
311 for (i
= 0; i
< o
->queries
->len
; ++i
) {
312 Query
*q
= g_array_index(o
->queries
, Query
*, i
);
313 ObClient
*query_target
= NULL
;
316 case QUERY_TARGET_IS_ACTION_TARGET
:
317 query_target
= data
->client
;
319 case QUERY_TARGET_IS_FOCUS_TARGET
:
320 query_target
= focus_client
;
324 /* If there's no client to query, then false. */
325 is_true
&= query_target
!= NULL
;
328 is_true
&= query_target
->shaded
;
330 is_true
&= !query_target
->shaded
;
333 is_true
&= query_target
->iconic
;
335 is_true
&= !query_target
->iconic
;
338 is_true
&= query_target
->max_horz
;
340 is_true
&= !query_target
->max_horz
;
343 is_true
&= query_target
->max_vert
;
345 is_true
&= !query_target
->max_vert
;
347 gboolean is_max_full
=
348 query_target
->max_vert
&& query_target
->max_horz
;
350 is_true
&= is_max_full
;
352 is_true
&= !is_max_full
;
355 is_true
&= query_target
== focus_client
;
357 is_true
&= query_target
!= focus_client
;
360 query_target
->urgent
|| query_target
->demands_attention
;
362 is_true
&= is_urgent
;
364 is_true
&= !is_urgent
;
366 gboolean has_visible_title_bar
=
367 !query_target
->undecorated
&&
368 (query_target
->decorations
& OB_FRAME_DECOR_TITLEBAR
);
370 is_true
&= has_visible_title_bar
;
372 is_true
&= !has_visible_title_bar
;
374 if (q
->omnipresent_on
)
375 is_true
&= query_target
->desktop
== DESKTOP_ALL
;
376 if (q
->omnipresent_off
)
377 is_true
&= query_target
->desktop
!= DESKTOP_ALL
;
379 gboolean is_on_current_desktop
=
380 query_target
->desktop
== screen_desktop
||
381 query_target
->desktop
== DESKTOP_ALL
;
382 if (q
->desktop_current
)
383 is_true
&= is_on_current_desktop
;
384 if (q
->desktop_other
)
385 is_true
&= !is_on_current_desktop
;
387 if (q
->desktop_number
) {
388 gboolean is_on_desktop
=
389 query_target
->desktop
== q
->desktop_number
- 1 ||
390 query_target
->desktop
== DESKTOP_ALL
;
391 is_true
&= is_on_desktop
;
394 if (q
->screendesktop_number
)
395 is_true
&= screen_desktop
== q
->screendesktop_number
- 1;
397 is_true
&= check_typed_match(&q
->title
, query_target
->original_title
);
398 is_true
&= check_typed_match(&q
->class, query_target
->class);
399 is_true
&= check_typed_match(&q
->name
, query_target
->name
);
400 is_true
&= check_typed_match(&q
->role
, query_target
->role
);
402 if (q
->client_monitor
)
403 is_true
&= client_monitor(query_target
) == q
->client_monitor
- 1;
413 actions_run_acts(acts
, data
->uact
, data
->state
,
414 data
->x
, data
->y
, data
->button
,
415 data
->context
, action_target
);
420 static gboolean
run_func_foreach(ObActionsData
*data
, gpointer options
)
423 Options
*o
= options
;
427 for (it
= client_list
; it
; it
= g_list_next(it
)) {
428 data
->client
= it
->data
;
429 run_func_if(data
, options
);
438 static gboolean
run_func_stop(ObActionsData
*data
, gpointer options
)
440 Options
*o
= options
;
442 /* This stops the loop above so we don't invoke actions on any more
446 /* TRUE causes actions_run_acts to not run further actions on the current