You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
783 lines
14 KiB
783 lines
14 KiB
/*
|
|
* Copyright © 2007 Novell, Inc.
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software
|
|
* and its documentation for any purpose is hereby granted without
|
|
* fee, provided that the above copyright notice appear in all copies
|
|
* and that both that copyright notice and this permission notice
|
|
* appear in supporting documentation, and that the name of
|
|
* Novell, Inc. not be used in advertising or publicity pertaining to
|
|
* distribution of the software without specific, written prior permission.
|
|
* Novell, Inc. makes no representations about the suitability of this
|
|
* software for any purpose. It is provided "as is" without express or
|
|
* implied warranty.
|
|
*
|
|
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
|
|
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* Author: David Reveman <davidr@novell.com>
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <compiz-core.h>
|
|
|
|
static void
|
|
matchResetOps (CompDisplay *display,
|
|
CompMatchOp *op,
|
|
int nOp)
|
|
{
|
|
while (nOp--)
|
|
{
|
|
switch (op->type) {
|
|
case CompMatchOpTypeGroup:
|
|
matchResetOps (display, op->group.op, op->group.nOp);
|
|
break;
|
|
case CompMatchOpTypeExp:
|
|
if (op->exp.e.fini)
|
|
{
|
|
(*op->exp.e.fini) (display, op->exp.e.priv);
|
|
op->exp.e.fini = NULL;
|
|
}
|
|
|
|
op->exp.e.eval = NULL;
|
|
op->exp.e.priv.val = 0;
|
|
break;
|
|
}
|
|
|
|
op++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
matchReset (CompMatch *match)
|
|
{
|
|
if (match->display)
|
|
matchResetOps (match->display, match->op, match->nOp);
|
|
|
|
match->display = NULL;
|
|
}
|
|
|
|
void
|
|
matchInit (CompMatch *match)
|
|
{
|
|
match->display = NULL;
|
|
match->op = NULL;
|
|
match->nOp = 0;
|
|
}
|
|
|
|
static void
|
|
matchFiniOps (CompMatchOp *op,
|
|
int nOp)
|
|
{
|
|
while (nOp--)
|
|
{
|
|
switch (op->type) {
|
|
case CompMatchOpTypeGroup:
|
|
matchFiniOps (op->group.op, op->group.nOp);
|
|
free (op->group.op);
|
|
break;
|
|
case CompMatchOpTypeExp:
|
|
free (op->exp.value);
|
|
break;
|
|
}
|
|
|
|
op++;
|
|
}
|
|
}
|
|
|
|
void
|
|
matchFini (CompMatch *match)
|
|
{
|
|
matchReset (match);
|
|
matchFiniOps (match->op, match->nOp);
|
|
free (match->op);
|
|
}
|
|
|
|
static Bool
|
|
matchOpsEqual (CompMatchOp *op1,
|
|
CompMatchOp *op2,
|
|
int nOp)
|
|
{
|
|
while (nOp--)
|
|
{
|
|
if (op1->type != op2->type)
|
|
return FALSE;
|
|
|
|
switch (op1->type) {
|
|
case CompMatchOpTypeGroup:
|
|
if (op1->group.nOp != op2->group.nOp)
|
|
return FALSE;
|
|
|
|
if (!matchOpsEqual (op1->group.op, op2->group.op, op1->group.nOp))
|
|
return FALSE;
|
|
|
|
break;
|
|
case CompMatchOpTypeExp:
|
|
if (op1->exp.flags != op2->exp.flags)
|
|
return FALSE;
|
|
|
|
if (strcmp (op1->exp.value, op2->exp.value))
|
|
return FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
op1++;
|
|
op2++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
matchEqual (CompMatch *m1,
|
|
CompMatch *m2)
|
|
{
|
|
if (m1->nOp != m2->nOp)
|
|
return FALSE;
|
|
|
|
return matchOpsEqual (m1->op, m2->op, m1->nOp);
|
|
}
|
|
|
|
static CompMatchOp *
|
|
matchAddOp (CompMatch *match,
|
|
CompMatchOpType type,
|
|
int flags)
|
|
{
|
|
CompMatchOp *op;
|
|
|
|
/* remove AND prefix if this is the first op in this group */
|
|
if (!match->nOp)
|
|
flags &= ~MATCH_OP_AND_MASK;
|
|
|
|
op = realloc (match->op, sizeof (CompMatchOp) * (match->nOp + 1));
|
|
if (!op)
|
|
return FALSE;
|
|
|
|
op[match->nOp].any.type = type;
|
|
op[match->nOp].any.flags = flags;
|
|
|
|
match->op = op;
|
|
match->nOp++;
|
|
|
|
return &match->op[match->nOp - 1];
|
|
}
|
|
|
|
static Bool
|
|
matchCopyOps (CompMatchOp *opDst,
|
|
CompMatchOp *opSrc,
|
|
int nOpSrc)
|
|
{
|
|
CompMatchOp *op, *first = opDst;
|
|
int count = 0;
|
|
|
|
while (nOpSrc--)
|
|
{
|
|
opDst->any.type = opSrc->any.type;
|
|
opDst->any.flags = opSrc->any.flags;
|
|
|
|
switch (opSrc->type) {
|
|
case CompMatchOpTypeGroup:
|
|
op = malloc (sizeof (CompMatchOp) * opSrc->group.nOp);
|
|
if (!op)
|
|
{
|
|
matchFiniOps (first, count);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!matchCopyOps (op, opSrc->group.op, opSrc->group.nOp))
|
|
{
|
|
free (op);
|
|
matchFiniOps (first, count);
|
|
return FALSE;
|
|
}
|
|
|
|
opDst->group.op = op;
|
|
opDst->group.nOp = opSrc->group.nOp;
|
|
break;
|
|
case CompMatchOpTypeExp:
|
|
opDst->exp.value = strdup (opSrc->exp.value);
|
|
if (!opDst->exp.value)
|
|
{
|
|
matchFiniOps (first, count);
|
|
return FALSE;
|
|
}
|
|
|
|
opDst->exp.e.fini = NULL;
|
|
opDst->exp.e.eval = NULL;
|
|
opDst->exp.e.priv.val = 0;
|
|
break;
|
|
}
|
|
|
|
count++;
|
|
opDst++;
|
|
opSrc++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
matchCopy (CompMatch *dst,
|
|
CompMatch *src)
|
|
{
|
|
CompMatchOp *opDst;
|
|
|
|
opDst = malloc (sizeof (CompMatchOp) * src->nOp);
|
|
if (!opDst)
|
|
return FALSE;
|
|
|
|
if (!matchCopyOps (opDst, src->op, src->nOp))
|
|
{
|
|
free (opDst);
|
|
return FALSE;
|
|
}
|
|
|
|
dst->op = opDst;
|
|
dst->nOp = src->nOp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
matchAddGroup (CompMatch *match,
|
|
int flags,
|
|
CompMatch *group)
|
|
{
|
|
CompMatchOp *op, *opDst;
|
|
|
|
opDst = malloc (sizeof (CompMatchOp) * group->nOp);
|
|
if (!opDst)
|
|
return FALSE;
|
|
|
|
if (!matchCopyOps (opDst, group->op, group->nOp))
|
|
{
|
|
free (opDst);
|
|
return FALSE;
|
|
}
|
|
|
|
op = matchAddOp (match, CompMatchOpTypeGroup, flags);
|
|
if (!op)
|
|
{
|
|
matchFiniOps (opDst, group->nOp);
|
|
free (opDst);
|
|
return FALSE;
|
|
}
|
|
|
|
op->group.op = opDst;
|
|
op->group.nOp = group->nOp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
matchAddExp (CompMatch *match,
|
|
int flags,
|
|
const char *str)
|
|
{
|
|
CompMatchOp *op;
|
|
char *value;
|
|
|
|
value = strdup (str);
|
|
if (!value)
|
|
return FALSE;
|
|
|
|
op = matchAddOp (match, CompMatchOpTypeExp, flags);
|
|
if (!op)
|
|
{
|
|
free (value);
|
|
return FALSE;
|
|
}
|
|
|
|
op->exp.value = value;
|
|
op->exp.e.fini = NULL;
|
|
op->exp.e.eval = NULL;
|
|
op->exp.e.priv.val = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static int
|
|
nextIndex (const char *str,
|
|
int i)
|
|
{
|
|
while (str[i] == '\\')
|
|
if (str[++i] != '\0')
|
|
i++;
|
|
|
|
return i;
|
|
}
|
|
|
|
static char *
|
|
strndupValue (const char *str,
|
|
int n)
|
|
{
|
|
char *value;
|
|
|
|
value = malloc (sizeof (char) * (n + 1));
|
|
if (value)
|
|
{
|
|
int i, j;
|
|
|
|
/* count trialing white spaces */
|
|
i = j = 0;
|
|
while (i < n)
|
|
{
|
|
if (str[i] != ' ')
|
|
{
|
|
j = 0;
|
|
if (str[i] == '\\')
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
j++;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
/* remove trialing white spaces */
|
|
n -= j;
|
|
|
|
i = j = 0;
|
|
for (;;)
|
|
{
|
|
if (str[i] == '\\')
|
|
i++;
|
|
|
|
value[j++] = str[i++];
|
|
|
|
if (i >= n)
|
|
{
|
|
value[j] = '\0';
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
Add match expressions from string. Special characters are
|
|
'(', ')', '!', '&', '|'. Escape character is '\'.
|
|
|
|
Example:
|
|
|
|
"type=desktop | !type=dock"
|
|
"!type=dock & (state=fullscreen | state=shaded)"
|
|
*/
|
|
void
|
|
matchAddFromString (CompMatch *match,
|
|
const char *str)
|
|
{
|
|
char *value;
|
|
int j, i = 0;
|
|
int flags = 0;
|
|
|
|
while (str[i] != '\0')
|
|
{
|
|
while (str[i] == ' ')
|
|
i++;
|
|
|
|
if (str[i] == '!')
|
|
{
|
|
flags |= MATCH_OP_NOT_MASK;
|
|
|
|
i++;
|
|
while (str[i] == ' ')
|
|
i++;
|
|
}
|
|
|
|
if (str[i] == '(')
|
|
{
|
|
int level = 1;
|
|
int length;
|
|
|
|
j = ++i;
|
|
|
|
while (str[j] != '\0')
|
|
{
|
|
if (str[j] == '(')
|
|
{
|
|
level++;
|
|
}
|
|
else if (str[j] == ')')
|
|
{
|
|
level--;
|
|
if (level == 0)
|
|
break;
|
|
}
|
|
|
|
j = nextIndex (str, ++j);
|
|
}
|
|
|
|
length = j - i;
|
|
|
|
value = malloc (sizeof (char) * (length + 1));
|
|
if (value)
|
|
{
|
|
CompMatch group;
|
|
|
|
strncpy (value, &str[i], length);
|
|
value[length] = '\0';
|
|
|
|
matchInit (&group);
|
|
matchAddFromString (&group, value);
|
|
matchAddGroup (match, flags, &group);
|
|
matchFini (&group);
|
|
|
|
free (value);
|
|
}
|
|
|
|
while (str[j] != '\0' && str[j] != '|' && str[j] != '&')
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
j = i;
|
|
|
|
while (str[j] != '\0' && str[j] != '|' && str[j] != '&')
|
|
j = nextIndex (str, ++j);
|
|
|
|
value = strndupValue (&str[i], j - i);
|
|
if (value)
|
|
{
|
|
matchAddExp (match, flags, value);
|
|
|
|
free (value);
|
|
}
|
|
}
|
|
|
|
i = j;
|
|
|
|
if (str[i] != '\0')
|
|
{
|
|
if (str[i] == '&')
|
|
flags = MATCH_OP_AND_MASK;
|
|
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *
|
|
matchOpsToString (CompMatchOp *op,
|
|
int nOp)
|
|
{
|
|
char *value, *group;
|
|
char *str = NULL;
|
|
int length = 0;
|
|
|
|
while (nOp--)
|
|
{
|
|
value = NULL;
|
|
|
|
switch (op->type) {
|
|
case CompMatchOpTypeGroup:
|
|
group = matchOpsToString (op->group.op, op->group.nOp);
|
|
if (group)
|
|
{
|
|
value = malloc (sizeof (char) * (strlen (group) + 7));
|
|
if (value)
|
|
sprintf (value, "%s%s(%s)%s", !str ? "" :
|
|
((op->any.flags & MATCH_OP_AND_MASK) ?
|
|
"& " : "| "),
|
|
(op->any.flags & MATCH_OP_NOT_MASK) ? "!" : "",
|
|
group, nOp ? " " : "");
|
|
|
|
free (group);
|
|
}
|
|
break;
|
|
case CompMatchOpTypeExp:
|
|
value = malloc (sizeof (char) * (strlen (op->exp.value) + 5));
|
|
if (value)
|
|
sprintf (value, "%s%s%s%s", !str ? "" :
|
|
((op->any.flags & MATCH_OP_AND_MASK) ? "& " : "| "),
|
|
(op->any.flags & MATCH_OP_NOT_MASK) ? "!" : "",
|
|
op->exp.value, nOp ? " " : "");
|
|
break;
|
|
}
|
|
|
|
if (value)
|
|
{
|
|
char *s;
|
|
int valueLength = strlen (value);
|
|
|
|
s = malloc (sizeof (char) * (length + valueLength + 1));
|
|
if (s)
|
|
{
|
|
if (str)
|
|
memcpy (s, str, sizeof (char) * length);
|
|
|
|
memcpy (s + length, value, sizeof (char) * valueLength);
|
|
|
|
length += valueLength;
|
|
|
|
s[length] = '\0';
|
|
|
|
if (str)
|
|
free (str);
|
|
|
|
str = s;
|
|
}
|
|
|
|
free (value);
|
|
}
|
|
|
|
op++;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
char *
|
|
matchToString (CompMatch *match)
|
|
{
|
|
char *str;
|
|
|
|
str = matchOpsToString (match->op, match->nOp);
|
|
if (!str)
|
|
str = strdup ("");
|
|
|
|
return str;
|
|
}
|
|
|
|
static void
|
|
matchUpdateOps (CompDisplay *display,
|
|
CompMatchOp *op,
|
|
int nOp)
|
|
{
|
|
while (nOp--)
|
|
{
|
|
switch (op->type) {
|
|
case CompMatchOpTypeGroup:
|
|
matchUpdateOps (display, op->group.op, op->group.nOp);
|
|
break;
|
|
case CompMatchOpTypeExp:
|
|
(*display->matchInitExp) (display, &op->exp.e, op->exp.value);
|
|
break;
|
|
}
|
|
|
|
op++;
|
|
}
|
|
}
|
|
|
|
void
|
|
matchUpdate (CompDisplay *display,
|
|
CompMatch *match)
|
|
{
|
|
matchReset (match);
|
|
matchUpdateOps (display, match->op, match->nOp);
|
|
match->display = display;
|
|
}
|
|
|
|
static Bool
|
|
matchEvalOps (CompDisplay *display,
|
|
CompMatchOp *op,
|
|
int nOp,
|
|
CompWindow *window)
|
|
{
|
|
Bool value, result = FALSE;
|
|
|
|
while (nOp--)
|
|
{
|
|
/* fast evaluation */
|
|
if (op->any.flags & MATCH_OP_AND_MASK)
|
|
{
|
|
/* result will never be true */
|
|
if (!result)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* result will always be true */
|
|
if (result)
|
|
return TRUE;
|
|
}
|
|
|
|
switch (op->type) {
|
|
case CompMatchOpTypeGroup:
|
|
value = matchEvalOps (display, op->group.op, op->group.nOp, window);
|
|
break;
|
|
case CompMatchOpTypeExp:
|
|
default:
|
|
value = (*op->exp.e.eval) (display, window, op->exp.e.priv);
|
|
break;
|
|
}
|
|
|
|
if (op->any.flags & MATCH_OP_NOT_MASK)
|
|
value = !value;
|
|
|
|
if (op->any.flags & MATCH_OP_AND_MASK)
|
|
result = (result && value);
|
|
else
|
|
result = (result || value);
|
|
|
|
op++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Bool
|
|
matchEval (CompMatch *match,
|
|
CompWindow *window)
|
|
{
|
|
if (match->display)
|
|
return matchEvalOps (match->display, match->op, match->nOp, window);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static Bool
|
|
matchEvalTypeExp (CompDisplay *display,
|
|
CompWindow *window,
|
|
CompPrivate private)
|
|
{
|
|
return (private.uval & window->wmType);
|
|
}
|
|
|
|
static Bool
|
|
matchEvalStateExp (CompDisplay *display,
|
|
CompWindow *window,
|
|
CompPrivate private)
|
|
{
|
|
return (private.uval & window->state);
|
|
}
|
|
|
|
static Bool
|
|
matchEvalIdExp (CompDisplay *display,
|
|
CompWindow *window,
|
|
CompPrivate private)
|
|
{
|
|
return (private.val == window->id);
|
|
}
|
|
|
|
static Bool
|
|
matchEvalOverrideRedirectExp (CompDisplay *display,
|
|
CompWindow *window,
|
|
CompPrivate private)
|
|
{
|
|
Bool overrideRedirect = window->attrib.override_redirect;
|
|
return ((private.val == 1 && overrideRedirect) ||
|
|
(private.val == 0 && !overrideRedirect));
|
|
}
|
|
|
|
static Bool
|
|
matchEvalAlphaExp (CompDisplay *display,
|
|
CompWindow *window,
|
|
CompPrivate private)
|
|
{
|
|
return ((private.val && window->alpha) ||
|
|
(!private.val && !window->alpha));
|
|
}
|
|
|
|
void
|
|
matchInitExp (CompDisplay *display,
|
|
CompMatchExp *exp,
|
|
const char *value)
|
|
{
|
|
if (strncmp (value, "xid=", 4) == 0)
|
|
{
|
|
exp->eval = matchEvalIdExp;
|
|
exp->priv.val = strtol (value + 4, NULL, 0);
|
|
}
|
|
else if (strncmp (value, "state=", 6) == 0)
|
|
{
|
|
exp->eval = matchEvalStateExp;
|
|
exp->priv.uval = windowStateFromString (value + 6);
|
|
}
|
|
else if (strncmp (value, "override_redirect=", 18) == 0)
|
|
{
|
|
exp->eval = matchEvalOverrideRedirectExp;
|
|
exp->priv.val = strtol (value + 18, NULL, 0);
|
|
}
|
|
else if (strncmp (value, "rgba=", 5) == 0)
|
|
{
|
|
exp->eval = matchEvalAlphaExp;
|
|
exp->priv.val = strtol (value + 5, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
if (strncmp (value, "type=", 5) == 0)
|
|
value += 5;
|
|
|
|
exp->eval = matchEvalTypeExp;
|
|
exp->priv.uval = windowTypeFromString (value);
|
|
}
|
|
}
|
|
|
|
static void
|
|
matchUpdateMatchOptions (CompOption *option,
|
|
int nOption)
|
|
{
|
|
while (nOption--)
|
|
{
|
|
switch (option->type) {
|
|
case CompOptionTypeMatch:
|
|
if (option->value.match.display)
|
|
matchUpdate (option->value.match.display, &option->value.match);
|
|
break;
|
|
case CompOptionTypeList:
|
|
if (option->value.list.type == CompOptionTypeMatch)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < option->value.list.nValue; i++)
|
|
if (option->value.list.value[i].match.display)
|
|
matchUpdate (option->value.list.value[i].match.display,
|
|
&option->value.list.value[i].match);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
option++;
|
|
}
|
|
}
|
|
|
|
void
|
|
matchExpHandlerChanged (CompDisplay *display)
|
|
{
|
|
CompOption *option;
|
|
int nOption;
|
|
CompPlugin *p;
|
|
CompScreen *s;
|
|
|
|
for (p = getPlugins (); p; p = p->next)
|
|
{
|
|
if (!p->vTable->getObjectOptions)
|
|
continue;
|
|
|
|
option = (*p->vTable->getObjectOptions) (p, &display->base, &nOption);
|
|
matchUpdateMatchOptions (option, nOption);
|
|
}
|
|
|
|
for (s = display->screens; s; s = s->next)
|
|
{
|
|
for (p = getPlugins (); p; p = p->next)
|
|
{
|
|
if (!p->vTable->getObjectOptions)
|
|
continue;
|
|
|
|
option = (*p->vTable->getObjectOptions) (p, &s->base, &nOption);
|
|
matchUpdateMatchOptions (option, nOption);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
matchPropertyChanged (CompDisplay *display,
|
|
CompWindow *w)
|
|
{
|
|
}
|