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
;
88 static gpointer
setup_func(xmlNodePtr node
);
89 static void free_func(gpointer options
);
90 static gboolean
run_func_if(ObActionsData
*data
, gpointer options
);
91 static gboolean
run_func_stop(ObActionsData
*data
, gpointer options
);
92 static gboolean
run_func_foreach(ObActionsData
*data
, gpointer options
);
94 void action_if_startup(void)
96 actions_register("If", setup_func
, free_func
, run_func_if
);
97 actions_register("Stop", NULL
, NULL
, run_func_stop
);
98 actions_register("ForEach", setup_func
, free_func
, run_func_foreach
);
100 actions_set_can_stop("Stop", TRUE
);
103 static inline void set_bool(xmlNodePtr node
,
110 if ((n
= obt_xml_find_node(node
, name
))) {
111 if (obt_xml_node_bool(n
))
118 static void setup_typed_match(TypedMatch
*tm
, xmlNodePtr n
)
121 if ((s
= obt_xml_node_string(n
))) {
123 if (!obt_xml_attr_string(n
, "type", &type
) ||
124 !g_ascii_strcasecmp(type
, "pattern"))
126 tm
->type
= MATCH_TYPE_PATTERN
;
127 tm
->m
.pattern
= g_pattern_spec_new(s
);
128 } else if (type
&& !g_ascii_strcasecmp(type
, "regex")) {
129 tm
->type
= MATCH_TYPE_REGEX
;
130 tm
->m
.regex
= g_regex_new(s
, 0, 0, NULL
);
131 } else if (type
&& !g_ascii_strcasecmp(type
, "exact")) {
132 tm
->type
= MATCH_TYPE_EXACT
;
133 tm
->m
.exact
= g_strdup(s
);
140 static void free_typed_match(TypedMatch
*tm
)
143 case MATCH_TYPE_PATTERN
:
144 g_pattern_spec_free(tm
->m
.pattern
);
146 case MATCH_TYPE_REGEX
:
147 g_regex_unref(tm
->m
.regex
);
149 case MATCH_TYPE_EXACT
:
152 case MATCH_TYPE_NONE
:
157 static gboolean
check_typed_match(TypedMatch
*tm
, const gchar
*s
)
160 case MATCH_TYPE_PATTERN
:
161 return g_pattern_match_string(tm
->m
.pattern
, s
);
162 case MATCH_TYPE_REGEX
:
163 return g_regex_match(tm
->m
.regex
, s
, 0, NULL
);
164 case MATCH_TYPE_EXACT
:
165 return !strcmp(tm
->m
.exact
, s
);
166 case MATCH_TYPE_NONE
:
169 g_assert_not_reached();
172 static void setup_query(Options
* o
, xmlNodePtr node
, QueryTarget target
) {
173 Query
*q
= g_slice_new0(Query
);
174 g_array_append_val(o
->queries
, q
);
178 set_bool(node
, "shaded", &q
->shaded_on
, &q
->shaded_off
);
179 set_bool(node
, "maximized", &q
->maxfull_on
, &q
->maxfull_off
);
180 set_bool(node
, "maximizedhorizontal", &q
->maxhorz_on
, &q
->maxhorz_off
);
181 set_bool(node
, "maximizedvertical", &q
->maxvert_on
, &q
->maxvert_off
);
182 set_bool(node
, "iconified", &q
->iconic_on
, &q
->iconic_off
);
183 set_bool(node
, "focused", &q
->focused
, &q
->unfocused
);
184 set_bool(node
, "urgent", &q
->urgent_on
, &q
->urgent_off
);
185 set_bool(node
, "undecorated", &q
->decor_off
, &q
->decor_on
);
186 set_bool(node
, "omnipresent", &q
->omnipresent_on
, &q
->omnipresent_off
);
189 if ((n
= obt_xml_find_node(node
, "desktop"))) {
191 if ((s
= obt_xml_node_string(n
))) {
192 if (!g_ascii_strcasecmp(s
, "current"))
193 q
->desktop_current
= TRUE
;
194 if (!g_ascii_strcasecmp(s
, "other"))
195 q
->desktop_other
= TRUE
;
197 q
->desktop_number
= atoi(s
);
201 if ((n
= obt_xml_find_node(node
, "activedesktop"))) {
202 q
->screendesktop_number
= obt_xml_node_int(n
);
204 if ((n
= obt_xml_find_node(node
, "title"))) {
205 setup_typed_match(&q
->title
, n
);
207 if ((n
= obt_xml_find_node(node
, "class"))) {
208 setup_typed_match(&q
->class, n
);
210 if ((n
= obt_xml_find_node(node
, "name"))) {
211 setup_typed_match(&q
->name
, n
);
213 if ((n
= obt_xml_find_node(node
, "role"))) {
214 setup_typed_match(&q
->role
, n
);
216 if ((n
= obt_xml_find_node(node
, "type"))) {
217 setup_typed_match(&q
->type
, n
);
219 if ((n
= obt_xml_find_node(node
, "monitor"))) {
220 q
->client_monitor
= obt_xml_node_int(n
);
224 static gpointer
setup_func(xmlNodePtr node
)
226 Options
*o
= g_slice_new0(Options
);
228 gboolean zero_terminated
= FALSE
;
229 gboolean clear_to_zero_on_alloc
= FALSE
;
230 o
->queries
= g_array_new(zero_terminated
,
231 clear_to_zero_on_alloc
,
235 if ((n
= obt_xml_find_node(node
, "then"))) {
238 m
= obt_xml_find_node(n
->children
, "action");
240 ObActionsAct
*action
= actions_parse(m
);
241 if (action
) o
->thenacts
= g_slist_append(o
->thenacts
, action
);
242 m
= obt_xml_find_node(m
->next
, "action");
245 if ((n
= obt_xml_find_node(node
, "else"))) {
248 m
= obt_xml_find_node(n
->children
, "action");
250 ObActionsAct
*action
= actions_parse(m
);
251 if (action
) o
->elseacts
= g_slist_append(o
->elseacts
, action
);
252 m
= obt_xml_find_node(m
->next
, "action");
256 xmlNodePtr query_node
= obt_xml_find_node(node
, "query");
258 /* The default query if none is specified. It uses the conditions
259 found in the action's node. */
262 QUERY_TARGET_IS_ACTION_TARGET
);
265 QueryTarget query_target
= QUERY_TARGET_IS_ACTION_TARGET
;
266 if (obt_xml_attr_contains(query_node
, "target", "focus"))
267 query_target
= QUERY_TARGET_IS_FOCUS_TARGET
;
269 setup_query(o
, query_node
->children
, query_target
);
271 query_node
= obt_xml_find_node(query_node
->next
, "query");
278 static void free_func(gpointer options
)
280 Options
*o
= options
;
283 for (i
= 0; i
< o
->queries
->len
; ++i
) {
284 Query
*q
= g_array_index(o
->queries
, Query
*, i
);
286 free_typed_match(&q
->title
);
287 free_typed_match(&q
->class);
288 free_typed_match(&q
->name
);
289 free_typed_match(&q
->role
);
290 free_typed_match(&q
->type
);
292 g_slice_free(Query
, q
);
295 while (o
->thenacts
) {
296 actions_act_unref(o
->thenacts
->data
);
297 o
->thenacts
= g_slist_delete_link(o
->thenacts
, o
->thenacts
);
299 while (o
->elseacts
) {
300 actions_act_unref(o
->elseacts
->data
);
301 o
->elseacts
= g_slist_delete_link(o
->elseacts
, o
->elseacts
);
304 g_array_unref(o
->queries
);
305 g_slice_free(Options
, o
);
308 /* Always return FALSE because its not interactive */
309 static gboolean
run_func_if(ObActionsData
*data
, gpointer options
)
311 Options
*o
= options
;
312 ObClient
*action_target
= data
->client
;
313 gboolean is_true
= TRUE
;
316 for (i
= 0; i
< o
->queries
->len
; ++i
) {
317 Query
*q
= g_array_index(o
->queries
, Query
*, i
);
318 ObClient
*query_target
= NULL
;
321 case QUERY_TARGET_IS_ACTION_TARGET
:
322 query_target
= data
->client
;
324 case QUERY_TARGET_IS_FOCUS_TARGET
:
325 query_target
= focus_client
;
329 /* If there's no client to query, then false. */
330 is_true
&= query_target
!= NULL
;
333 is_true
&= query_target
->shaded
;
335 is_true
&= !query_target
->shaded
;
338 is_true
&= query_target
->iconic
;
340 is_true
&= !query_target
->iconic
;
343 is_true
&= query_target
->max_horz
;
345 is_true
&= !query_target
->max_horz
;
348 is_true
&= query_target
->max_vert
;
350 is_true
&= !query_target
->max_vert
;
352 gboolean is_max_full
=
353 query_target
->max_vert
&& query_target
->max_horz
;
355 is_true
&= is_max_full
;
357 is_true
&= !is_max_full
;
360 is_true
&= query_target
== focus_client
;
362 is_true
&= query_target
!= focus_client
;
365 query_target
->urgent
|| query_target
->demands_attention
;
367 is_true
&= is_urgent
;
369 is_true
&= !is_urgent
;
371 gboolean has_visible_title_bar
=
372 !query_target
->undecorated
&&
373 (query_target
->decorations
& OB_FRAME_DECOR_TITLEBAR
);
375 is_true
&= has_visible_title_bar
;
377 is_true
&= !has_visible_title_bar
;
379 if (q
->omnipresent_on
)
380 is_true
&= query_target
->desktop
== DESKTOP_ALL
;
381 if (q
->omnipresent_off
)
382 is_true
&= query_target
->desktop
!= DESKTOP_ALL
;
384 gboolean is_on_current_desktop
=
385 query_target
->desktop
== screen_desktop
||
386 query_target
->desktop
== DESKTOP_ALL
;
387 if (q
->desktop_current
)
388 is_true
&= is_on_current_desktop
;
389 if (q
->desktop_other
)
390 is_true
&= !is_on_current_desktop
;
392 if (q
->desktop_number
) {
393 gboolean is_on_desktop
=
394 query_target
->desktop
== q
->desktop_number
- 1 ||
395 query_target
->desktop
== DESKTOP_ALL
;
396 is_true
&= is_on_desktop
;
399 if (q
->screendesktop_number
)
400 is_true
&= screen_desktop
== q
->screendesktop_number
- 1;
402 is_true
&= check_typed_match(&q
->title
, query_target
->original_title
);
403 is_true
&= check_typed_match(&q
->class, query_target
->class);
404 is_true
&= check_typed_match(&q
->name
, query_target
->name
);
405 is_true
&= check_typed_match(&q
->role
, query_target
->role
);
406 is_true
&= check_typed_match(&q
->type
,
407 client_type_to_string(query_target
));
409 if (q
->client_monitor
)
410 is_true
&= client_monitor(query_target
) == q
->client_monitor
- 1;
420 actions_run_acts(acts
, data
->uact
, data
->state
,
421 data
->x
, data
->y
, data
->button
,
422 data
->context
, action_target
);
427 static gboolean
run_func_foreach(ObActionsData
*data
, gpointer options
)
430 Options
*o
= options
;
434 for (it
= client_list
; it
; it
= g_list_next(it
)) {
435 data
->client
= it
->data
;
436 run_func_if(data
, options
);
445 static gboolean
run_func_stop(ObActionsData
*data
, gpointer options
)
447 Options
*o
= options
;
449 /* This stops the loop above so we don't invoke actions on any more
453 /* TRUE causes actions_run_acts to not run further actions on the current