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 (&regexMetadata, p->vTable->name,
524 					 0, 0, 0, 0))
525 	return FALSE;
526 
527     displayPrivateIndex = allocateDisplayPrivateIndex ();
528     if (displayPrivateIndex < 0)
529     {
530 	compFiniMetadata (&regexMetadata);
531 	return FALSE;
532     }
533 
534     compAddMetadataFromFile (&regexMetadata, 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 (&regexMetadata);
544 }
545 
546 static CompMetadata *
regexGetMetadata(CompPlugin * plugin)547 regexGetMetadata (CompPlugin *plugin)
548 {
549     return &regexMetadata;
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 &regexVTable;
567 }
568