]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/actions/if.c
Remove incorrect assertion for MoveResizeTo
[mikachu/openbox.git] / openbox / actions / if.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    if.c for the Openbox window manager
4    Copyright (c) 2007        Mikael Magnusson
5    Copyright (c) 2007        Dana Jansens
6
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.
11
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.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
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"
26 #include <glib.h>
27
28 typedef enum {
29     QUERY_TARGET_IS_ACTION_TARGET,
30     QUERY_TARGET_IS_FOCUS_TARGET,
31 } QueryTarget;
32
33 typedef enum {
34     MATCH_TYPE_NONE = 0,
35     MATCH_TYPE_PATTERN,
36     MATCH_TYPE_REGEX,
37     MATCH_TYPE_EXACT,
38 } MatchType;
39
40 typedef struct {
41     MatchType type;
42     union m {
43         GPatternSpec *pattern;
44         GRegex *regex;
45         gchar *exact;
46     } m;
47 } TypedMatch;
48
49 typedef struct {
50     QueryTarget target;
51     gboolean shaded_on;
52     gboolean shaded_off;
53     gboolean maxvert_on;
54     gboolean maxvert_off;
55     gboolean maxhorz_on;
56     gboolean maxhorz_off;
57     gboolean maxfull_on;
58     gboolean maxfull_off;
59     gboolean iconic_on;
60     gboolean iconic_off;
61     gboolean fullscreen_on;
62     gboolean fullscreen_off;
63     gboolean locked_on;
64     gboolean locked_off;
65     gboolean focused;
66     gboolean unfocused;
67     gboolean focusable;
68     gboolean nonfocusable;
69     gboolean urgent_on;
70     gboolean urgent_off;
71     gboolean decor_off;
72     gboolean decor_on;
73     gboolean omnipresent_on;
74     gboolean omnipresent_off;
75     gboolean desktop_current;
76     gboolean desktop_other;
77     gboolean desktop_last;
78     guint    desktop_number;
79     guint    screendesktop_number;
80     guint    client_monitor;
81     TypedMatch title;
82     TypedMatch class;
83     TypedMatch name;
84     TypedMatch role;
85     TypedMatch type;
86 } Query;
87
88 typedef struct {
89     GArray *queries;
90     GSList *thenacts;
91     GSList *elseacts;
92 } Options;
93
94 static gpointer setup_func(xmlNodePtr node);
95 static void     free_func(gpointer options);
96 static gboolean run_func_if(ObActionsData *data, gpointer options);
97 static gboolean run_func_stop(ObActionsData *data, gpointer options);
98 static gboolean run_func_foreach(ObActionsData *data, gpointer options);
99
100 static gboolean foreach_stop;
101
102 void action_if_startup(void)
103 {
104     actions_register("If", setup_func, free_func, run_func_if);
105     actions_register("Stop", NULL, NULL, run_func_stop);
106     actions_register("ForEach", setup_func, free_func, run_func_foreach);
107
108     actions_set_can_stop("Stop", TRUE);
109 }
110
111 static inline void set_bool(xmlNodePtr node,
112                             const char *name,
113                             gboolean *on,
114                             gboolean *off)
115 {
116     xmlNodePtr n;
117
118     if ((n = obt_xml_find_node(node, name))) {
119         if (obt_xml_node_bool(n))
120             *on = TRUE;
121         else
122             *off = TRUE;
123     }
124 }
125
126 static void setup_typed_match(TypedMatch *tm, xmlNodePtr n)
127 {
128     gchar *s;
129     if ((s = obt_xml_node_string(n))) {
130         gchar *type = NULL;
131         if (!obt_xml_attr_string(n, "type", &type) ||
132             !g_ascii_strcasecmp(type, "pattern"))
133         {
134             tm->type = MATCH_TYPE_PATTERN;
135             tm->m.pattern = g_pattern_spec_new(s);
136         } else if (type && !g_ascii_strcasecmp(type, "regex")) {
137             tm->type = MATCH_TYPE_REGEX;
138             tm->m.regex = g_regex_new(s, 0, 0, NULL);
139         } else if (type && !g_ascii_strcasecmp(type, "exact")) {
140             tm->type = MATCH_TYPE_EXACT;
141             tm->m.exact = g_strdup(s);
142         }
143         g_free(s);
144         g_free(type);
145     }
146 }
147
148 static void free_typed_match(TypedMatch *tm)
149 {
150     switch (tm->type) {
151     case MATCH_TYPE_PATTERN:
152         g_pattern_spec_free(tm->m.pattern);
153         break;
154     case MATCH_TYPE_REGEX:
155         g_regex_unref(tm->m.regex);
156         break;
157     case MATCH_TYPE_EXACT:
158         g_free(tm->m.exact);
159         break;
160     case MATCH_TYPE_NONE:
161         break;
162     }
163 }
164
165 static gboolean check_typed_match(TypedMatch *tm, const gchar *s)
166 {
167     switch (tm->type) {
168     case MATCH_TYPE_PATTERN:
169         return g_pattern_match_string(tm->m.pattern, s);
170     case MATCH_TYPE_REGEX:
171         return g_regex_match(tm->m.regex, s, 0, NULL);
172     case MATCH_TYPE_EXACT:
173         return !strcmp(tm->m.exact, s);
174     case MATCH_TYPE_NONE:
175         return TRUE;
176     }
177     g_assert_not_reached();
178 }
179
180 static void setup_query(Options* o, xmlNodePtr node, QueryTarget target) {
181     Query *q = g_slice_new0(Query);
182     g_array_append_val(o->queries, q);
183
184     q->target = target;
185
186     set_bool(node, "shaded", &q->shaded_on, &q->shaded_off);
187     set_bool(node, "maximized", &q->maxfull_on, &q->maxfull_off);
188     set_bool(node, "maximizedhorizontal", &q->maxhorz_on, &q->maxhorz_off);
189     set_bool(node, "maximizedvertical", &q->maxvert_on, &q->maxvert_off);
190     set_bool(node, "iconified", &q->iconic_on, &q->iconic_off);
191     set_bool(node, "fullscreen", &q->fullscreen_on, &q->fullscreen_off);
192     set_bool(node, "locked", &q->locked_on, &q->locked_off);
193     set_bool(node, "focused", &q->focused, &q->unfocused);
194     set_bool(node, "focusable", &q->focusable, &q->nonfocusable);
195     set_bool(node, "urgent", &q->urgent_on, &q->urgent_off);
196     set_bool(node, "undecorated", &q->decor_off, &q->decor_on);
197     set_bool(node, "omnipresent", &q->omnipresent_on, &q->omnipresent_off);
198
199     xmlNodePtr n;
200     if ((n = obt_xml_find_node(node, "desktop"))) {
201         gchar *s;
202         if ((s = obt_xml_node_string(n))) {
203             if (!g_ascii_strcasecmp(s, "current"))
204                 q->desktop_current = TRUE;
205             else if (!g_ascii_strcasecmp(s, "other"))
206                 q->desktop_other = TRUE;
207             else if (!g_ascii_strcasecmp(s, "last"))
208                 q->desktop_last = TRUE;
209             else
210                 q->desktop_number = atoi(s);
211             g_free(s);
212         }
213     }
214     if ((n = obt_xml_find_node(node, "activedesktop"))) {
215         q->screendesktop_number = obt_xml_node_int(n);
216     }
217     if ((n = obt_xml_find_node(node, "title"))) {
218         setup_typed_match(&q->title, n);
219     }
220     if ((n = obt_xml_find_node(node, "class"))) {
221         setup_typed_match(&q->class, n);
222     }
223     if ((n = obt_xml_find_node(node, "name"))) {
224         setup_typed_match(&q->name, n);
225     }
226     if ((n = obt_xml_find_node(node, "role"))) {
227         setup_typed_match(&q->role, n);
228     }
229     if ((n = obt_xml_find_node(node, "type"))) {
230         setup_typed_match(&q->type, n);
231     }
232     if ((n = obt_xml_find_node(node, "monitor"))) {
233         q->client_monitor = obt_xml_node_int(n);
234     }
235 }
236
237 static gpointer setup_func(xmlNodePtr node)
238 {
239     Options *o = g_slice_new0(Options);
240
241     gboolean zero_terminated = FALSE;
242     gboolean clear_to_zero_on_alloc = FALSE;
243     o->queries = g_array_new(zero_terminated,
244                              clear_to_zero_on_alloc,
245                              sizeof(Query*));
246
247     xmlNodePtr n;
248     if ((n = obt_xml_find_node(node, "then"))) {
249         xmlNodePtr m;
250
251         m = obt_xml_find_node(n->children, "action");
252         while (m) {
253             ObActionsAct *action = actions_parse(m);
254             if (action) o->thenacts = g_slist_append(o->thenacts, action);
255             m = obt_xml_find_node(m->next, "action");
256         }
257     }
258     if ((n = obt_xml_find_node(node, "else"))) {
259         xmlNodePtr m;
260
261         m = obt_xml_find_node(n->children, "action");
262         while (m) {
263             ObActionsAct *action = actions_parse(m);
264             if (action) o->elseacts = g_slist_append(o->elseacts, action);
265             m = obt_xml_find_node(m->next, "action");
266         }
267     }
268
269     xmlNodePtr query_node = obt_xml_find_node(node, "query");
270     if (!query_node) {
271         /* The default query if none is specified. It uses the conditions
272            found in the action's node. */
273         setup_query(o,
274                     node,
275                     QUERY_TARGET_IS_ACTION_TARGET);
276     } else {
277         while (query_node) {
278             QueryTarget query_target = QUERY_TARGET_IS_ACTION_TARGET;
279             if (obt_xml_attr_contains(query_node, "target", "focus"))
280                 query_target = QUERY_TARGET_IS_FOCUS_TARGET;
281
282             setup_query(o, query_node->children, query_target);
283
284             query_node = obt_xml_find_node(query_node->next, "query");
285         }
286     }
287
288     return o;
289 }
290
291 static void free_func(gpointer options)
292 {
293     Options *o = options;
294
295     guint i;
296     for (i = 0; i < o->queries->len; ++i) {
297         Query *q = g_array_index(o->queries, Query*, i);
298
299         free_typed_match(&q->title);
300         free_typed_match(&q->class);
301         free_typed_match(&q->name);
302         free_typed_match(&q->role);
303         free_typed_match(&q->type);
304
305         g_slice_free(Query, q);
306     }
307
308     while (o->thenacts) {
309         actions_act_unref(o->thenacts->data);
310         o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts);
311     }
312     while (o->elseacts) {
313         actions_act_unref(o->elseacts->data);
314         o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts);
315     }
316
317     g_array_unref(o->queries);
318     g_slice_free(Options, o);
319 }
320
321 /* Always return FALSE because its not interactive */
322 static gboolean run_func_if(ObActionsData *data, gpointer options)
323 {
324     Options *o = options;
325     ObClient *action_target = data->client;
326     gboolean is_true = TRUE;
327
328     guint i;
329     for (i = 0; is_true && i < o->queries->len; ++i) {
330         Query *q = g_array_index(o->queries, Query*, i);
331         ObClient *query_target = NULL;
332
333         switch (q->target) {
334         case QUERY_TARGET_IS_ACTION_TARGET:
335             query_target = data->client;
336             break;
337         case QUERY_TARGET_IS_FOCUS_TARGET:
338             query_target = focus_client;
339             break;
340         }
341
342         /* If there's no client to query, then false. */
343         if (!query_target) {
344             is_true = FALSE;
345             break;
346         }
347
348         if (q->shaded_on)
349             is_true &= query_target->shaded;
350         if (q->shaded_off)
351             is_true &= !query_target->shaded;
352
353         if (q->iconic_on)
354             is_true &= query_target->iconic;
355         if (q->iconic_off)
356             is_true &= !query_target->iconic;
357
358         if (q->fullscreen_on)
359             is_true &= query_target->fullscreen;
360         if (q->fullscreen_off)
361             is_true &= !query_target->fullscreen;
362
363         if (q->locked_on)
364             is_true &= query_target->locked;
365         if (q->locked_off)
366             is_true &= !query_target->locked;
367
368         if (q->maxhorz_on)
369             is_true &= query_target->max_horz;
370         if (q->maxhorz_off)
371             is_true &= !query_target->max_horz;
372
373         if (q->maxvert_on)
374             is_true &= query_target->max_vert;
375         if (q->maxvert_off)
376             is_true &= !query_target->max_vert;
377
378         gboolean is_max_full =
379             query_target->max_vert && query_target->max_horz;
380         if (q->maxfull_on)
381             is_true &= is_max_full;
382         if (q->maxfull_off)
383             is_true &= !is_max_full;
384
385         if (q->focused)
386             is_true &= query_target == focus_client;
387         if (q->unfocused)
388             is_true &= query_target != focus_client;
389
390         if (q->focusable)
391             is_true &= query_target->can_focus;
392         if (q->nonfocusable)
393             is_true &= !query_target->can_focus;
394
395         gboolean is_urgent =
396             query_target->urgent || query_target->demands_attention;
397         if (q->urgent_on)
398             is_true &= is_urgent;
399         if (q->urgent_off)
400             is_true &= !is_urgent;
401
402         gboolean has_visible_title_bar =
403             !query_target->undecorated &&
404             (query_target->decorations & OB_FRAME_DECOR_TITLEBAR);
405         if (q->decor_on)
406             is_true &= has_visible_title_bar;
407         if (q->decor_off)
408             is_true &= !has_visible_title_bar;
409
410         if (q->omnipresent_on)
411             is_true &= query_target->desktop == DESKTOP_ALL;
412         if (q->omnipresent_off)
413             is_true &= query_target->desktop != DESKTOP_ALL;
414
415         gboolean is_on_current_desktop =
416             query_target->desktop == screen_desktop ||
417             query_target->desktop == DESKTOP_ALL;
418         if (q->desktop_current)
419             is_true &= is_on_current_desktop;
420         if (q->desktop_other)
421             is_true &= !is_on_current_desktop;
422         if (q->desktop_last)
423             is_true &= query_target->desktop == screen_last_desktop;
424
425         if (q->desktop_number) {
426             gboolean is_on_desktop =
427                 query_target->desktop == q->desktop_number - 1 ||
428                 query_target->desktop == DESKTOP_ALL;
429             is_true &= is_on_desktop;
430         }
431
432         if (q->screendesktop_number)
433             is_true &= screen_desktop == q->screendesktop_number - 1;
434
435         is_true &= check_typed_match(&q->title, query_target->original_title);
436         is_true &= check_typed_match(&q->class, query_target->class);
437         is_true &= check_typed_match(&q->name, query_target->name);
438         is_true &= check_typed_match(&q->role, query_target->role);
439         is_true &= check_typed_match(&q->type,
440                                      client_type_to_string(query_target));
441
442         if (q->client_monitor)
443             is_true &= client_monitor(query_target) == q->client_monitor - 1;
444
445     }
446
447     GSList *acts;
448     if (is_true)
449         acts = o->thenacts;
450     else
451         acts = o->elseacts;
452
453     actions_run_acts(acts, data->uact, data->state,
454                      data->x, data->y, data->button,
455                      data->context, action_target);
456
457     return FALSE;
458 }
459
460 static gboolean run_func_foreach(ObActionsData *data, gpointer options)
461 {
462     GList *it;
463
464     foreach_stop = FALSE;
465
466     for (it = client_list; it; it = g_list_next(it)) {
467         data->client = it->data;
468         run_func_if(data, options);
469         if (foreach_stop) {
470             foreach_stop = FALSE;
471             break;
472         }
473     }
474
475     return FALSE;
476 }
477
478 static gboolean run_func_stop(ObActionsData *data, gpointer options)
479 {
480     /* This stops the loop above so we don't invoke actions on any more
481        clients */
482     foreach_stop = TRUE;
483
484     /* TRUE causes actions_run_acts to not run further actions on the current
485        client */
486     return TRUE;
487 }