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 
29 #include <compiz-core.h>
30 
31 static void
matchResetOps(CompDisplay * display,CompMatchOp * op,int nOp)32 matchResetOps (CompDisplay *display,
33 	       CompMatchOp *op,
34 	       int	   nOp)
35 {
36     while (nOp--)
37     {
38 	switch (op->type) {
39 	case CompMatchOpTypeGroup:
40 	    matchResetOps (display, op->group.op, op->group.nOp);
41 	    break;
42 	case CompMatchOpTypeExp:
43 	    if (op->exp.e.fini)
44 	    {
45 		(*op->exp.e.fini) (display, op->exp.e.priv);
46 		op->exp.e.fini = NULL;
47 	    }
48 
49 	    op->exp.e.eval     = NULL;
50 	    op->exp.e.priv.val = 0;
51 	    break;
52 	}
53 
54 	op++;
55     }
56 }
57 
58 static void
matchReset(CompMatch * match)59 matchReset (CompMatch *match)
60 {
61     if (match->display)
62 	matchResetOps (match->display, match->op, match->nOp);
63 
64     match->display = NULL;
65 }
66 
67 void
matchInit(CompMatch * match)68 matchInit (CompMatch *match)
69 {
70     match->display = NULL;
71     match->op	   = NULL;
72     match->nOp	    = 0;
73 }
74 
75 static void
matchFiniOps(CompMatchOp * op,int nOp)76 matchFiniOps (CompMatchOp *op,
77 	      int	  nOp)
78 {
79     while (nOp--)
80     {
81 	switch (op->type) {
82 	case CompMatchOpTypeGroup:
83 	    matchFiniOps (op->group.op, op->group.nOp);
84 	    free (op->group.op);
85 	    break;
86 	case CompMatchOpTypeExp:
87 	    free (op->exp.value);
88 	    break;
89 	}
90 
91 	op++;
92     }
93 }
94 
95 void
matchFini(CompMatch * match)96 matchFini (CompMatch *match)
97 {
98     matchReset (match);
99     matchFiniOps (match->op, match->nOp);
100     free (match->op);
101 }
102 
103 static Bool
matchOpsEqual(CompMatchOp * op1,CompMatchOp * op2,int nOp)104 matchOpsEqual (CompMatchOp *op1,
105 	       CompMatchOp *op2,
106 	       int	   nOp)
107 {
108     while (nOp--)
109     {
110 	if (op1->type != op2->type)
111 	    return FALSE;
112 
113 	switch (op1->type) {
114 	case CompMatchOpTypeGroup:
115 	    if (op1->group.nOp != op2->group.nOp)
116 		return FALSE;
117 
118 	    if (!matchOpsEqual (op1->group.op, op2->group.op, op1->group.nOp))
119 		return FALSE;
120 
121 	    break;
122 	case CompMatchOpTypeExp:
123 	    if (op1->exp.flags != op2->exp.flags)
124 		return FALSE;
125 
126 	    if (strcmp (op1->exp.value, op2->exp.value))
127 		return FALSE;
128 
129 	    break;
130 	}
131 
132 	op1++;
133 	op2++;
134     }
135 
136     return TRUE;
137 }
138 
139 Bool
matchEqual(CompMatch * m1,CompMatch * m2)140 matchEqual (CompMatch *m1,
141 	    CompMatch *m2)
142 {
143     if (m1->nOp != m2->nOp)
144 	return FALSE;
145 
146     return matchOpsEqual (m1->op, m2->op, m1->nOp);
147 }
148 
149 static CompMatchOp *
matchAddOp(CompMatch * match,CompMatchOpType type,int flags)150 matchAddOp (CompMatch	    *match,
151 	    CompMatchOpType type,
152 	    int		    flags)
153 {
154     CompMatchOp *op;
155 
156     /* remove AND prefix if this is the first op in this group */
157     if (!match->nOp)
158 	flags &= ~MATCH_OP_AND_MASK;
159 
160     op = realloc (match->op, sizeof (CompMatchOp) * (match->nOp + 1));
161     if (!op)
162 	return FALSE;
163 
164     op[match->nOp].any.type  = type;
165     op[match->nOp].any.flags = flags;
166 
167     match->op = op;
168     match->nOp++;
169 
170     return &match->op[match->nOp - 1];
171 }
172 
173 static Bool
matchCopyOps(CompMatchOp * opDst,CompMatchOp * opSrc,int nOpSrc)174 matchCopyOps (CompMatchOp *opDst,
175 	      CompMatchOp *opSrc,
176 	      int	   nOpSrc)
177 {
178     CompMatchOp *op, *first = opDst;
179     int		count = 0;
180 
181     while (nOpSrc--)
182     {
183 	opDst->any.type  = opSrc->any.type;
184 	opDst->any.flags = opSrc->any.flags;
185 
186 	switch (opSrc->type) {
187 	case CompMatchOpTypeGroup:
188 	    op = malloc (sizeof (CompMatchOp) * opSrc->group.nOp);
189 	    if (!op)
190 	    {
191 		matchFiniOps (first, count);
192 		return FALSE;
193 	    }
194 
195 	    if (!matchCopyOps (op, opSrc->group.op, opSrc->group.nOp))
196 	    {
197 		free (op);
198 		matchFiniOps (first, count);
199 		return FALSE;
200 	    }
201 
202 	    opDst->group.op  = op;
203 	    opDst->group.nOp = opSrc->group.nOp;
204 	    break;
205 	case CompMatchOpTypeExp:
206 	    opDst->exp.value = strdup (opSrc->exp.value);
207 	    if (!opDst->exp.value)
208 	    {
209 		matchFiniOps (first, count);
210 		return FALSE;
211 	    }
212 
213 	    opDst->exp.e.fini	  = NULL;
214 	    opDst->exp.e.eval	  = NULL;
215 	    opDst->exp.e.priv.val = 0;
216 	    break;
217 	}
218 
219 	count++;
220 	opDst++;
221 	opSrc++;
222     }
223 
224     return TRUE;
225 }
226 
227 Bool
matchCopy(CompMatch * dst,CompMatch * src)228 matchCopy (CompMatch *dst,
229 	   CompMatch *src)
230 {
231     CompMatchOp *opDst;
232 
233     opDst = malloc (sizeof (CompMatchOp) * src->nOp);
234     if (!opDst)
235 	return FALSE;
236 
237     if (!matchCopyOps (opDst, src->op, src->nOp))
238     {
239 	free (opDst);
240 	return FALSE;
241     }
242 
243     dst->op  = opDst;
244     dst->nOp = src->nOp;
245 
246     return TRUE;
247 }
248 
249 Bool
matchAddGroup(CompMatch * match,int flags,CompMatch * group)250 matchAddGroup (CompMatch *match,
251 	       int	 flags,
252 	       CompMatch *group)
253 {
254     CompMatchOp *op, *opDst;
255 
256     opDst = malloc (sizeof (CompMatchOp) * group->nOp);
257     if (!opDst)
258 	return FALSE;
259 
260     if (!matchCopyOps (opDst, group->op, group->nOp))
261     {
262 	free (opDst);
263 	return FALSE;
264     }
265 
266     op = matchAddOp (match, CompMatchOpTypeGroup, flags);
267     if (!op)
268     {
269 	matchFiniOps (opDst, group->nOp);
270 	free (opDst);
271 	return FALSE;
272     }
273 
274     op->group.op  = opDst;
275     op->group.nOp = group->nOp;
276 
277     return TRUE;
278 }
279 
280 Bool
matchAddExp(CompMatch * match,int flags,const char * str)281 matchAddExp (CompMatch  *match,
282 	     int        flags,
283 	     const char *str)
284 {
285     CompMatchOp *op;
286     char	*value;
287 
288     value = strdup (str);
289     if (!value)
290 	return FALSE;
291 
292     op = matchAddOp (match, CompMatchOpTypeExp, flags);
293     if (!op)
294     {
295 	free (value);
296 	return FALSE;
297     }
298 
299     op->exp.value      = value;
300     op->exp.e.fini     = NULL;
301     op->exp.e.eval     = NULL;
302     op->exp.e.priv.val = 0;
303 
304     return TRUE;
305 }
306 
307 static int
nextIndex(const char * str,int i)308 nextIndex (const char *str,
309 	   int	      i)
310 {
311     while (str[i] == '\\')
312 	if (str[++i] != '\0')
313 	    i++;
314 
315     return i;
316 }
317 
318 static char *
strndupValue(const char * str,int n)319 strndupValue (const char *str,
320 	      int	 n)
321 {
322     char *value;
323 
324     value = malloc (sizeof (char) * (n + 1));
325     if (value)
326     {
327 	int i, j;
328 
329 	/* count trialing white spaces */
330 	i = j = 0;
331 	while (i < n)
332 	{
333 	    if (str[i] != ' ')
334 	    {
335 		j = 0;
336 		if (str[i] == '\\')
337 		    i++;
338 	    }
339 	    else
340 	    {
341 		j++;
342 	    }
343 
344 	    i++;
345 	}
346 
347 	/* remove trialing white spaces */
348 	n -= j;
349 
350 	i = j = 0;
351 	for (;;)
352 	{
353 	    if (str[i] == '\\')
354 		i++;
355 
356 	    value[j++] = str[i++];
357 
358 	    if (i >= n)
359 	    {
360 		value[j] = '\0';
361 		return value;
362 	    }
363 	}
364     }
365 
366     return NULL;
367 }
368 
369 /*
370   Add match expressions from string. Special characters are
371   '(', ')', '!', '&', '|'. Escape character is '\'.
372 
373   Example:
374 
375   "type=desktop | !type=dock"
376   "!type=dock & (state=fullscreen | state=shaded)"
377 */
378 void
matchAddFromString(CompMatch * match,const char * str)379 matchAddFromString (CompMatch  *match,
380 		    const char *str)
381 {
382     char *value;
383     int	 j, i = 0;
384     int	 flags = 0;
385 
386     while (str[i] != '\0')
387     {
388 	while (str[i] == ' ')
389 	    i++;
390 
391 	if (str[i] == '!')
392 	{
393 	    flags |= MATCH_OP_NOT_MASK;
394 
395 	    i++;
396 	    while (str[i] == ' ')
397 		i++;
398 	}
399 
400 	if (str[i] == '(')
401 	{
402 	    int	level = 1;
403 	    int length;
404 
405 	    j = ++i;
406 
407 	    while (str[j] != '\0')
408 	    {
409 		if (str[j] == '(')
410 		{
411 		    level++;
412 		}
413 		else if (str[j] == ')')
414 		{
415 		    level--;
416 		    if (level == 0)
417 			break;
418 		}
419 
420 		j = nextIndex (str, ++j);
421 	    }
422 
423 	    length = j - i;
424 
425 	    value = malloc (sizeof (char) * (length + 1));
426 	    if (value)
427 	    {
428 		CompMatch group;
429 
430 		strncpy (value, &str[i], length);
431 		value[length] = '\0';
432 
433 		matchInit (&group);
434 		matchAddFromString (&group, value);
435 		matchAddGroup (match, flags, &group);
436 		matchFini (&group);
437 
438 		free (value);
439 	    }
440 
441 	    while (str[j] != '\0' && str[j] != '|' && str[j] != '&')
442 		j++;
443 	}
444 	else
445 	{
446 	    j = i;
447 
448 	    while (str[j] != '\0' && str[j] != '|' && str[j] != '&')
449 		j = nextIndex (str, ++j);
450 
451 	    value = strndupValue (&str[i], j - i);
452 	    if (value)
453 	    {
454 		matchAddExp (match, flags, value);
455 
456 		free (value);
457 	    }
458 	}
459 
460 	i = j;
461 
462 	if (str[i] != '\0')
463 	{
464 	    if (str[i] == '&')
465 		flags = MATCH_OP_AND_MASK;
466 
467 	    i++;
468 	}
469     }
470 }
471 
472 static char *
matchOpsToString(CompMatchOp * op,int nOp)473 matchOpsToString (CompMatchOp *op,
474 		  int	      nOp)
475 {
476     char *value, *group;
477     char *str = NULL;
478     int  length = 0;
479 
480     while (nOp--)
481     {
482 	value = NULL;
483 
484 	switch (op->type) {
485 	case CompMatchOpTypeGroup:
486 	    group = matchOpsToString (op->group.op, op->group.nOp);
487 	    if (group)
488 	    {
489 		value = malloc (sizeof (char) * (strlen (group) + 7));
490 		if (value)
491 		    sprintf (value, "%s%s(%s)%s", !str ? "" :
492 			     ((op->any.flags & MATCH_OP_AND_MASK) ?
493 			      "& " : "| "),
494 			     (op->any.flags & MATCH_OP_NOT_MASK) ? "!" : "",
495 			     group, nOp ? " " : "");
496 
497 		free (group);
498 	    }
499 	    break;
500 	case CompMatchOpTypeExp:
501 	    value = malloc (sizeof (char) * (strlen (op->exp.value) + 5));
502 	    if (value)
503 		sprintf (value, "%s%s%s%s", !str ? "" :
504 			 ((op->any.flags & MATCH_OP_AND_MASK) ? "& " : "| "),
505 			 (op->any.flags & MATCH_OP_NOT_MASK) ? "!" : "",
506 			 op->exp.value, nOp ? " " : "");
507 	    break;
508 	}
509 
510 	if (value)
511 	{
512 	    char *s;
513 	    int  valueLength = strlen (value);
514 
515 	    s = malloc (sizeof (char) * (length + valueLength + 1));
516 	    if (s)
517 	    {
518 		if (str)
519 		    memcpy (s, str, sizeof (char) * length);
520 
521 		memcpy (s + length, value, sizeof (char) * valueLength);
522 
523 		length += valueLength;
524 
525 		s[length] = '\0';
526 
527 		if (str)
528 		    free (str);
529 
530 		str = s;
531 	    }
532 
533 	    free (value);
534 	}
535 
536 	op++;
537     }
538 
539     return str;
540 }
541 
542 char *
matchToString(CompMatch * match)543 matchToString (CompMatch *match)
544 {
545     char *str;
546 
547     str = matchOpsToString (match->op, match->nOp);
548     if (!str)
549 	str = strdup ("");
550 
551     return str;
552 }
553 
554 static void
matchUpdateOps(CompDisplay * display,CompMatchOp * op,int nOp)555 matchUpdateOps (CompDisplay *display,
556 		CompMatchOp *op,
557 		int	    nOp)
558 {
559     while (nOp--)
560     {
561 	switch (op->type) {
562 	case CompMatchOpTypeGroup:
563 	    matchUpdateOps (display, op->group.op, op->group.nOp);
564 	    break;
565 	case CompMatchOpTypeExp:
566 	    (*display->matchInitExp) (display, &op->exp.e, op->exp.value);
567 	    break;
568 	}
569 
570 	op++;
571     }
572 }
573 
574 void
matchUpdate(CompDisplay * display,CompMatch * match)575 matchUpdate (CompDisplay *display,
576 	     CompMatch   *match)
577 {
578     matchReset (match);
579     matchUpdateOps (display, match->op, match->nOp);
580     match->display = display;
581 }
582 
583 static Bool
matchEvalOps(CompDisplay * display,CompMatchOp * op,int nOp,CompWindow * window)584 matchEvalOps (CompDisplay *display,
585 	      CompMatchOp *op,
586 	      int	  nOp,
587 	      CompWindow  *window)
588 {
589     Bool value, result = FALSE;
590 
591     while (nOp--)
592     {
593 	/* fast evaluation */
594 	if (op->any.flags & MATCH_OP_AND_MASK)
595 	{
596 	    /* result will never be true */
597 	    if (!result)
598 		return FALSE;
599 	}
600 	else
601 	{
602 	    /* result will always be true */
603 	    if (result)
604 		return TRUE;
605 	}
606 
607 	switch (op->type) {
608 	case CompMatchOpTypeGroup:
609 	    value = matchEvalOps (display, op->group.op, op->group.nOp, window);
610 	    break;
611 	case CompMatchOpTypeExp:
612 	default:
613 	    value = (*op->exp.e.eval) (display, window, op->exp.e.priv);
614 	    break;
615 	}
616 
617 	if (op->any.flags & MATCH_OP_NOT_MASK)
618 	    value = !value;
619 
620 	if (op->any.flags & MATCH_OP_AND_MASK)
621 	    result = (result && value);
622 	else
623 	    result = (result || value);
624 
625 	op++;
626     }
627 
628     return result;
629 }
630 
631 Bool
matchEval(CompMatch * match,CompWindow * window)632 matchEval (CompMatch  *match,
633 	   CompWindow *window)
634 {
635     if (match->display)
636 	return matchEvalOps (match->display, match->op, match->nOp, window);
637 
638     return FALSE;
639 }
640 
641 static Bool
matchEvalTypeExp(CompDisplay * display,CompWindow * window,CompPrivate private)642 matchEvalTypeExp (CompDisplay *display,
643 		  CompWindow  *window,
644 		  CompPrivate private)
645 {
646     return (private.uval & window->wmType);
647 }
648 
649 static Bool
matchEvalStateExp(CompDisplay * display,CompWindow * window,CompPrivate private)650 matchEvalStateExp (CompDisplay *display,
651 		   CompWindow  *window,
652 		   CompPrivate private)
653 {
654     return (private.uval & window->state);
655 }
656 
657 static Bool
matchEvalIdExp(CompDisplay * display,CompWindow * window,CompPrivate private)658 matchEvalIdExp (CompDisplay *display,
659 		CompWindow  *window,
660 		CompPrivate private)
661 {
662     return (private.val == window->id);
663 }
664 
665 static Bool
matchEvalOverrideRedirectExp(CompDisplay * display,CompWindow * window,CompPrivate private)666 matchEvalOverrideRedirectExp (CompDisplay *display,
667 			      CompWindow  *window,
668 			      CompPrivate private)
669 {
670     Bool overrideRedirect = window->attrib.override_redirect;
671     return ((private.val == 1 && overrideRedirect) ||
672 	    (private.val == 0 && !overrideRedirect));
673 }
674 
675 static Bool
matchEvalAlphaExp(CompDisplay * display,CompWindow * window,CompPrivate private)676 matchEvalAlphaExp (CompDisplay *display,
677 		   CompWindow  *window,
678 		   CompPrivate private)
679 {
680     return ((private.val && window->alpha) ||
681 	    (!private.val && !window->alpha));
682 }
683 
684 void
matchInitExp(CompDisplay * display,CompMatchExp * exp,const char * value)685 matchInitExp (CompDisplay  *display,
686 	      CompMatchExp *exp,
687 	      const char   *value)
688 {
689     if (strncmp (value, "xid=", 4) == 0)
690     {
691 	exp->eval     = matchEvalIdExp;
692 	exp->priv.val = strtol (value + 4, NULL, 0);
693     }
694     else if (strncmp (value, "state=", 6) == 0)
695     {
696 	exp->eval      = matchEvalStateExp;
697 	exp->priv.uval = windowStateFromString (value + 6);
698     }
699     else if (strncmp (value, "override_redirect=", 18) == 0)
700     {
701 	exp->eval     = matchEvalOverrideRedirectExp;
702 	exp->priv.val = strtol (value + 18, NULL, 0);
703     }
704     else if (strncmp (value, "rgba=", 5) == 0)
705     {
706 	exp->eval     = matchEvalAlphaExp;
707 	exp->priv.val = strtol (value + 5, NULL, 0);
708     }
709     else
710     {
711 	if (strncmp (value, "type=", 5) == 0)
712 	    value += 5;
713 
714 	exp->eval      = matchEvalTypeExp;
715 	exp->priv.uval = windowTypeFromString (value);
716     }
717 }
718 
719 static void
matchUpdateMatchOptions(CompOption * option,int nOption)720 matchUpdateMatchOptions (CompOption *option,
721 			 int	    nOption)
722 {
723     while (nOption--)
724     {
725 	switch (option->type) {
726 	case CompOptionTypeMatch:
727 	    if (option->value.match.display)
728 		matchUpdate (option->value.match.display, &option->value.match);
729 	    break;
730 	case CompOptionTypeList:
731 	    if (option->value.list.type == CompOptionTypeMatch)
732 	    {
733 		int i;
734 
735 		for (i = 0; i < option->value.list.nValue; i++)
736 		    if (option->value.list.value[i].match.display)
737 			matchUpdate (option->value.list.value[i].match.display,
738 				     &option->value.list.value[i].match);
739 	    }
740 	default:
741 	    break;
742 	}
743 
744 	option++;
745     }
746 }
747 
748 void
matchExpHandlerChanged(CompDisplay * display)749 matchExpHandlerChanged (CompDisplay *display)
750 {
751     CompOption *option;
752     int	       nOption;
753     CompPlugin *p;
754     CompScreen *s;
755 
756     for (p = getPlugins (); p; p = p->next)
757     {
758 	if (!p->vTable->getObjectOptions)
759 	    continue;
760 
761 	option = (*p->vTable->getObjectOptions) (p, &display->base, &nOption);
762 	matchUpdateMatchOptions (option, nOption);
763     }
764 
765     for (s = display->screens; s; s = s->next)
766     {
767 	for (p = getPlugins (); p; p = p->next)
768 	{
769 	    if (!p->vTable->getObjectOptions)
770 		continue;
771 
772 	    option = (*p->vTable->getObjectOptions) (p, &s->base, &nOption);
773 	    matchUpdateMatchOptions (option, nOption);
774 	}
775     }
776 }
777 
778 void
matchPropertyChanged(CompDisplay * display,CompWindow * w)779 matchPropertyChanged (CompDisplay *display,
780 		      CompWindow  *w)
781 {
782 }
783