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