]> git.sur5r.net Git - i3/i3/blob - src/match.c
Merge branch 'master' into next
[i3/i3] / src / match.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
6  *
7  * A "match" is a data structure which acts like a mask or expression to match
8  * certain windows or not. For example, when using commands, you can specify a
9  * command like this: [title="*Firefox*"] kill. The title member of the match
10  * data structure will then be filled and i3 will check each window using
11  * match_matches_window() to find the windows affected by this command.
12  *
13  */
14 #include "all.h"
15
16 /*
17  * Initializes the Match data structure. This function is necessary because the
18  * members representing boolean values (like dock) need to be initialized with
19  * -1 instead of 0.
20  *
21  */
22 void match_init(Match *match) {
23     memset(match, 0, sizeof(Match));
24     match->dock = -1;
25     match->urgent = U_DONTCHECK;
26 }
27
28 /*
29  * Check if a match is empty. This is necessary while parsing commands to see
30  * whether the user specified a match at all.
31  *
32  */
33 bool match_is_empty(Match *match) {
34     /* we cannot simply use memcmp() because the structure is part of a
35      * TAILQ and I don’t want to start with things like assuming that the
36      * last member of a struct really is at the end in memory… */
37     return (match->title == NULL &&
38             match->mark == NULL &&
39             match->application == NULL &&
40             match->class == NULL &&
41             match->instance == NULL &&
42             match->role == NULL &&
43             match->urgent == U_DONTCHECK &&
44             match->id == XCB_NONE &&
45             match->con_id == NULL &&
46             match->dock == -1 &&
47             match->floating == M_ANY);
48 }
49
50 /*
51  * Copies the data of a match from src to dest.
52  *
53  */
54 void match_copy(Match *dest, Match *src) {
55     memcpy(dest, src, sizeof(Match));
56
57 /* The DUPLICATE_REGEX macro creates a new regular expression from the
58  * ->pattern of the old one. It therefore does use a little more memory then
59  *  with a refcounting system, but it’s easier this way. */
60 #define DUPLICATE_REGEX(field) do { \
61     if (src->field != NULL) \
62         dest->field = regex_new(src->field->pattern); \
63 } while (0)
64
65     DUPLICATE_REGEX(title);
66     DUPLICATE_REGEX(mark);
67     DUPLICATE_REGEX(application);
68     DUPLICATE_REGEX(class);
69     DUPLICATE_REGEX(instance);
70     DUPLICATE_REGEX(role);
71 }
72
73 /*
74  * Check if a match data structure matches the given window.
75  *
76  */
77 bool match_matches_window(Match *match, i3Window *window) {
78     LOG("Checking window 0x%08x (class %s)\n", window->id, window->class_class);
79
80     if (match->class != NULL) {
81         if (window->class_class != NULL &&
82             regex_matches(match->class, window->class_class)) {
83             LOG("window class matches (%s)\n", window->class_class);
84         } else {
85             return false;
86         }
87     }
88
89     if (match->instance != NULL) {
90         if (window->class_instance != NULL &&
91             regex_matches(match->instance, window->class_instance)) {
92             LOG("window instance matches (%s)\n", window->class_instance);
93         } else {
94             return false;
95         }
96     }
97
98     if (match->id != XCB_NONE) {
99         if (window->id == match->id) {
100             LOG("match made by window id (%d)\n", window->id);
101         } else {
102             LOG("window id does not match\n");
103             return false;
104         }
105     }
106
107     if (match->title != NULL) {
108         if (window->name_json != NULL &&
109             regex_matches(match->title, window->name_json)) {
110             LOG("title matches (%s)\n", window->name_json);
111         } else {
112             return false;
113         }
114     }
115
116     if (match->role != NULL) {
117         if (window->role != NULL &&
118             regex_matches(match->role, window->role)) {
119             LOG("window_role matches (%s)\n", window->role);
120         } else {
121             return false;
122         }
123     }
124
125     Con *con = NULL;
126     if (match->urgent == U_LATEST) {
127         /* if the window isn't urgent, no sense in searching */
128         if (window->urgent == 0) {
129             return false;
130         }
131         /* if we find a window that is newer than this one, bail */
132         TAILQ_FOREACH(con, &all_cons, all_cons) {
133             if ((con->window != NULL) &&
134                 (con->window->urgent > window->urgent)) {
135                 return false;
136             }
137         }
138         LOG("urgent matches latest\n");
139     }
140
141     if (match->urgent == U_OLDEST) {
142         /* if the window isn't urgent, no sense in searching */
143         if (window->urgent == 0) {
144             return false;
145         }
146         /* if we find a window that is older than this one (and not 0), bail */
147         TAILQ_FOREACH(con, &all_cons, all_cons) {
148             if ((con->window != NULL) &&
149                 (con->window->urgent != 0) &&
150                 (con->window->urgent < window->urgent)) {
151                 return false;
152             }
153         }
154         LOG("urgent matches oldest\n");
155     }
156
157     if (match->dock != -1) {
158         if ((window->dock == W_DOCK_TOP && match->dock == M_DOCK_TOP) ||
159          (window->dock == W_DOCK_BOTTOM && match->dock == M_DOCK_BOTTOM) ||
160          ((window->dock == W_DOCK_TOP || window->dock == W_DOCK_BOTTOM) &&
161           match->dock == M_DOCK_ANY) ||
162          (window->dock == W_NODOCK && match->dock == M_NODOCK)) {
163             LOG("dock status matches\n");
164         } else {
165             LOG("dock status does not match\n");
166             return false;
167         }
168     }
169
170     /* We don’t check the mark because this function is not even called when
171      * the mark would have matched - it is checked in cmdparse.y itself */
172     if (match->mark != NULL) {
173         LOG("mark does not match\n");
174         return false;
175     }
176
177     return true;
178 }
179
180 /*
181  * Frees the given match. It must not be used afterwards!
182  *
183  */
184 void match_free(Match *match) {
185     /* First step: free the regex fields / patterns */
186     regex_free(match->title);
187     regex_free(match->application);
188     regex_free(match->class);
189     regex_free(match->instance);
190     regex_free(match->mark);
191     regex_free(match->role);
192
193     /* Second step: free the regex helper struct itself */
194     FREE(match->title);
195     FREE(match->application);
196     FREE(match->class);
197     FREE(match->instance);
198     FREE(match->mark);
199     FREE(match->role);
200 }