1 /*
2 * Copyright © 2007 Novell, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of
9 * Novell, Inc. not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior permission.
11 * Novell, Inc. makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 *
15 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author: David Reveman <davidr@novell.com>
24 */
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <limits.h>
29
30 #include <regex.h>
31
32 #include <X11/Xatom.h>
33
34 #include <compiz-core.h>
35
36 static CompMetadata regexMetadata;
37
38 static int displayPrivateIndex;
39
40 typedef struct _RegexDisplay {
41 int screenPrivateIndex;
42 HandleEventProc handleEvent;
43 MatchInitExpProc matchInitExp;
44 Atom roleAtom;
45 Atom visibleNameAtom;
46 CompTimeoutHandle timeoutHandle;
47 } RegexDisplay;
48
49 typedef struct _RegexScreen {
50 int windowPrivateIndex;
51 } RegexScreen;
52
53 typedef struct _RegexWindow {
54 char *title;
55 char *role;
56 } RegexWindow;
57
58 #define GET_REGEX_DISPLAY(d) \
59 ((RegexDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
60
61 #define REGEX_DISPLAY(d) \
62 RegexDisplay *rd = GET_REGEX_DISPLAY (d)
63
64 #define GET_REGEX_SCREEN(s, rd) \
65 ((RegexScreen *) (s)->base.privates[(rd)->screenPrivateIndex].ptr)
66
67 #define REGEX_SCREEN(s) \
68 RegexScreen *rs = GET_REGEX_SCREEN (s, GET_REGEX_DISPLAY (s->display))
69
70 #define GET_REGEX_WINDOW(w, rs) \
71 ((RegexWindow *) (w)->base.privates[(rs)->windowPrivateIndex].ptr)
72
73 #define REGEX_WINDOW(w) \
74 RegexWindow *rw = GET_REGEX_WINDOW (w, \
75 GET_REGEX_SCREEN (w->screen, \
76 GET_REGEX_DISPLAY (w->screen->display)))
77
78 static void
regexMatchExpFini(CompDisplay * d,CompPrivate private)79 regexMatchExpFini (CompDisplay *d,
80 CompPrivate private)
81 {
82 regex_t *preg = (regex_t *) private.ptr;
83
84 if (preg)
85 {
86 regfree (preg);
87 free (preg);
88 }
89 }
90
91 static Bool
regexMatchExpEvalTitle(CompDisplay * d,CompWindow * w,CompPrivate private)92 regexMatchExpEvalTitle (CompDisplay *d,
93 CompWindow *w,
94 CompPrivate private)
95 {
96 regex_t *preg = (regex_t *) private.ptr;
97 int status;
98
99 REGEX_WINDOW (w);
100
101 if (!preg)
102 return FALSE;
103
104 if (!rw->title)
105 return FALSE;
106
107 status = regexec (preg, rw->title, 0, NULL, 0);
108 if (status)
109 return FALSE;
110
111 return TRUE;
112 }
113
114 static Bool
regexMatchExpEvalRole(CompDisplay * d,CompWindow * w,CompPrivate private)115 regexMatchExpEvalRole (CompDisplay *d,
116 CompWindow *w,
117 CompPrivate private)
118 {
119 regex_t *preg = (regex_t *) private.ptr;
120 int status;
121
122 REGEX_WINDOW (w);
123
124 if (!preg)
125 return FALSE;
126
127 if (!rw->role)
128 return FALSE;
129
130 status = regexec (preg, rw->role, 0, NULL, 0);
131 if (status)
132 return FALSE;
133
134 return TRUE;
135 }
136
137 static Bool
regexMatchExpEvalClass(CompDisplay * d,CompWindow * w,CompPrivate private)138 regexMatchExpEvalClass (CompDisplay *d,
139 CompWindow *w,
140 CompPrivate private)
141 {
142 regex_t *preg = (regex_t *) private.ptr;
143 int status;
144
145 if (!preg)
146 return FALSE;
147
148 if (!w->resClass)
149 return FALSE;
150
151 status = regexec (preg, w->resClass, 0, NULL, 0);
152 if (status)
153 return FALSE;
154
155 return TRUE;
156 }
157
158 static Bool
regexMatchExpEvalName(CompDisplay * d,CompWindow * w,CompPrivate private)159 regexMatchExpEvalName (CompDisplay *d,
160 CompWindow *w,
161 CompPrivate private)
162 {
163 regex_t *preg = (regex_t *) private.ptr;
164 int status;
165
166 if (!preg)
167 return FALSE;
168
169 if (!w->resName)
170 return FALSE;
171
172 status = regexec (preg, w->resName, 0, NULL, 0);
173 if (status)
174 return FALSE;
175
176 return TRUE;
177 }
178
179 static void
regexMatchInitExp(CompDisplay * d,CompMatchExp * exp,const char * value)180 regexMatchInitExp (CompDisplay *d,
181 CompMatchExp *exp,
182 const char *value)
183 {
184 static struct _Prefix {
185 char *s;
186 int len;
187 CompMatchExpEvalProc eval;
188 unsigned int flags;
189 } prefix[] = {
190 { "title=", 6, regexMatchExpEvalTitle, 0 },
191 { "role=", 5, regexMatchExpEvalRole, 0 },
192 { "class=", 6, regexMatchExpEvalClass, 0 },
193 { "name=", 5, regexMatchExpEvalName, 0 },
194 { "ititle=", 7, regexMatchExpEvalTitle, REG_ICASE },
195 { "irole=", 6, regexMatchExpEvalRole, REG_ICASE },
196 { "iclass=", 7, regexMatchExpEvalClass, REG_ICASE },
197 { "iname=", 6, regexMatchExpEvalName, REG_ICASE },
198 };
199 int i;
200
201 REGEX_DISPLAY (d);
202
203 for (i = 0; i < sizeof (prefix) / sizeof (prefix[0]); i++)
204 if (strncmp (value, prefix[i].s, prefix[i].len) == 0)
205 break;
206
207 if (i < sizeof (prefix) / sizeof (prefix[0]))
208 {
209 regex_t *preg;
210
211 preg = malloc (sizeof (regex_t));
212 if (preg)
213 {
214 int status;
215
216 value += prefix[i].len;
217
218 status = regcomp (preg, value, REG_NOSUB | prefix[i].flags);
219 if (status)
220 {
221 char errMsg[1024];
222
223 regerror (status, preg, errMsg, sizeof (errMsg));
224
225 compLogMessage ("regex", CompLogLevelWarn,
226 "%s = %s", errMsg, value);
227
228 regfree (preg);
229 free (preg);
230 preg = NULL;
231 }
232 }
233
234 exp->fini = regexMatchExpFini;
235 exp->eval = prefix[i].eval;
236 exp->priv.ptr = preg;
237 }
238 else
239 {
240 UNWRAP (rd, d, matchInitExp);
241 (*d->matchInitExp) (d, exp, value);
242 WRAP (rd, d, matchInitExp, regexMatchInitExp);
243 }
244 }
245
246 static char *
regexGetStringProperty(CompWindow * w,Atom propAtom,Atom formatAtom)247 regexGetStringProperty (CompWindow *w,
248 Atom propAtom,
249 Atom formatAtom)
250 {
251 Atom type;
252 unsigned long nItems;
253 unsigned long bytesAfter;
254 unsigned char *str = NULL;
255 int format, result;
256 char *retval;
257
258 result = XGetWindowProperty (w->screen->display->display,
259 w->id, propAtom, 0, LONG_MAX,
260 FALSE, formatAtom, &type, &format, &nItems,
261 &bytesAfter, (unsigned char **) &str);
262
263 if (result != Success)
264 return NULL;
265
266 if (type != formatAtom)
267 {
268 XFree (str);
269 return NULL;
270 }
271
272 retval = strdup ((char *) str);
273
274 XFree (str);
275
276 return retval;
277 }
278
279 static char *
regexGetWindowTitle(CompWindow * w)280 regexGetWindowTitle (CompWindow *w)
281 {
282 CompDisplay *d = w->screen->display;
283 char *title;
284
285 REGEX_DISPLAY (d);
286
287 title = regexGetStringProperty (w, rd->visibleNameAtom, d->utf8StringAtom);
288 if (title)
289 return title;
290
291 title = regexGetStringProperty (w, d->wmNameAtom, d->utf8StringAtom);
292 if (title)
293 return title;
294
295 return regexGetStringProperty (w, XA_WM_NAME, XA_STRING);
296 }
297
298 static void
regexHandleEvent(CompDisplay * d,XEvent * event)299 regexHandleEvent (CompDisplay *d,
300 XEvent *event)
301 {
302 REGEX_DISPLAY (d);
303
304 UNWRAP (rd, d, handleEvent);
305 (*d->handleEvent) (d, event);
306 WRAP (rd, d, handleEvent, regexHandleEvent);
307
308 if (event->type == PropertyNotify)
309 {
310 CompWindow *w;
311
312 if (event->xproperty.atom == XA_WM_NAME)
313 {
314 w = findWindowAtDisplay (d, event->xproperty.window);
315 if (w)
316 {
317 REGEX_WINDOW (w);
318
319 if (rw->title)
320 free (rw->title);
321
322 rw->title = regexGetWindowTitle (w);
323
324 (*d->matchPropertyChanged) (d, w);
325 }
326 }
327 if (event->xproperty.atom == rd->roleAtom)
328 {
329 w = findWindowAtDisplay (d, event->xproperty.window);
330 if (w)
331 {
332 REGEX_WINDOW (w);
333
334 if (rw->role)
335 free (rw->role);
336
337 rw->role = regexGetStringProperty (w, rd->roleAtom, XA_STRING);
338
339 (*d->matchPropertyChanged) (d, w);
340 }
341 }
342 else if (event->xproperty.atom == XA_WM_CLASS)
343 {
344 w = findWindowAtDisplay (d, event->xproperty.window);
345 if (w)
346 (*d->matchPropertyChanged) (d, w);
347 }
348 }
349 }
350
351 static Bool
regexRegisterExpHandler(void * closure)352 regexRegisterExpHandler (void *closure)
353 {
354 CompDisplay *display = (CompDisplay *) closure;
355
356 (*display->matchExpHandlerChanged) (display);
357
358 REGEX_DISPLAY (display);
359
360 rd->timeoutHandle = 0;
361
362 return FALSE;
363 }
364
365 static Bool
regexInitDisplay(CompPlugin * p,CompDisplay * d)366 regexInitDisplay (CompPlugin *p,
367 CompDisplay *d)
368 {
369 RegexDisplay *rd;
370
371 if (!checkPluginABI ("core", CORE_ABIVERSION))
372 return FALSE;
373
374 rd = malloc (sizeof (RegexDisplay));
375 if (!rd)
376 return FALSE;
377
378 rd->screenPrivateIndex = allocateScreenPrivateIndex (d);
379 if (rd->screenPrivateIndex < 0)
380 {
381 free (rd);
382 return FALSE;
383 }
384
385 rd->roleAtom = XInternAtom (d->display, "WM_WINDOW_ROLE", 0);
386 rd->visibleNameAtom = XInternAtom (d->display, "_NET_WM_VISIBLE_NAME", 0);
387
388 WRAP (rd, d, handleEvent, regexHandleEvent);
389 WRAP (rd, d, matchInitExp, regexMatchInitExp);
390
391 d->base.privates[displayPrivateIndex].ptr = rd;
392
393 /* one shot timeout to which will register the expression handler
394 after all screens and windows have been initialized */
395 rd->timeoutHandle =
396 compAddTimeout (0, 0, regexRegisterExpHandler, (void *) d);
397
398 return TRUE;
399 }
400
401 static void
regexFiniDisplay(CompPlugin * p,CompDisplay * d)402 regexFiniDisplay (CompPlugin *p,
403 CompDisplay *d)
404 {
405 REGEX_DISPLAY (d);
406
407 freeScreenPrivateIndex (d, rd->screenPrivateIndex);
408
409 if (rd->timeoutHandle)
410 compRemoveTimeout (rd->timeoutHandle);
411
412 UNWRAP (rd, d, handleEvent);
413 UNWRAP (rd, d, matchInitExp);
414
415 if (d->base.parent)
416 (*d->matchExpHandlerChanged) (d);
417
418 free (rd);
419 }
420
421 static Bool
regexInitScreen(CompPlugin * p,CompScreen * s)422 regexInitScreen (CompPlugin *p,
423 CompScreen *s)
424 {
425 RegexScreen *rs;
426
427 REGEX_DISPLAY (s->display);
428
429 rs = malloc (sizeof (RegexScreen));
430 if (!rs)
431 return FALSE;
432
433 rs->windowPrivateIndex = allocateWindowPrivateIndex (s);
434 if (rs->windowPrivateIndex < 0)
435 {
436 free (rs);
437 return FALSE;
438 }
439
440 s->base.privates[rd->screenPrivateIndex].ptr = rs;
441
442 return TRUE;
443 }
444
445 static void
regexFiniScreen(CompPlugin * p,CompScreen * s)446 regexFiniScreen (CompPlugin *p,
447 CompScreen *s)
448 {
449 REGEX_SCREEN (s);
450
451 freeWindowPrivateIndex (s, rs->windowPrivateIndex);
452
453 free (rs);
454 }
455
456 static Bool
regexInitWindow(CompPlugin * p,CompWindow * w)457 regexInitWindow (CompPlugin *p,
458 CompWindow *w)
459 {
460 RegexWindow *rw;
461
462 REGEX_DISPLAY (w->screen->display);
463 REGEX_SCREEN (w->screen);
464
465 rw = malloc (sizeof (RegexWindow));
466 if (!rw)
467 return FALSE;
468
469 rw->title = regexGetWindowTitle (w);
470 rw->role = regexGetStringProperty (w, rd->roleAtom, XA_STRING);
471
472 w->base.privates[rs->windowPrivateIndex].ptr = rw;
473
474 return TRUE;
475 }
476
477 static void
regexFiniWindow(CompPlugin * p,CompWindow * w)478 regexFiniWindow (CompPlugin *p,
479 CompWindow *w)
480 {
481 REGEX_WINDOW (w);
482
483 if (rw->title)
484 free (rw->title);
485
486 if (rw->role)
487 free (rw->role);
488
489 free (rw);
490 }
491
492 static CompBool
regexInitObject(CompPlugin * p,CompObject * o)493 regexInitObject (CompPlugin *p,
494 CompObject *o)
495 {
496 static InitPluginObjectProc dispTab[] = {
497 (InitPluginObjectProc) 0, /* InitCore */
498 (InitPluginObjectProc) regexInitDisplay,
499 (InitPluginObjectProc) regexInitScreen,
500 (InitPluginObjectProc) regexInitWindow
501 };
502
503 RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
504 }
505
506 static void
regexFiniObject(CompPlugin * p,CompObject * o)507 regexFiniObject (CompPlugin *p,
508 CompObject *o)
509 {
510 static FiniPluginObjectProc dispTab[] = {
511 (FiniPluginObjectProc) 0, /* FiniCore */
512 (FiniPluginObjectProc) regexFiniDisplay,
513 (FiniPluginObjectProc) regexFiniScreen,
514 (FiniPluginObjectProc) regexFiniWindow
515 };
516
517 DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
518 }
519
520 static Bool
regexInit(CompPlugin * p)521 regexInit (CompPlugin *p)
522 {
523 if (!compInitPluginMetadataFromInfo (®exMetadata, p->vTable->name,
524 0, 0, 0, 0))
525 return FALSE;
526
527 displayPrivateIndex = allocateDisplayPrivateIndex ();
528 if (displayPrivateIndex < 0)
529 {
530 compFiniMetadata (®exMetadata);
531 return FALSE;
532 }
533
534 compAddMetadataFromFile (®exMetadata, p->vTable->name);
535
536 return TRUE;
537 }
538
539 static void
regexFini(CompPlugin * p)540 regexFini (CompPlugin *p)
541 {
542 freeDisplayPrivateIndex (displayPrivateIndex);
543 compFiniMetadata (®exMetadata);
544 }
545
546 static CompMetadata *
regexGetMetadata(CompPlugin * plugin)547 regexGetMetadata (CompPlugin *plugin)
548 {
549 return ®exMetadata;
550 }
551
552 static CompPluginVTable regexVTable = {
553 "regex",
554 regexGetMetadata,
555 regexInit,
556 regexFini,
557 regexInitObject,
558 regexFiniObject,
559 0, /* GetObjectOptions */
560 0 /* SetObjectOption */
561 };
562
563 CompPluginVTable *
getCompPluginInfo20070830(void)564 getCompPluginInfo20070830 (void)
565 {
566 return ®exVTable;
567 }
568