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.

1797 lines
39 KiB

/*
* Copyright © 2005 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>
*/
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <compiz-core.h>
#include <decoration.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
static CompMetadata decorMetadata;
typedef struct _Vector {
int dx;
int dy;
int x0;
int y0;
} Vector;
#define DECOR_BARE 0
#define DECOR_NORMAL 1
#define DECOR_ACTIVE 2
#define DECOR_NUM 3
typedef struct _DecorTexture {
struct _DecorTexture *next;
int refCount;
Pixmap pixmap;
Damage damage;
CompTexture texture;
} DecorTexture;
typedef struct _Decoration {
int refCount;
DecorTexture *texture;
CompWindowExtents output;
CompWindowExtents input;
CompWindowExtents maxInput;
int minWidth;
int minHeight;
decor_quad_t *quad;
int nQuad;
} Decoration;
typedef struct _ScaledQuad {
CompMatrix matrix;
BoxRec box;
float sx;
float sy;
} ScaledQuad;
typedef struct _WindowDecoration {
Decoration *decor;
ScaledQuad *quad;
int nQuad;
} WindowDecoration;
static int corePrivateIndex;
typedef struct _DecorCore {
ObjectAddProc objectAdd;
ObjectRemoveProc objectRemove;
} DecorCore;
#define DECOR_DISPLAY_OPTION_SHADOW_RADIUS 0
#define DECOR_DISPLAY_OPTION_SHADOW_OPACITY 1
#define DECOR_DISPLAY_OPTION_SHADOW_COLOR 2
#define DECOR_DISPLAY_OPTION_SHADOW_OFFSET_X 3
#define DECOR_DISPLAY_OPTION_SHADOW_OFFSET_Y 4
#define DECOR_DISPLAY_OPTION_COMMAND 5
#define DECOR_DISPLAY_OPTION_MIPMAP 6
#define DECOR_DISPLAY_OPTION_DECOR_MATCH 7
#define DECOR_DISPLAY_OPTION_SHADOW_MATCH 8
#define DECOR_DISPLAY_OPTION_NUM 9
static int displayPrivateIndex;
typedef struct _DecorDisplay {
int screenPrivateIndex;
HandleEventProc handleEvent;
MatchPropertyChangedProc matchPropertyChanged;
DecorTexture *textures;
Atom supportingDmCheckAtom;
Atom winDecorAtom;
Atom requestFrameExtentsAtom;
Atom decorAtom[DECOR_NUM];
CompOption opt[DECOR_DISPLAY_OPTION_NUM];
} DecorDisplay;
typedef struct _DecorScreen {
int windowPrivateIndex;
Window dmWin;
Decoration *decor[DECOR_NUM];
DrawWindowProc drawWindow;
DamageWindowRectProc damageWindowRect;
GetOutputExtentsForWindowProc getOutputExtentsForWindow;
AddSupportedAtomsProc addSupportedAtoms;
WindowMoveNotifyProc windowMoveNotify;
WindowResizeNotifyProc windowResizeNotify;
WindowStateChangeNotifyProc windowStateChangeNotify;
CompTimeoutHandle decoratorStartHandle;
} DecorScreen;
typedef struct _DecorWindow {
WindowDecoration *wd;
Decoration *decor;
CompTimeoutHandle resizeUpdateHandle;
} DecorWindow;
#define GET_DECOR_CORE(c) \
((DecorCore *) (c)->base.privates[corePrivateIndex].ptr)
#define DECOR_CORE(c) \
DecorCore *dc = GET_DECOR_CORE (c)
#define GET_DECOR_DISPLAY(d) \
((DecorDisplay *) (d)->base.privates[displayPrivateIndex].ptr)
#define DECOR_DISPLAY(d) \
DecorDisplay *dd = GET_DECOR_DISPLAY (d)
#define GET_DECOR_SCREEN(s, dd) \
((DecorScreen *) (s)->base.privates[(dd)->screenPrivateIndex].ptr)
#define DECOR_SCREEN(s) \
DecorScreen *ds = GET_DECOR_SCREEN (s, GET_DECOR_DISPLAY (s->display))
#define GET_DECOR_WINDOW(w, ds) \
((DecorWindow *) (w)->base.privates[(ds)->windowPrivateIndex].ptr)
#define DECOR_WINDOW(w) \
DecorWindow *dw = GET_DECOR_WINDOW (w, \
GET_DECOR_SCREEN (w->screen, \
GET_DECOR_DISPLAY (w->screen->display)))
#define NUM_OPTIONS(d) (sizeof ((d)->opt) / sizeof (CompOption))
static Bool
decorDrawWindow (CompWindow *w,
const CompTransform *transform,
const FragmentAttrib *attrib,
Region region,
unsigned int mask)
{
Bool status;
DECOR_SCREEN (w->screen);
DECOR_WINDOW (w);
UNWRAP (ds, w->screen, drawWindow);
status = (*w->screen->drawWindow) (w, transform, attrib, region, mask);
WRAP (ds, w->screen, drawWindow, decorDrawWindow);
if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
region = &infiniteRegion;
if (dw->wd && region->numRects)
{
WindowDecoration *wd = dw->wd;
REGION box;
int i;
mask |= PAINT_WINDOW_BLEND_MASK;
box.rects = &box.extents;
box.numRects = 1;
w->vCount = w->indexCount = 0;
for (i = 0; i < wd->nQuad; i++)
{
box.extents = wd->quad[i].box;
if (box.extents.x1 < box.extents.x2 &&
box.extents.y1 < box.extents.y2)
{
(*w->screen->addWindowGeometry) (w,
&wd->quad[i].matrix, 1,
&box,
region);
}
}
if (w->vCount)
(*w->screen->drawWindowTexture) (w,
&wd->decor->texture->texture,
attrib, mask);
}
return status;
}
static DecorTexture *
decorGetTexture (CompScreen *screen,
Pixmap pixmap)
{
DecorTexture *texture;
unsigned int width, height, depth, ui;
Window root;
int i;
DECOR_DISPLAY (screen->display);
for (texture = dd->textures; texture; texture = texture->next)
{
if (texture->pixmap == pixmap)
{
texture->refCount++;
return texture;
}
}
texture = malloc (sizeof (DecorTexture));
if (!texture)
return NULL;
initTexture (screen, &texture->texture);
if (!XGetGeometry (screen->display->display, pixmap, &root,
&i, &i, &width, &height, &ui, &depth))
{
finiTexture (screen, &texture->texture);
free (texture);
return NULL;
}
if (!bindPixmapToTexture (screen, &texture->texture, pixmap,
width, height, depth))
{
finiTexture (screen, &texture->texture);
free (texture);
return NULL;
}
if (!dd->opt[DECOR_DISPLAY_OPTION_MIPMAP].value.b)
texture->texture.mipmap = FALSE;
texture->damage = XDamageCreate (screen->display->display, pixmap,
XDamageReportRawRectangles);
texture->refCount = 1;
texture->pixmap = pixmap;
texture->next = dd->textures;
dd->textures = texture;
return texture;
}
static void
decorReleaseTexture (CompScreen *screen,
DecorTexture *texture)
{
DECOR_DISPLAY (screen->display);
texture->refCount--;
if (texture->refCount)
return;
if (texture == dd->textures)
{
dd->textures = texture->next;
}
else
{
DecorTexture *t;
for (t = dd->textures; t; t = t->next)
{
if (t->next == texture)
{
t->next = texture->next;
break;
}
}
}
finiTexture (screen, &texture->texture);
free (texture);
}
static void
computeQuadBox (decor_quad_t *q,
int width,
int height,
int *return_x1,
int *return_y1,
int *return_x2,
int *return_y2,
float *return_sx,
float *return_sy)
{
int x1, y1, x2, y2;
float sx = 1.0f;
float sy = 1.0f;
decor_apply_gravity (q->p1.gravity, q->p1.x, q->p1.y, width, height,
&x1, &y1);
decor_apply_gravity (q->p2.gravity, q->p2.x, q->p2.y, width, height,
&x2, &y2);
if (q->clamp & CLAMP_HORZ)
{
if (x1 < 0)
x1 = 0;
if (x2 > width)
x2 = width;
}
if (q->clamp & CLAMP_VERT)
{
if (y1 < 0)
y1 = 0;
if (y2 > height)
y2 = height;
}
if (q->stretch & STRETCH_X)
{
sx = (float)q->max_width / ((float)(x2 - x1));
}
else if (q->max_width < x2 - x1)
{
if (q->align & ALIGN_RIGHT)
x1 = x2 - q->max_width;
else
x2 = x1 + q->max_width;
}
if (q->stretch & STRETCH_Y)
{
sy = (float)q->max_height / ((float)(y2 - y1));
}
else if (q->max_height < y2 - y1)
{
if (q->align & ALIGN_BOTTOM)
y1 = y2 - q->max_height;
else
y2 = y1 + q->max_height;
}
*return_x1 = x1;
*return_y1 = y1;
*return_x2 = x2;
*return_y2 = y2;
if (return_sx)
*return_sx = sx;
if (return_sy)
*return_sy = sy;
}
static Decoration *
decorCreateDecoration (CompScreen *screen,
Window id,
Atom decorAtom)
{
Decoration *decoration;
Atom actual;
int result, format;
unsigned long n, nleft;
unsigned char *data;
long *prop;
Pixmap pixmap;
decor_extents_t input;
decor_extents_t maxInput;
decor_quad_t *quad;
int nQuad;
int minWidth;
int minHeight;
int left, right, top, bottom;
int x1, y1, x2, y2;
result = XGetWindowProperty (screen->display->display, id,
decorAtom, 0L, 1024L, FALSE,
XA_INTEGER, &actual, &format,
&n, &nleft, &data);
if (result != Success || !data)
return NULL;
if (!n)
{
XFree (data);
return NULL;
}
prop = (long *) data;
if (decor_property_get_version (prop) != decor_version ())
{
compLogMessage ("decoration", CompLogLevelWarn,
"Property ignored because "
"version is %d and decoration plugin version is %d\n",
decor_property_get_version (prop), decor_version ());
XFree (data);
return NULL;
}
nQuad = (n - BASE_PROP_SIZE) / QUAD_PROP_SIZE;
quad = malloc (sizeof (decor_quad_t) * nQuad);
if (!quad)
{
XFree (data);
return NULL;
}
nQuad = decor_property_to_quads (prop,
n,
&pixmap,
&input,
&maxInput,
&minWidth,
&minHeight,
quad);
XFree (data);
if (!nQuad)
{
free (quad);
return NULL;
}
decoration = malloc (sizeof (Decoration));
if (!decoration)
{
free (quad);
return NULL;
}
decoration->texture = decorGetTexture (screen, pixmap);
if (!decoration->texture)
{
free (decoration);
free (quad);
return NULL;
}
decoration->minWidth = minWidth;
decoration->minHeight = minHeight;
decoration->quad = quad;
decoration->nQuad = nQuad;
left = 0;
right = minWidth;
top = 0;
bottom = minHeight;
while (nQuad--)
{
computeQuadBox (quad, minWidth, minHeight, &x1, &y1, &x2, &y2,
NULL, NULL);
if (x1 < left)
left = x1;
if (y1 < top)
top = y1;
if (x2 > right)
right = x2;
if (y2 > bottom)
bottom = y2;
quad++;
}
decoration->output.left = -left;
decoration->output.right = right - minWidth;
decoration->output.top = -top;
decoration->output.bottom = bottom - minHeight;
decoration->input.left = input.left;
decoration->input.right = input.right;
decoration->input.top = input.top;
decoration->input.bottom = input.bottom;
decoration->maxInput.left = maxInput.left;
decoration->maxInput.right = maxInput.right;
decoration->maxInput.top = maxInput.top;
decoration->maxInput.bottom = maxInput.bottom;
decoration->refCount = 1;
return decoration;
}
static void
decorReleaseDecoration (CompScreen *screen,
Decoration *decoration)
{
decoration->refCount--;
if (decoration->refCount)
return;
decorReleaseTexture (screen, decoration->texture);
free (decoration->quad);
free (decoration);
}
static void
decorWindowUpdateDecoration (CompWindow *w)
{
Decoration *decoration;
DECOR_DISPLAY (w->screen->display);
DECOR_WINDOW (w);
decoration = decorCreateDecoration (w->screen, w->id, dd->winDecorAtom);
if (dw->decor)
decorReleaseDecoration (w->screen, dw->decor);
dw->decor = decoration;
}
static WindowDecoration *
createWindowDecoration (Decoration *d)
{
WindowDecoration *wd;
wd = malloc (sizeof (WindowDecoration) +
sizeof (ScaledQuad) * d->nQuad);
if (!wd)
return NULL;
d->refCount++;
wd->decor = d;
wd->quad = (ScaledQuad *) (wd + 1);
wd->nQuad = d->nQuad;
return wd;
}
static void
destroyWindowDecoration (CompScreen *screen,
WindowDecoration *wd)
{
decorReleaseDecoration (screen, wd->decor);
free (wd);
}
static void
setDecorationMatrices (CompWindow *w)
{
WindowDecoration *wd;
int i;
float x0, y0;
decor_matrix_t a;
CompMatrix b;
DECOR_WINDOW (w);
wd = dw->wd;
if (!wd)
return;
for (i = 0; i < wd->nQuad; i++)
{
wd->quad[i].matrix = wd->decor->texture->texture.matrix;
x0 = wd->decor->quad[i].m.x0;
y0 = wd->decor->quad[i].m.y0;
a = wd->decor->quad[i].m;
b = wd->quad[i].matrix;
wd->quad[i].matrix.xx = a.xx * b.xx + a.yx * b.xy;
wd->quad[i].matrix.yx = a.xx * b.yx + a.yx * b.yy;
wd->quad[i].matrix.xy = a.xy * b.xx + a.yy * b.xy;
wd->quad[i].matrix.yy = a.xy * b.yx + a.yy * b.yy;
wd->quad[i].matrix.x0 = x0 * b.xx + y0 * b.xy + b.x0;
wd->quad[i].matrix.y0 = x0 * b.yx + y0 * b.yy + b.y0;
wd->quad[i].matrix.xx *= wd->quad[i].sx;
wd->quad[i].matrix.yx *= wd->quad[i].sx;
wd->quad[i].matrix.xy *= wd->quad[i].sy;
wd->quad[i].matrix.yy *= wd->quad[i].sy;
if (wd->decor->quad[i].align & ALIGN_RIGHT)
x0 = wd->quad[i].box.x2 - wd->quad[i].box.x1;
else
x0 = 0.0f;
if (wd->decor->quad[i].align & ALIGN_BOTTOM)
y0 = wd->quad[i].box.y2 - wd->quad[i].box.y1;
else
y0 = 0.0f;
wd->quad[i].matrix.x0 -=
x0 * wd->quad[i].matrix.xx +
y0 * wd->quad[i].matrix.xy;
wd->quad[i].matrix.y0 -=
y0 * wd->quad[i].matrix.yy +
x0 * wd->quad[i].matrix.yx;
wd->quad[i].matrix.x0 -=
wd->quad[i].box.x1 * wd->quad[i].matrix.xx +
wd->quad[i].box.y1 * wd->quad[i].matrix.xy;
wd->quad[i].matrix.y0 -=
wd->quad[i].box.y1 * wd->quad[i].matrix.yy +
wd->quad[i].box.x1 * wd->quad[i].matrix.yx;
}
}
static void
updateWindowDecorationScale (CompWindow *w)
{
WindowDecoration *wd;
int x1, y1, x2, y2;
float sx, sy;
int i;
DECOR_WINDOW (w);
wd = dw->wd;
if (!wd)
return;
for (i = 0; i < wd->nQuad; i++)
{
computeQuadBox (&wd->decor->quad[i], w->width, w->height,
&x1, &y1, &x2, &y2, &sx, &sy);
wd->quad[i].box.x1 = x1 + w->attrib.x;
wd->quad[i].box.y1 = y1 + w->attrib.y;
wd->quad[i].box.x2 = x2 + w->attrib.x;
wd->quad[i].box.y2 = y2 + w->attrib.y;
wd->quad[i].sx = sx;
wd->quad[i].sy = sy;
}
setDecorationMatrices (w);
}
static Bool
decorCheckSize (CompWindow *w,
Decoration *decor)
{
return (decor->minWidth <= w->width && decor->minHeight <= w->height);
}
static int
decorWindowShiftX (CompWindow *w)
{
switch (w->sizeHints.win_gravity) {
case WestGravity:
case NorthWestGravity:
case SouthWestGravity:
return w->input.left;
case EastGravity:
case NorthEastGravity:
case SouthEastGravity:
return -w->input.right;
}
return 0;
}
static int
decorWindowShiftY (CompWindow *w)
{
switch (w->sizeHints.win_gravity) {
case NorthGravity:
case NorthWestGravity:
case NorthEastGravity:
return w->input.top;
case SouthGravity:
case SouthWestGravity:
case SouthEastGravity:
return -w->input.bottom;
}
return 0;
}
static Bool
decorWindowUpdate (CompWindow *w,
Bool allowDecoration)
{
WindowDecoration *wd;
Decoration *old, *decor = NULL;
Bool decorate = FALSE;
CompMatch *match;
int moveDx, moveDy;
int oldShiftX = 0;
int oldShiftY = 0;
DECOR_DISPLAY (w->screen->display);
DECOR_SCREEN (w->screen);
DECOR_WINDOW (w);
wd = dw->wd;
old = (wd) ? wd->decor : NULL;
switch (w->type) {
case CompWindowTypeDialogMask:
case CompWindowTypeModalDialogMask:
case CompWindowTypeUtilMask:
case CompWindowTypeMenuMask:
case CompWindowTypeNormalMask:
if (w->mwmDecor & (MwmDecorAll | MwmDecorTitle))
decorate = TRUE;
default:
break;
}
if (w->wmType & (CompWindowTypeDockMask | CompWindowTypeDesktopMask))
decorate = FALSE;
if (w->attrib.override_redirect)
decorate = FALSE;
if (decorate)
{
match = &dd->opt[DECOR_DISPLAY_OPTION_DECOR_MATCH].value.match;
if (!matchEval (match, w))
decorate = FALSE;
}
if (decorate)
{
if (dw->decor && decorCheckSize (w, dw->decor))
{
decor = dw->decor;
}
else
{
if (w->id == w->screen->display->activeWindow)
decor = ds->decor[DECOR_ACTIVE];
else
decor = ds->decor[DECOR_NORMAL];
}
}
else
{
match = &dd->opt[DECOR_DISPLAY_OPTION_SHADOW_MATCH].value.match;
if (matchEval (match, w))
{
if (w->region->numRects == 1)
decor = ds->decor[DECOR_BARE];
if (decor)
{
if (!decorCheckSize (w, decor))
decor = NULL;
}
}
}
if (!ds->dmWin || !allowDecoration)
decor = NULL;
if (decor == old)
return FALSE;
damageWindowOutputExtents (w);
if (old)
{
oldShiftX = decorWindowShiftX (w);
oldShiftY = decorWindowShiftY (w);
destroyWindowDecoration (w->screen, wd);
}
if (decor)
{
dw->wd = createWindowDecoration (decor);
if (!dw->wd)
return FALSE;
if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
setWindowFrameExtents (w, &decor->maxInput);
else
setWindowFrameExtents (w, &decor->input);
moveDx = decorWindowShiftX (w) - oldShiftX;
moveDy = decorWindowShiftY (w) - oldShiftY;
updateWindowOutputExtents (w);
damageWindowOutputExtents (w);
updateWindowDecorationScale (w);
}
else
{
CompWindowExtents emptyInput;
memset (&emptyInput, 0, sizeof (emptyInput));
setWindowFrameExtents (w, &emptyInput);
dw->wd = NULL;
moveDx = -oldShiftX;
moveDy = -oldShiftY;
}
if (w->placed && !w->attrib.override_redirect && (moveDx || moveDy))
{
XWindowChanges xwc;
unsigned int mask = CWX | CWY;
xwc.x = w->serverX + moveDx;
xwc.y = w->serverY + moveDy;
if (w->state & CompWindowStateFullscreenMask)
mask &= ~(CWX | CWY);
if (w->state & CompWindowStateMaximizedHorzMask)
mask &= ~CWX;
if (w->state & CompWindowStateMaximizedVertMask)
mask &= ~CWY;
if (w->saveMask & CWX)
w->saveWc.x += moveDx;
if (w->saveMask & CWY)
w->saveWc.y += moveDy;
if (mask)
configureXWindow (w, mask, &xwc);
}
return TRUE;
}
static void
decorCheckForDmOnScreen (CompScreen *s,
Bool updateWindows)
{
CompDisplay *d = s->display;
Atom actual;
int result, format;
unsigned long n, left;
unsigned char *data;
Window dmWin = None;
DECOR_DISPLAY (s->display);
DECOR_SCREEN (s);
result = XGetWindowProperty (d->display, s->root,
dd->supportingDmCheckAtom, 0L, 1L, FALSE,
XA_WINDOW, &actual, &format,
&n, &left, &data);
if (result == Success && data)
{
if (n)
{
XWindowAttributes attr;
memcpy (&dmWin, data, sizeof (Window));
compCheckForError (d->display);
XGetWindowAttributes (d->display, dmWin, &attr);
if (compCheckForError (d->display))
dmWin = None;
}
XFree (data);
}
if (dmWin != ds->dmWin)
{
CompWindow *w;
int i;
if (dmWin)
{
for (i = 0; i < DECOR_NUM; i++)
ds->decor[i] =
decorCreateDecoration (s, s->root, dd->decorAtom[i]);
}
else
{
for (i = 0; i < DECOR_NUM; i++)
{
if (ds->decor[i])
{
decorReleaseDecoration (s, ds->decor[i]);
ds->decor[i] = 0;
}
}
for (w = s->windows; w; w = w->next)
{
DECOR_WINDOW (w);
if (dw->decor)
{
decorReleaseDecoration (s, dw->decor);
dw->decor = 0;
}
}
}
ds->dmWin = dmWin;
if (updateWindows)
{
for (w = s->windows; w; w = w->next)
decorWindowUpdate (w, TRUE);
}
}
}
static void
decorHandleEvent (CompDisplay *d,
XEvent *event)
{
Window activeWindow = d->activeWindow;
CompWindow *w;
DECOR_DISPLAY (d);
switch (event->type) {
case DestroyNotify:
w = findWindowAtDisplay (d, event->xdestroywindow.window);
if (w)
{
DECOR_SCREEN (w->screen);
if (w->id == ds->dmWin)
decorCheckForDmOnScreen (w->screen, TRUE);
}
break;
case MapRequest:
w = findWindowAtDisplay (d, event->xmaprequest.window);
if (w)
decorWindowUpdate (w, TRUE);
break;
case ClientMessage:
if (event->xclient.message_type == dd->requestFrameExtentsAtom)
{
w = findWindowAtDisplay (d, event->xclient.window);
if (w)
decorWindowUpdate (w, TRUE);
}
break;
default:
if (event->type == d->damageEvent + XDamageNotify)
{
XDamageNotifyEvent *de = (XDamageNotifyEvent *) event;
DecorTexture *t;
for (t = dd->textures; t; t = t->next)
{
if (t->pixmap == de->drawable)
{
DecorWindow *dw;
DecorScreen *ds;
CompScreen *s;
t->texture.oldMipmaps = TRUE;
for (s = d->screens; s; s = s->next)
{
ds = GET_DECOR_SCREEN (s, dd);
for (w = s->windows; w; w = w->next)
{
if (w->shaded || w->mapNum)
{
dw = GET_DECOR_WINDOW (w, ds);
if (dw->wd && dw->wd->decor->texture == t)
damageWindowOutputExtents (w);
}
}
}
return;
}
}
}
break;
}
UNWRAP (dd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP (dd, d, handleEvent, decorHandleEvent);
if (d->activeWindow != activeWindow)
{
w = findWindowAtDisplay (d, activeWindow);
if (w)
decorWindowUpdate (w, TRUE);
w = findWindowAtDisplay (d, d->activeWindow);
if (w)
decorWindowUpdate (w, TRUE);
}
switch (event->type) {
case PropertyNotify:
if (event->xproperty.atom == dd->winDecorAtom)
{
w = findWindowAtDisplay (d, event->xproperty.window);
if (w)
{
decorWindowUpdateDecoration (w);
decorWindowUpdate (w, TRUE);
}
}
else if (event->xproperty.atom == d->mwmHintsAtom)
{
w = findWindowAtDisplay (d, event->xproperty.window);
if (w)
decorWindowUpdate (w, TRUE);
}
else
{
CompScreen *s;
s = findScreenAtDisplay (d, event->xproperty.window);
if (s)
{
if (event->xproperty.atom == dd->supportingDmCheckAtom)
{
decorCheckForDmOnScreen (s, TRUE);
}
else
{
int i;
for (i = 0; i < DECOR_NUM; i++)
{
if (event->xproperty.atom == dd->decorAtom[i])
{
DECOR_SCREEN (s);
if (ds->decor[i])
decorReleaseDecoration (s, ds->decor[i]);
ds->decor[i] =
decorCreateDecoration (s, s->root,
dd->decorAtom[i]);
for (w = s->windows; w; w = w->next)
decorWindowUpdate (w, TRUE);
}
}
}
}
}
break;
default:
if (d->shapeExtension && event->type == d->shapeEvent + ShapeNotify)
{
w = findWindowAtDisplay (d, ((XShapeEvent *) event)->window);
if (w)
decorWindowUpdate (w, TRUE);
}
break;
}
}
static Bool
decorDamageWindowRect (CompWindow *w,
Bool initial,
BoxPtr rect)
{
Bool status;
DECOR_SCREEN (w->screen);
if (initial)
decorWindowUpdate (w, TRUE);
UNWRAP (ds, w->screen, damageWindowRect);
status = (*w->screen->damageWindowRect) (w, initial, rect);
WRAP (ds, w->screen, damageWindowRect, decorDamageWindowRect);
return status;
}
static unsigned int
decorAddSupportedAtoms (CompScreen *s,
Atom *atoms,
unsigned int size)
{
unsigned int count;
DECOR_DISPLAY (s->display);
DECOR_SCREEN (s);
UNWRAP (ds, s, addSupportedAtoms);
count = (*s->addSupportedAtoms) (s, atoms, size);
WRAP (ds, s, addSupportedAtoms, decorAddSupportedAtoms);
if (count < size)
atoms[count++] = dd->requestFrameExtentsAtom;
return count;
}
static void
decorGetOutputExtentsForWindow (CompWindow *w,
CompWindowExtents *output)
{
DECOR_SCREEN (w->screen);
DECOR_WINDOW (w);
UNWRAP (ds, w->screen, getOutputExtentsForWindow);
(*w->screen->getOutputExtentsForWindow) (w, output);
WRAP (ds, w->screen, getOutputExtentsForWindow,
decorGetOutputExtentsForWindow);
if (dw->wd)
{
CompWindowExtents *e = &dw->wd->decor->output;
if (e->left > output->left)
output->left = e->left;
if (e->right > output->right)
output->right = e->right;
if (e->top > output->top)
output->top = e->top;
if (e->bottom > output->bottom)
output->bottom = e->bottom;
}
}
static CompBool
decorStartDecorator (void *closure)
{
CompScreen *s = (CompScreen *) closure;
DECOR_DISPLAY (s->display);
DECOR_SCREEN (s);
ds->decoratorStartHandle = 0;
if (!ds->dmWin)
runCommand (s, dd->opt[DECOR_DISPLAY_OPTION_COMMAND].value.s);
return FALSE;
}
static CompOption *
decorGetDisplayOptions (CompPlugin *plugin,
CompDisplay *display,
int *count)
{
DECOR_DISPLAY (display);
*count = NUM_OPTIONS (dd);
return dd->opt;
}
static Bool
decorSetDisplayOption (CompPlugin *plugin,
CompDisplay *display,
const char *name,
CompOptionValue *value)
{
CompOption *o;
int index;
DECOR_DISPLAY (display);
o = compFindOption (dd->opt, NUM_OPTIONS (dd), name, &index);
if (!o)
return FALSE;
switch (index) {
case DECOR_DISPLAY_OPTION_COMMAND:
if (compSetStringOption (o, value))
{
CompScreen *s;
for (s = display->screens; s; s = s->next)
{
DECOR_SCREEN (s);
if (!ds->dmWin)
runCommand (s, o->value.s);
}
return TRUE;
}
break;
case DECOR_DISPLAY_OPTION_SHADOW_MATCH:
{
char *matchString;
/*
Make sure RGBA matching is always present and disable shadows
for RGBA windows by default if the user didn't specify an
RGBA match.
Reasoning for that is that shadows are desired for some RGBA
windows (e.g. rectangular windows that just happen to have an
RGBA colormap), while it's absolutely undesired for others
(especially shaped ones) ... by enforcing no shadows for RGBA
windows by default, we are flexible to user desires while still
making sure we don't show ugliness by default
*/
matchString = matchToString (&value->match);
if (matchString)
{
if (!strstr (matchString, "rgba="))
{
CompMatch rgbaMatch;
matchInit (&rgbaMatch);
matchAddFromString (&rgbaMatch, "rgba=0");
matchAddGroup (&value->match, MATCH_OP_AND_MASK,
&rgbaMatch);
matchFini (&rgbaMatch);
}
free (matchString);
}
}
/* fall-through intended */
case DECOR_DISPLAY_OPTION_DECOR_MATCH:
if (compSetMatchOption (o, value))
{
CompScreen *s;
CompWindow *w;
for (s = display->screens; s; s = s->next)
for (w = s->windows; w; w = w->next)
decorWindowUpdate (w, TRUE);
}
break;
default:
if (compSetOption (o, value))
return TRUE;
break;
}
return FALSE;
}
static void
decorWindowMoveNotify (CompWindow *w,
int dx,
int dy,
Bool immediate)
{
DECOR_SCREEN (w->screen);
DECOR_WINDOW (w);
if (dw->wd)
{
WindowDecoration *wd = dw->wd;
int i;
for (i = 0; i < wd->nQuad; i++)
{
wd->quad[i].box.x1 += dx;
wd->quad[i].box.y1 += dy;
wd->quad[i].box.x2 += dx;
wd->quad[i].box.y2 += dy;
}
setDecorationMatrices (w);
}
UNWRAP (ds, w->screen, windowMoveNotify);
(*w->screen->windowMoveNotify) (w, dx, dy, immediate);
WRAP (ds, w->screen, windowMoveNotify, decorWindowMoveNotify);
}
static Bool
decorResizeUpdateTimeout (void *closure)
{
CompWindow *w = (CompWindow *) closure;
DECOR_WINDOW (w);
decorWindowUpdate (w, TRUE);
dw->resizeUpdateHandle = 0;
return FALSE;
}
static void
decorWindowResizeNotify (CompWindow *w,
int dx,
int dy,
int dwidth,
int dheight)
{
DECOR_SCREEN (w->screen);
DECOR_WINDOW (w);
/* FIXME: we should not need a timer for calling decorWindowUpdate,
and only call updateWindowDecorationScale if decorWindowUpdate
returns FALSE. Unfortunately, decorWindowUpdate may call
updateWindowOutputExtents, which may call WindowResizeNotify. As
we never should call a wrapped function that's currently
processed, we need the timer for the moment. updateWindowOutputExtents
should be fixed so that it does not emit a resize notification. */
dw->resizeUpdateHandle = compAddTimeout (0, 0, decorResizeUpdateTimeout, w);
updateWindowDecorationScale (w);
UNWRAP (ds, w->screen, windowResizeNotify);
(*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight);
WRAP (ds, w->screen, windowResizeNotify, decorWindowResizeNotify);
}
static void
decorWindowStateChangeNotify (CompWindow *w,
unsigned int lastState)
{
DECOR_SCREEN (w->screen);
DECOR_WINDOW (w);
if (!decorWindowUpdate (w, TRUE))
{
if (dw->wd && dw->wd->decor)
{
if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
setWindowFrameExtents (w, &dw->wd->decor->maxInput);
else
setWindowFrameExtents (w, &dw->wd->decor->input);
}
}
UNWRAP (ds, w->screen, windowStateChangeNotify);
(*w->screen->windowStateChangeNotify) (w, lastState);
WRAP (ds, w->screen, windowStateChangeNotify, decorWindowStateChangeNotify);
}
static void
decorMatchPropertyChanged (CompDisplay *d,
CompWindow *w)
{
DECOR_DISPLAY (d);
decorWindowUpdate (w, TRUE);
UNWRAP (dd, d, matchPropertyChanged);
(*d->matchPropertyChanged) (d, w);
WRAP (dd, d, matchPropertyChanged, decorMatchPropertyChanged);
}
static void
decorWindowAdd (CompScreen *s,
CompWindow *w)
{
if (w->shaded || w->attrib.map_state == IsViewable)
decorWindowUpdate (w, TRUE);
}
static void
decorWindowRemove (CompScreen *s,
CompWindow *w)
{
if (!w->destroyed)
decorWindowUpdate (w, FALSE);
}
static void
decorObjectAdd (CompObject *parent,
CompObject *object)
{
static ObjectAddProc dispTab[] = {
(ObjectAddProc) 0, /* CoreAdd */
(ObjectAddProc) 0, /* DisplayAdd */
(ObjectAddProc) 0, /* ScreenAdd */
(ObjectAddProc) decorWindowAdd
};
DECOR_CORE (&core);
UNWRAP (dc, &core, objectAdd);
(*core.objectAdd) (parent, object);
WRAP (dc, &core, objectAdd, decorObjectAdd);
DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), (parent, object));
}
static void
decorObjectRemove (CompObject *parent,
CompObject *object)
{
static ObjectRemoveProc dispTab[] = {
(ObjectRemoveProc) 0, /* CoreRemove */
(ObjectRemoveProc) 0, /* DisplayRemove */
(ObjectRemoveProc) 0, /* ScreenRemove */
(ObjectRemoveProc) decorWindowRemove
};
DECOR_CORE (&core);
DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), (parent, object));
UNWRAP (dc, &core, objectRemove);
(*core.objectRemove) (parent, object);
WRAP (dc, &core, objectRemove, decorObjectRemove);
}
static Bool
decorInitCore (CompPlugin *p,
CompCore *c)
{
DecorCore *dc;
if (!checkPluginABI ("core", CORE_ABIVERSION))
return FALSE;
dc = malloc (sizeof (DecorCore));
if (!dc)
return FALSE;
displayPrivateIndex = allocateDisplayPrivateIndex ();
if (displayPrivateIndex < 0)
{
free (dc);
return FALSE;
}
WRAP (dc, c, objectAdd, decorObjectAdd);
WRAP (dc, c, objectRemove, decorObjectRemove);
c->base.privates[corePrivateIndex].ptr = dc;
return TRUE;
}
static void
decorFiniCore (CompPlugin *p,
CompCore *c)
{
DECOR_CORE (c);
freeDisplayPrivateIndex (displayPrivateIndex);
UNWRAP (dc, c, objectAdd);
UNWRAP (dc, c, objectRemove);
free (dc);
}
static const CompMetadataOptionInfo decorDisplayOptionInfo[] = {
{ "shadow_radius", "float", "<min>0.0</min><max>48.0</max>", 0, 0 },
{ "shadow_opacity", "float", "<min>0.0</min>", 0, 0 },
{ "shadow_color", "color", 0, 0, 0 },
{ "shadow_x_offset", "int", "<min>-16</min><max>16</max>", 0, 0 },
{ "shadow_y_offset", "int", "<min>-16</min><max>16</max>", 0, 0 },
{ "command", "string", 0, 0, 0 },
{ "mipmap", "bool", 0, 0, 0 },
{ "decoration_match", "match", 0, 0, 0 },
{ "shadow_match", "match", 0, 0, 0 }
};
static Bool
decorInitDisplay (CompPlugin *p,
CompDisplay *d)
{
DecorDisplay *dd;
dd = malloc (sizeof (DecorDisplay));
if (!dd)
return FALSE;
if (!compInitDisplayOptionsFromMetadata (d,
&decorMetadata,
decorDisplayOptionInfo,
dd->opt,
DECOR_DISPLAY_OPTION_NUM))
{
free (dd);
return FALSE;
}
dd->screenPrivateIndex = allocateScreenPrivateIndex (d);
if (dd->screenPrivateIndex < 0)
{
compFiniDisplayOptions (d, dd->opt, DECOR_DISPLAY_OPTION_NUM);
free (dd);
return FALSE;
}
dd->textures = 0;
dd->supportingDmCheckAtom =
XInternAtom (d->display, DECOR_SUPPORTING_DM_CHECK_ATOM_NAME, 0);
dd->winDecorAtom =
XInternAtom (d->display, DECOR_WINDOW_ATOM_NAME, 0);
dd->decorAtom[DECOR_BARE] =
XInternAtom (d->display, DECOR_BARE_ATOM_NAME, 0);
dd->decorAtom[DECOR_NORMAL] =
XInternAtom (d->display, DECOR_NORMAL_ATOM_NAME, 0);
dd->decorAtom[DECOR_ACTIVE] =
XInternAtom (d->display, DECOR_ACTIVE_ATOM_NAME, 0);
dd->requestFrameExtentsAtom =
XInternAtom (d->display, "_NET_REQUEST_FRAME_EXTENTS", 0);
WRAP (dd, d, handleEvent, decorHandleEvent);
WRAP (dd, d, matchPropertyChanged, decorMatchPropertyChanged);
d->base.privates[displayPrivateIndex].ptr = dd;
return TRUE;
}
static void
decorFiniDisplay (CompPlugin *p,
CompDisplay *d)
{
DECOR_DISPLAY (d);
freeScreenPrivateIndex (d, dd->screenPrivateIndex);
UNWRAP (dd, d, handleEvent);
UNWRAP (dd, d, matchPropertyChanged);
compFiniDisplayOptions (d, dd->opt, DECOR_DISPLAY_OPTION_NUM);
free (dd);
}
static Bool
decorInitScreen (CompPlugin *p,
CompScreen *s)
{
DecorScreen *ds;
DECOR_DISPLAY (s->display);
ds = malloc (sizeof (DecorScreen));
if (!ds)
return FALSE;
ds->windowPrivateIndex = allocateWindowPrivateIndex (s);
if (ds->windowPrivateIndex < 0)
{
free (ds);
return FALSE;
}
memset (ds->decor, 0, sizeof (ds->decor));
ds->dmWin = None;
ds->decoratorStartHandle = 0;
WRAP (ds, s, drawWindow, decorDrawWindow);
WRAP (ds, s, damageWindowRect, decorDamageWindowRect);
WRAP (ds, s, getOutputExtentsForWindow, decorGetOutputExtentsForWindow);
WRAP (ds, s, windowMoveNotify, decorWindowMoveNotify);
WRAP (ds, s, windowResizeNotify, decorWindowResizeNotify);
WRAP (ds, s, windowStateChangeNotify, decorWindowStateChangeNotify);
WRAP (ds, s, addSupportedAtoms, decorAddSupportedAtoms);
s->base.privates[dd->screenPrivateIndex].ptr = ds;
decorCheckForDmOnScreen (s, FALSE);
setSupportedWmHints (s);
if (!ds->dmWin)
ds->decoratorStartHandle = compAddTimeout (0, -1,
decorStartDecorator, s);
return TRUE;
}
static void
decorFiniScreen (CompPlugin *p,
CompScreen *s)
{
int i;
DECOR_SCREEN (s);
for (i = 0; i < DECOR_NUM; i++)
if (ds->decor[i])
decorReleaseDecoration (s, ds->decor[i]);
if (ds->decoratorStartHandle)
compRemoveTimeout (ds->decoratorStartHandle);
freeWindowPrivateIndex (s, ds->windowPrivateIndex);
UNWRAP (ds, s, drawWindow);
UNWRAP (ds, s, damageWindowRect);
UNWRAP (ds, s, getOutputExtentsForWindow);
UNWRAP (ds, s, windowMoveNotify);
UNWRAP (ds, s, windowResizeNotify);
UNWRAP (ds, s, windowStateChangeNotify);
UNWRAP (ds, s, addSupportedAtoms);
setSupportedWmHints (s);
free (ds);
}
static Bool
decorInitWindow (CompPlugin *p,
CompWindow *w)
{
DecorWindow *dw;
DECOR_SCREEN (w->screen);
dw = malloc (sizeof (DecorWindow));
if (!dw)
return FALSE;
dw->wd = NULL;
dw->decor = NULL;
dw->resizeUpdateHandle = 0;
w->base.privates[ds->windowPrivateIndex].ptr = dw;
if (!w->attrib.override_redirect)
decorWindowUpdateDecoration (w);
if (w->base.parent)
decorWindowAdd (w->screen, w);
return TRUE;
}
static void
decorFiniWindow (CompPlugin *p,
CompWindow *w)
{
DECOR_WINDOW (w);
if (dw->resizeUpdateHandle)
compRemoveTimeout (dw->resizeUpdateHandle);
if (w->base.parent)
decorWindowRemove (w->screen, w);
if (dw->wd)
destroyWindowDecoration (w->screen, dw->wd);
if (dw->decor)
decorReleaseDecoration (w->screen, dw->decor);
free (dw);
}
static CompBool
decorInitObject (CompPlugin *p,
CompObject *o)
{
static InitPluginObjectProc dispTab[] = {
(InitPluginObjectProc) decorInitCore,
(InitPluginObjectProc) decorInitDisplay,
(InitPluginObjectProc) decorInitScreen,
(InitPluginObjectProc) decorInitWindow
};
RETURN_DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), TRUE, (p, o));
}
static void
decorFiniObject (CompPlugin *p,
CompObject *o)
{
static FiniPluginObjectProc dispTab[] = {
(FiniPluginObjectProc) decorFiniCore,
(FiniPluginObjectProc) decorFiniDisplay,
(FiniPluginObjectProc) decorFiniScreen,
(FiniPluginObjectProc) decorFiniWindow
};
DISPATCH (o, dispTab, ARRAY_SIZE (dispTab), (p, o));
}
static CompOption *
decorGetObjectOptions (CompPlugin *plugin,
CompObject *object,
int *count)
{
static GetPluginObjectOptionsProc dispTab[] = {
(GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
(GetPluginObjectOptionsProc) decorGetDisplayOptions
};
*count = 0;
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
(void *) count, (plugin, object, count));
}
static CompBool
decorSetObjectOption (CompPlugin *plugin,
CompObject *object,
const char *name,
CompOptionValue *value)
{
static SetPluginObjectOptionProc dispTab[] = {
(SetPluginObjectOptionProc) 0, /* SetCoreOption */
(SetPluginObjectOptionProc) decorSetDisplayOption
};
RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
(plugin, object, name, value));
}
static Bool
decorInit (CompPlugin *p)
{
if (!compInitPluginMetadataFromInfo (&decorMetadata,
p->vTable->name,
decorDisplayOptionInfo,
DECOR_DISPLAY_OPTION_NUM,
0, 0))
return FALSE;
corePrivateIndex = allocateCorePrivateIndex ();
if (corePrivateIndex < 0)
{
compFiniMetadata (&decorMetadata);
return FALSE;
}
compAddMetadataFromFile (&decorMetadata, p->vTable->name);
return TRUE;
}
static void
decorFini (CompPlugin *p)
{
freeCorePrivateIndex (corePrivateIndex);
compFiniMetadata (&decorMetadata);
}
static CompMetadata *
decorGetMetadata (CompPlugin *plugin)
{
return &decorMetadata;
}
static CompPluginVTable decorVTable = {
"decoration",
decorGetMetadata,
decorInit,
decorFini,
decorInitObject,
decorFiniObject,
decorGetObjectOptions,
decorSetObjectOption
};
CompPluginVTable *
getCompPluginInfo20070830 (void)
{
return &decorVTable;
}