1 /*
2 * Compton - a compositor for X11
3 *
4 * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
5 *
6 * Copyright (c) 2011-2013, Christopher Jeffrey
7 * See LICENSE for more information.
8 *
9 */
10
11 #include "common.h"
12
13 #include <fnmatch.h>
14 #include <ctype.h>
15
16 // libpcre
17 #ifdef CONFIG_REGEX_PCRE
18 #include <pcre.h>
19
20 // For compatiblity with <libpcre-8.20
21 #ifndef PCRE_STUDY_JIT_COMPILE
22 #define PCRE_STUDY_JIT_COMPILE 0
23 #define LPCRE_FREE_STUDY(extra) pcre_free(extra)
24 #else
25 #define LPCRE_FREE_STUDY(extra) pcre_free_study(extra)
26 #endif
27
28 #endif
29
30 #define C2_MAX_LEVELS 10
31
32 typedef struct _c2_b c2_b_t;
33 typedef struct _c2_l c2_l_t;
34
35 /// Pointer to a condition tree.
36 typedef struct {
37 bool isbranch : 1;
38 c2_b_t *b;
39 c2_l_t *l;
40 } c2_ptr_t;
41
42 /// Initializer for c2_ptr_t.
43 #define C2_PTR_INIT { \
44 .isbranch = false, \
45 .l = NULL, \
46 }
47
48 const static c2_ptr_t C2_PTR_NULL = C2_PTR_INIT;
49
50 /// Operator of a branch element.
51 typedef enum {
52 C2_B_OUNDEFINED,
53 C2_B_OAND,
54 C2_B_OOR,
55 C2_B_OXOR,
56 } c2_b_op_t;
57
58 /// Structure for branch element in a window condition
59 struct _c2_b {
60 bool neg : 1;
61 c2_b_op_t op;
62 c2_ptr_t opr1;
63 c2_ptr_t opr2;
64 };
65
66 /// Initializer for c2_b_t.
67 #define C2_B_INIT { \
68 .neg = false, \
69 .op = C2_B_OUNDEFINED, \
70 .opr1 = C2_PTR_INIT, \
71 .opr2 = C2_PTR_INIT, \
72 }
73
74 /// Structure for leaf element in a window condition
75 struct _c2_l {
76 bool neg : 1;
77 enum {
78 C2_L_OEXISTS,
79 C2_L_OEQ,
80 C2_L_OGT,
81 C2_L_OGTEQ,
82 C2_L_OLT,
83 C2_L_OLTEQ,
84 } op : 3;
85 enum {
86 C2_L_MEXACT,
87 C2_L_MSTART,
88 C2_L_MCONTAINS,
89 C2_L_MWILDCARD,
90 C2_L_MPCRE,
91 } match : 3;
92 bool match_ignorecase : 1;
93 char *tgt;
94 Atom tgtatom;
95 bool tgt_onframe;
96 int index;
97 enum {
98 C2_L_PUNDEFINED,
99 C2_L_PID,
100 C2_L_PX,
101 C2_L_PY,
102 C2_L_PX2,
103 C2_L_PY2,
104 C2_L_PWIDTH,
105 C2_L_PHEIGHT,
106 C2_L_PWIDTHB,
107 C2_L_PHEIGHTB,
108 C2_L_PBDW,
109 C2_L_PFULLSCREEN,
110 C2_L_POVREDIR,
111 C2_L_PARGB,
112 C2_L_PFOCUSED,
113 C2_L_PWMWIN,
114 C2_L_PBSHAPED,
115 C2_L_PROUNDED,
116 C2_L_PCLIENT,
117 C2_L_PWINDOWTYPE,
118 C2_L_PLEADER,
119 C2_L_PNAME,
120 C2_L_PCLASSG,
121 C2_L_PCLASSI,
122 C2_L_PROLE,
123 } predef;
124 enum c2_l_type {
125 C2_L_TUNDEFINED,
126 C2_L_TSTRING,
127 C2_L_TCARDINAL,
128 C2_L_TWINDOW,
129 C2_L_TATOM,
130 C2_L_TDRAWABLE,
131 } type;
132 int format;
133 enum {
134 C2_L_PTUNDEFINED,
135 C2_L_PTSTRING,
136 C2_L_PTINT,
137 } ptntype;
138 char *ptnstr;
139 long ptnint;
140 #ifdef CONFIG_REGEX_PCRE
141 pcre *regex_pcre;
142 pcre_extra *regex_pcre_extra;
143 #endif
144 };
145
146 /// Initializer for c2_l_t.
147 #define C2_L_INIT { \
148 .neg = false, \
149 .op = C2_L_OEXISTS, \
150 .match = C2_L_MEXACT, \
151 .match_ignorecase = false, \
152 .tgt = NULL, \
153 .tgtatom = 0, \
154 .tgt_onframe = false, \
155 .predef = C2_L_PUNDEFINED, \
156 .index = -1, \
157 .type = C2_L_TUNDEFINED, \
158 .format = 0, \
159 .ptntype = C2_L_PTUNDEFINED, \
160 .ptnstr = NULL, \
161 .ptnint = 0, \
162 }
163
164 const static c2_l_t leaf_def = C2_L_INIT;
165
166 /// Linked list type of conditions.
167 struct _c2_lptr {
168 c2_ptr_t ptr;
169 void *data;
170 struct _c2_lptr *next;
171 };
172
173 /// Initializer for c2_lptr_t.
174 #define C2_LPTR_INIT { \
175 .ptr = C2_PTR_INIT, \
176 .data = NULL, \
177 .next = NULL, \
178 }
179
180 /// Structure representing a predefined target.
181 typedef struct {
182 const char *name;
183 enum c2_l_type type;
184 int format;
185 } c2_predef_t;
186
187 // Predefined targets.
188 const static c2_predef_t C2_PREDEFS[] = {
189 [C2_L_PID ] = { "id" , C2_L_TCARDINAL , 0 },
190 [C2_L_PX ] = { "x" , C2_L_TCARDINAL , 0 },
191 [C2_L_PY ] = { "y" , C2_L_TCARDINAL , 0 },
192 [C2_L_PX2 ] = { "x2" , C2_L_TCARDINAL , 0 },
193 [C2_L_PY2 ] = { "y2" , C2_L_TCARDINAL , 0 },
194 [C2_L_PWIDTH ] = { "width" , C2_L_TCARDINAL , 0 },
195 [C2_L_PHEIGHT ] = { "height" , C2_L_TCARDINAL , 0 },
196 [C2_L_PWIDTHB ] = { "widthb" , C2_L_TCARDINAL , 0 },
197 [C2_L_PHEIGHTB ] = { "heightb" , C2_L_TCARDINAL , 0 },
198 [C2_L_PBDW ] = { "border_width" , C2_L_TCARDINAL , 0 },
199 [C2_L_PFULLSCREEN ] = { "fullscreen" , C2_L_TCARDINAL , 0 },
200 [C2_L_POVREDIR ] = { "override_redirect" , C2_L_TCARDINAL , 0 },
201 [C2_L_PARGB ] = { "argb" , C2_L_TCARDINAL , 0 },
202 [C2_L_PFOCUSED ] = { "focused" , C2_L_TCARDINAL , 0 },
203 [C2_L_PWMWIN ] = { "wmwin" , C2_L_TCARDINAL , 0 },
204 [C2_L_PBSHAPED ] = { "bounding_shaped" , C2_L_TCARDINAL , 0 },
205 [C2_L_PROUNDED ] = { "rounded_corners" , C2_L_TCARDINAL , 0 },
206 [C2_L_PCLIENT ] = { "client" , C2_L_TWINDOW , 0 },
207 [C2_L_PWINDOWTYPE ] = { "window_type" , C2_L_TSTRING , 0 },
208 [C2_L_PLEADER ] = { "leader" , C2_L_TWINDOW , 0 },
209 [C2_L_PNAME ] = { "name" , C2_L_TSTRING , 0 },
210 [C2_L_PCLASSG ] = { "class_g" , C2_L_TSTRING , 0 },
211 [C2_L_PCLASSI ] = { "class_i" , C2_L_TSTRING , 0 },
212 [C2_L_PROLE ] = { "role" , C2_L_TSTRING , 0 },
213 };
214
215 #define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))
216
217 /**
218 * Compare next word in a string with another string.
219 */
220 static inline int
strcmp_wd(const char * needle,const char * src)221 strcmp_wd(const char *needle, const char *src) {
222 int ret = mstrncmp(needle, src);
223 if (ret)
224 return ret;
225
226 char c = src[strlen(needle)];
227 if (isalnum(c) || '_' == c)
228 return 1;
229 else
230 return 0;
231 }
232
233 /**
234 * Return whether a c2_ptr_t is empty.
235 */
236 static inline bool
c2_ptr_isempty(const c2_ptr_t p)237 c2_ptr_isempty(const c2_ptr_t p) {
238 return !(p.isbranch ? (bool) p.b: (bool) p.l);
239 }
240
241 /**
242 * Reset a c2_ptr_t.
243 */
244 static inline void
c2_ptr_reset(c2_ptr_t * pp)245 c2_ptr_reset(c2_ptr_t *pp) {
246 if (pp)
247 memcpy(pp, &C2_PTR_NULL, sizeof(c2_ptr_t));
248 }
249
250 /**
251 * Combine two condition trees.
252 */
253 static inline c2_ptr_t
c2h_comb_tree(c2_b_op_t op,c2_ptr_t p1,c2_ptr_t p2)254 c2h_comb_tree(c2_b_op_t op, c2_ptr_t p1, c2_ptr_t p2) {
255 c2_ptr_t p = {
256 .isbranch = true,
257 .b = malloc(sizeof(c2_b_t))
258 };
259
260 p.b->opr1 = p1;
261 p.b->opr2 = p2;
262 p.b->op = op;
263
264 return p;
265 }
266
267 /**
268 * Get the precedence value of a condition branch operator.
269 */
270 static inline int
c2h_b_opp(c2_b_op_t op)271 c2h_b_opp(c2_b_op_t op) {
272 switch (op) {
273 case C2_B_OAND: return 2;
274 case C2_B_OOR: return 1;
275 case C2_B_OXOR: return 1;
276 default: break;
277 }
278
279 assert(0);
280 return 0;
281 }
282
283 /**
284 * Compare precedence of two condition branch operators.
285 *
286 * Associativity is left-to-right, forever.
287 *
288 * @return positive number if op1 > op2, 0 if op1 == op2 in precedence,
289 * negative number otherwise
290 */
291 static inline int
c2h_b_opcmp(c2_b_op_t op1,c2_b_op_t op2)292 c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) {
293 return c2h_b_opp(op1) - c2h_b_opp(op2);
294 }
295
296 static int
297 c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level);
298
299 static int
300 c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
301
302 static int
303 c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult);
304
305 static int
306 c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
307
308 static int
309 c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult);
310
311 static bool
312 c2_l_postprocess(session_t *ps, c2_l_t *pleaf);
313
314 static void
315 c2_free(c2_ptr_t p);
316
317 /**
318 * Wrapper of c2_free().
319 */
320 static inline void
c2_freep(c2_ptr_t * pp)321 c2_freep(c2_ptr_t *pp) {
322 if (pp) {
323 c2_free(*pp);
324 c2_ptr_reset(pp);
325 }
326 }
327
328 static const char *
329 c2h_dump_str_tgt(const c2_l_t *pleaf);
330
331 static const char *
332 c2h_dump_str_type(const c2_l_t *pleaf);
333
334 static void
335 c2_dump_raw(c2_ptr_t p);
336
337 /**
338 * Wrapper of c2_dump_raw().
339 */
340 static inline void
c2_dump(c2_ptr_t p)341 c2_dump(c2_ptr_t p) {
342 c2_dump_raw(p);
343 printf("\n");
344 fflush(stdout);
345 }
346
347 static Atom
348 c2_get_atom_type(const c2_l_t *pleaf);
349
350 static bool
351 c2_match_once(session_t *ps, win *w, const c2_ptr_t cond);
352
353