]> Dogcows Code - chaz/openbox/blob - openbox/actions/if.c
Add window type to If as well
[chaz/openbox] / 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 focused;
62 gboolean unfocused;
63 gboolean urgent_on;
64 gboolean urgent_off;
65 gboolean decor_off;
66 gboolean decor_on;
67 gboolean omnipresent_on;
68 gboolean omnipresent_off;
69 gboolean desktop_current;
70 gboolean desktop_other;
71 guint desktop_number;
72 guint screendesktop_number;
73 guint client_monitor;
74 TypedMatch title;
75 TypedMatch class;
76 TypedMatch name;
77 TypedMatch role;
78 TypedMatch type;
79 } Query;
80
81 typedef struct {
82 GArray* queries;
83 GSList *thenacts;
84 GSList *elseacts;
85 gboolean stop;
86 } Options;
87
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);
93
94 void action_if_startup(void)
95 {
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);
99
100 actions_set_can_stop("Stop", TRUE);
101 }
102
103 static inline void set_bool(xmlNodePtr node,
104 const char *name,
105 gboolean *on,
106 gboolean *off)
107 {
108 xmlNodePtr n;
109
110 if ((n = obt_xml_find_node(node, name))) {
111 if (obt_xml_node_bool(n))
112 *on = TRUE;
113 else
114 *off = TRUE;
115 }
116 }
117
118 static void setup_typed_match(TypedMatch *tm, xmlNodePtr n)
119 {
120 gchar *s;
121 if ((s = obt_xml_node_string(n))) {
122 gchar *type = NULL;
123 if (!obt_xml_attr_string(n, "type", &type) ||
124 !g_ascii_strcasecmp(type, "pattern"))
125 {
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);
134 }
135 g_free(s);
136 g_free(type);
137 }
138 }
139
140 static void free_typed_match(TypedMatch *tm)
141 {
142 switch (tm->type) {
143 case MATCH_TYPE_PATTERN:
144 g_pattern_spec_free(tm->m.pattern);
145 break;
146 case MATCH_TYPE_REGEX:
147 g_regex_unref(tm->m.regex);
148 break;
149 case MATCH_TYPE_EXACT:
150 g_free(tm->m.exact);
151 break;
152 case MATCH_TYPE_NONE:
153 break;
154 }
155 }
156
157 static gboolean check_typed_match(TypedMatch *tm, const gchar *s)
158 {
159 switch (tm->type) {
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:
167 return TRUE;
168 }
169 g_assert_not_reached();
170 }
171
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);
175
176 q->target = target;
177
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);
187
188 xmlNodePtr n;
189 if ((n = obt_xml_find_node(node, "desktop"))) {
190 gchar *s;
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;
196 else
197 q->desktop_number = atoi(s);
198 g_free(s);
199 }
200 }
201 if ((n = obt_xml_find_node(node, "activedesktop"))) {
202 q->screendesktop_number = obt_xml_node_int(n);
203 }
204 if ((n = obt_xml_find_node(node, "title"))) {
205 setup_typed_match(&q->title, n);
206 }
207 if ((n = obt_xml_find_node(node, "class"))) {
208 setup_typed_match(&q->class, n);
209 }
210 if ((n = obt_xml_find_node(node, "name"))) {
211 setup_typed_match(&q->name, n);
212 }
213 if ((n = obt_xml_find_node(node, "role"))) {
214 setup_typed_match(&q->role, n);
215 }
216 if ((n = obt_xml_find_node(node, "type"))) {
217 setup_typed_match(&q->type, n);
218 }
219 if ((n = obt_xml_find_node(node, "monitor"))) {
220 q->client_monitor = obt_xml_node_int(n);
221 }
222 }
223
224 static gpointer setup_func(xmlNodePtr node)
225 {
226 Options *o = g_slice_new0(Options);
227
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,
232 sizeof(Query*));
233
234 xmlNodePtr n;
235 if ((n = obt_xml_find_node(node, "then"))) {
236 xmlNodePtr m;
237
238 m = obt_xml_find_node(n->children, "action");
239 while (m) {
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");
243 }
244 }
245 if ((n = obt_xml_find_node(node, "else"))) {
246 xmlNodePtr m;
247
248 m = obt_xml_find_node(n->children, "action");
249 while (m) {
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");
253 }
254 }
255
256 xmlNodePtr query_node = obt_xml_find_node(node, "query");
257 if (!query_node) {
258 /* The default query if none is specified. It uses the conditions
259 found in the action's node. */
260 setup_query(o,
261 node,
262 QUERY_TARGET_IS_ACTION_TARGET);
263 } else {
264 while (query_node) {
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;
268
269 setup_query(o, query_node->children, query_target);
270
271 query_node = obt_xml_find_node(query_node->next, "query");
272 }
273 }
274
275 return o;
276 }
277
278 static void free_func(gpointer options)
279 {
280 Options *o = options;
281
282 guint i;
283 for (i = 0; i < o->queries->len; ++i) {
284 Query *q = g_array_index(o->queries, Query*, i);
285
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);
291
292 g_slice_free(Query, q);
293 }
294
295 while (o->thenacts) {
296 actions_act_unref(o->thenacts->data);
297 o->thenacts = g_slist_delete_link(o->thenacts, o->thenacts);
298 }
299 while (o->elseacts) {
300 actions_act_unref(o->elseacts->data);
301 o->elseacts = g_slist_delete_link(o->elseacts, o->elseacts);
302 }
303
304 g_array_unref(o->queries);
305 g_slice_free(Options, o);
306 }
307
308 /* Always return FALSE because its not interactive */
309 static gboolean run_func_if(ObActionsData *data, gpointer options)
310 {
311 Options *o = options;
312 ObClient *action_target = data->client;
313 gboolean is_true = TRUE;
314
315 guint i;
316 for (i = 0; i < o->queries->len; ++i) {
317 Query *q = g_array_index(o->queries, Query*, i);
318 ObClient *query_target = NULL;
319
320 switch (q->target) {
321 case QUERY_TARGET_IS_ACTION_TARGET:
322 query_target = data->client;
323 break;
324 case QUERY_TARGET_IS_FOCUS_TARGET:
325 query_target = focus_client;
326 break;
327 }
328
329 /* If there's no client to query, then false. */
330 is_true &= query_target != NULL;
331
332 if (q->shaded_on)
333 is_true &= query_target->shaded;
334 if (q->shaded_off)
335 is_true &= !query_target->shaded;
336
337 if (q->iconic_on)
338 is_true &= query_target->iconic;
339 if (q->iconic_off)
340 is_true &= !query_target->iconic;
341
342 if (q->maxhorz_on)
343 is_true &= query_target->max_horz;
344 if (q->maxhorz_off)
345 is_true &= !query_target->max_horz;
346
347 if (q->maxvert_on)
348 is_true &= query_target->max_vert;
349 if (q->maxvert_off)
350 is_true &= !query_target->max_vert;
351
352 gboolean is_max_full =
353 query_target->max_vert && query_target->max_horz;
354 if (q->maxfull_on)
355 is_true &= is_max_full;
356 if (q->maxfull_off)
357 is_true &= !is_max_full;
358
359 if (q->focused)
360 is_true &= query_target == focus_client;
361 if (q->unfocused)
362 is_true &= query_target != focus_client;
363
364 gboolean is_urgent =
365 query_target->urgent || query_target->demands_attention;
366 if (q->urgent_on)
367 is_true &= is_urgent;
368 if (q->urgent_off)
369 is_true &= !is_urgent;
370
371 gboolean has_visible_title_bar =
372 !query_target->undecorated &&
373 (query_target->decorations & OB_FRAME_DECOR_TITLEBAR);
374 if (q->decor_on)
375 is_true &= has_visible_title_bar;
376 if (q->decor_off)
377 is_true &= !has_visible_title_bar;
378
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;
383
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;
391
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;
397 }
398
399 if (q->screendesktop_number)
400 is_true &= screen_desktop == q->screendesktop_number - 1;
401
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));
408
409 if (q->client_monitor)
410 is_true &= client_monitor(query_target) == q->client_monitor - 1;
411
412 }
413
414 GSList *acts;
415 if (is_true)
416 acts = o->thenacts;
417 else
418 acts = o->elseacts;
419
420 actions_run_acts(acts, data->uact, data->state,
421 data->x, data->y, data->button,
422 data->context, action_target);
423
424 return FALSE;
425 }
426
427 static gboolean run_func_foreach(ObActionsData *data, gpointer options)
428 {
429 GList *it;
430 Options *o = options;
431
432 o->stop = FALSE;
433
434 for (it = client_list; it; it = g_list_next(it)) {
435 data->client = it->data;
436 run_func_if(data, options);
437 if (o->stop) {
438 break;
439 }
440 }
441
442 return FALSE;
443 }
444
445 static gboolean run_func_stop(ObActionsData *data, gpointer options)
446 {
447 Options *o = options;
448
449 /* This stops the loop above so we don't invoke actions on any more
450 clients */
451 o->stop = TRUE;
452
453 /* TRUE causes actions_run_acts to not run further actions on the current
454 client */
455 return TRUE;
456 }
This page took 0.055247 seconds and 4 git commands to generate.