/*
* Copyright © 2005 Novell, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Author: David Reveman <davidr@novell.com>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <X11/Xatom.h>
#include <X11/extensions/shape.h>
#include <beryl.h>
#include <beryl-decoration.h>
#define BERYL_ADVANCED_DECORATION_VERSION 10121980
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;
long flags;
decor_quad_t *quad;
int nQuad;
} Decoration;
typedef struct _ScaledQuad
{
CompMatrix matrix;
double sx, sy; // stretch scales
BoxRec box;
} ScaledQuad;
typedef struct _WindowDecoration
{
Decoration *decor;
ScaledQuad *quad;
int nQuad;
} WindowDecoration;
#define DECOR_APPLY_PAINT_MODIFIERS_DEFAULT TRUE
#define DECOR_SHADOWS_ON_DOCKS_DEFAULT FALSE
#define DECOR_MIPMAP_DEFAULT FALSE
#define DECOR_DISPLAY_OPTION_APPLY_PAINT_MODIFIERS 0
#define DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS 1
#define DECOR_DISPLAY_OPTION_COMMAND 2
#define DECOR_DISPLAY_OPTION_MIPMAP 3
#define DECOR_DISPLAY_OPTION_NUM 4
static int displayPrivateIndex;
typedef struct _DecorDisplay
{
int screenPrivateIndex;
HandleEventProc handleEvent;
DecorTexture *textures;
Atom supportingDmCheckAtom;
Atom winDecorAtom;
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;
WindowMoveNotifyProc windowMoveNotify;
WindowResizeNotifyProc windowResizeNotify;
WindowStateChangeNotifyProc windowStateChangeNotify;
} DecorScreen;
typedef struct _DecorWindow
{
WindowDecoration *wd;
Decoration *decor;
Bool needsUpdate;
} DecorWindow;
#define GET_DECOR_DISPLAY(d) \
((DecorDisplay *) (d)->privates[displayPrivateIndex].ptr)
#define DECOR_DISPLAY(d) \
DecorDisplay *dd = GET_DECOR_DISPLAY (d)
#define GET_DECOR_SCREEN(s, dd) \
((DecorScreen *) (s)->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)->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 void decorDisplayInitOptions(DecorDisplay * dd)
{
CompOption *o;
o = &dd->opt[DECOR_DISPLAY_OPTION_APPLY_PAINT_MODIFIERS];
o->advanced = False;
o->name = "apply_paint_modifiers";
o->group = N_("Misc. options");
o->subGroup = N_("Appearance");
o->displayHints = "";
o->shortDesc = N_("Apply Transparency/Brightness/Saturation");
o->longDesc =
N_
("Apply the transparency/brightness/saturation of the Window to the Decoration.");
o->type = CompOptionTypeBool;
o->value.b = DECOR_APPLY_PAINT_MODIFIERS_DEFAULT;
o = &dd->opt[DECOR_DISPLAY_OPTION_COMMAND];
o->advanced = False;
o->name = "command";
o->group = N_("Misc. options");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Command Line");
o->longDesc = N_("Decorator Command Line that is executed if no "
"decorator is already running.");
o->type = CompOptionTypeString;
o->value.s = strdup("");
o->rest.s.string = NULL;
o->rest.s.nString = 0;
o = &dd->opt[DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS];
o->advanced = False;
o->name = "shadows_on_docks";
o->group = N_("Misc. options");
o->subGroup = N_("Appearance");
o->displayHints = "";
o->shortDesc = N_("Draw Shadows on Panel Type Windows");
o->longDesc = N_("Draw basic Shadow Decoration on panel windows.");
o->type = CompOptionTypeBool;
o->value.b = DECOR_SHADOWS_ON_DOCKS_DEFAULT;
/*o = &dd->opt[DECOR_DISPLAY_OPTION_DROP_SHADOWS];
o->advanced=False;
o->name = "drop_shadows";
o->group=N_("");
o->subGroup=N_("");
o->displayHints="";
o->shortDesc = N_("Draw simple shaped drop-shadows");
o->longDesc =
N_("Draw a simple shaped drop-shadow on windows, separate from "
"decoration manager.");
o->type = CompOptionTypeBool;
o->value.b = FALSE; */
o = &dd->opt[DECOR_DISPLAY_OPTION_MIPMAP];
o->advanced = False;
o->name = "mipmap";
o->group = N_("Main");
o->subGroup = N_("");
o->displayHints = "";
o->shortDesc = N_("Mipmap");
o->longDesc = N_("Allow Mipmaps to be generated for Decoration textures.");
o->type = CompOptionTypeBool;
o->value.b = DECOR_MIPMAP_DEFAULT;
}
static CompOption *decorGetDisplayOptions(CompDisplay * display, int *count)
{
if (display)
{
DECOR_DISPLAY(display);
*count = NUM_OPTIONS(dd);
return dd->opt;
}
else
{
DecorDisplay *dd = malloc(sizeof(DecorDisplay));
decorDisplayInitOptions(dd);
*count = NUM_OPTIONS(dd);
return dd->opt;
}
}
static Bool decorWindowUpdate(CompWindow * w, Bool move);
static Bool
decorSetDisplayOption(CompDisplay * display,
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_APPLY_PAINT_MODIFIERS:
//case DECOR_DISPLAY_OPTION_DROP_SHADOWS:
if (compSetBoolOption(o, value))
{
damageScreen(display->screens);
return TRUE;
}
break;
case DECOR_DISPLAY_OPTION_COMMAND:
if (compSetStringOption(o, value))
{
if (display->screens && *o->value.s != '\0')
{
DECOR_SCREEN(display->screens);
/* run decorator command if no decorator is present on
first screen */
if (!ds->dmWin)
{
if (fork() == 0)
{
putenv(display->displayString);
execl("/bin/sh", "/bin/sh", "-c", o->value.s, NULL);
exit(0);
}
}
}
return TRUE;
}
break;
case DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS:
if (compSetBoolOption(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);
return TRUE;
}
case DECOR_DISPLAY_OPTION_MIPMAP:
if (compSetBoolOption(o, value))
return TRUE;
default:
break;
}
return FALSE;
}
static Bool
decorDrawWindow(CompWindow * w,
const WindowPaintAttrib * attrib,
Region region, unsigned int mask)
{
Bool status;
DECOR_WINDOW(w);
if (dw->needsUpdate)
decorWindowUpdate(w, FALSE);
DECOR_SCREEN(w->screen);
DECOR_DISPLAY(w->screen->display);
if (!(mask & PAINT_WINDOW_SOLID_MASK))
{
DECOR_WINDOW(w);
if (mask & PAINT_WINDOW_TRANSFORMED_MASK)
region = getInfiniteRegion();
if (dw->wd && region->numRects)
{
WindowDecoration *wd = dw->wd;
WindowPaintAttrib sAttrib = *attrib;
REGION box;
int i;
box.rects = &box.extents;
box.numRects = 1;
w->vCount = 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 (!dd->opt[DECOR_DISPLAY_OPTION_APPLY_PAINT_MODIFIERS].value.b)
{
sAttrib.opacity = OPAQUE;
sAttrib.saturation = COLOR;
sAttrib.brightness = BRIGHT;
}
if (w->vCount)
{
(*w->screen->drawWindowTexture) (w,
&wd->decor->texture->texture,
&sAttrib,
mask |
PAINT_WINDOW_TRANSLUCENT_MASK
|
PAINT_WINDOW_DECORATION_MASK);
}
}
}
UNWRAP(ds, w->screen, drawWindow);
status = (*w->screen->drawWindow) (w, attrib, region, mask);
WRAP(ds, w->screen, drawWindow, decorDrawWindow);
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);
// we get damage events for decorations
if (texture->texture.mode == TEXTURE_MODE_COPY)
{
texture->texture.mode = TEXTURE_MODE_COPY_DAMAGE;
texture->texture.cmd.damaged = TRUE;
texture->texture.cmd.fullDamage = TRUE;
}
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, double *return_sx, double *return_sy)
{
int x1, y1, x2, y2;
double sx, sy;
sx = 1;
sy = 1;
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 (x2 - x1 > 0 && (q->clamp & CLAMP_VERT) &&
q->p1.x == 0 && q->p2.x == 0)
sx = (double)q->max_width / ((double)x2 - (double)x1);
if ((q->p1.x == 0 && q->p2.x == 0) || !(q->clamp & CLAMP_VERT))
{
if (x1 < 0)
x1 = 0;
if (x2 > width)
x2 = width;
}
}
if (q->clamp & CLAMP_VERT)
{
if (y2 - y1 > 0 && (q->clamp & CLAMP_HORZ) &&
q->p1.y == 0 && q->p2.y == 0)
sy = (double)q->max_height / ((double)y2 - (double)y1);
if ((q->p1.y == 0 && q->p2.y == 0) || !(q->clamp & CLAMP_HORZ))
{
if (y1 < 0)
y1 = 0;
if (y2 > height)
y2 = height;
}
}
if (q->max_width < x2 - x1
&& !(q->clamp & CLAMP_VERT && q->clamp & CLAMP_HORZ))
{
if (q->align & ALIGN_RIGHT)
x1 = x2 - q->max_width;
else
x2 = x1 + q->max_width;
}
if (q->max_height < y2 - y1
&& !(q->clamp & CLAMP_VERT && q->clamp & CLAMP_HORZ))
{
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, minHeight;
int left, right, top, bottom;
int x1, y1, x2, y2;
double sx, sy;
result = XGetWindowProperty(screen->display->display, id,
decorAtom, 0L, 1024L, FALSE,
XA_INTEGER, &actual, &format,
&n, &nleft, &data);
if (result != Success || !n || !data)
return NULL;
prop = (long*) data;
if (decor_property_get_version(prop) != decor_version())
{
fprintf (stderr, "%s: decoration: property ignored because "
"version is %d and decoration plugin version is %d\n",
getProgramName(), 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 = decoration->minWidth;
top = 0;
bottom = decoration->minHeight;
while (nQuad--)
{
computeQuadBox(quad, minWidth, minHeight, &x1, &y1, &x2, &y2, &sx, &sy);
if (x1 < left)
left = x1;
if (y1 < top)
top = y1;
if (x2 > right)
right = x2;
if (y2 > bottom)
bottom = y2;
quad++;
}
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->output.left = -left;
decoration->output.right = right - decoration->minWidth;
decoration->output.top = -top;
decoration->output.bottom = bottom - decoration->minHeight;
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);
dw->needsUpdate = False;
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_WINDOW(w);
wd = dw->wd;
if (!wd)
return;
for (i = 0; i < wd->nQuad; i++)
{
decor_matrix_t a;
CompMatrix b;
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;
if (wd->decor->quad[i].clamp & CLAMP_VERT
&& wd->decor->quad[i].clamp & CLAMP_HORZ)
{
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;
double 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 Bool decorWindowUpdate(CompWindow * w, Bool move)
{
WindowDecoration *wd;
Decoration *old, *decor = NULL;
DECOR_SCREEN(w->screen);
DECOR_WINDOW(w);
DECOR_DISPLAY(w->screen->display);
wd = dw->wd;
old = (wd) ? wd->decor : NULL;
if (dw->decor && decorCheckSize(w, dw->decor))
{
if (w->type != CompWindowTypeFullscreenMask)
decor = dw->decor;
}
else
{
if (w->attrib.override_redirect)
{
if (w->region->numRects == 1 && !w->alpha)
decor = ds->decor[DECOR_BARE];
}
else
{
switch (w->type)
{
case CompWindowTypeDialogMask:
case CompWindowTypeModalDialogMask:
case CompWindowTypeUtilMask:
case CompWindowTypeNormalMask:
if (w->mwmDecor & (MwmDecorAll | MwmDecorTitle | MwmDecorBorder))
{
if (w->id == w->screen->display->activeWindow)
decor = ds->decor[DECOR_ACTIVE];
else
decor = ds->decor[DECOR_NORMAL];
break;
}
/* fall-through */
default:
if (w->region->numRects == 1 && !w->alpha)
decor = ds->decor[DECOR_BARE];
/* no decoration on windows with below state */
if (w->state & CompWindowStateBelowMask)
decor = NULL;
if (w->type == CompWindowTypeDockMask &&
!dd->opt[DECOR_DISPLAY_OPTION_SHADOWS_ON_DOCKS].value.b)
decor = NULL;
if (w->type & CompWindowTypeDesktopMask)
decor = NULL;
break;
}
}
if (decor)
{
if (!decorCheckSize(w, decor))
decor = NULL;
}
}
if (!ds->dmWin)
decor = NULL;
if (decor == old)
return FALSE;
damageWindowOutputExtents(w);
if (old)
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);
updateWindowOutputExtents(w);
damageWindowOutputExtents(w);
updateWindowDecorationScale(w);
}
else
{
dw->wd = NULL;
}
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 && n && data)
{
XWindowAttributes attr;
memcpy(&dmWin, data, sizeof(Window));
XFree(data);
compCheckForError(d->display);
XGetWindowAttributes(d->display, dmWin, &attr);
if (compCheckForError(d->display))
dmWin = None;
}
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 = 0;
CompWindow *w;
DECOR_DISPLAY(d);
switch (event->type)
{
case PropertyNotify:
if (event->xproperty.atom == d->winActiveAtom)
activeWindow = d->activeWindow;
break;
case DestroyNotify:
w = findWindowAtDisplay(d, event->xdestroywindow.window);
if (w)
{
DECOR_SCREEN(w->screen);
if (w->id == ds->dmWin)
decorCheckForDmOnScreen(w->screen, TRUE);
}
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)
{
if (dw->wd->decor->texture->texture.
mode == TEXTURE_MODE_COPY_DAMAGE)
{
dw->wd->decor->texture->texture.cmd.
damaged = TRUE;
dw->wd->decor->texture->texture.cmd.
fullDamage = TRUE;
}
damageWindowOutputExtents(w);
}
}
}
}
return;
}
}
}
break;
}
UNWRAP(dd, d, handleEvent);
(*d->handleEvent) (d, event);
WRAP(dd, d, handleEvent, decorHandleEvent);
switch (event->type)
{
case PropertyNotify:
if (event->xproperty.atom == d->winActiveAtom)
{
if (d->activeWindow != activeWindow)
{
w = findWindowAtDisplay(d, activeWindow);
if (w)
decorWindowUpdate(w, FALSE);
w = findWindowAtDisplay(d, d->activeWindow);
if (w)
decorWindowUpdate(w, FALSE);
}
}
else 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;
case MapRequest:
w = findWindowAtDisplay(d, event->xmaprequest.window);
if (w)
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, FALSE);
UNWRAP(ds, w->screen, damageWindowRect);
status = (*w->screen->damageWindowRect) (w, initial, rect);
WRAP(ds, w->screen, damageWindowRect, decorDamageWindowRect);
return status;
}
static void
decorGetOutputExtentsForWindow(CompWindow * w, CompWindowExtents * output)
{
DECOR_SCREEN(w->screen);
DECOR_WINDOW(w);
if (dw->needsUpdate)
decorWindowUpdate(w, FALSE);
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 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 void decorWindowResizeNotify(CompWindow * w, int dx, int dy,
int dwidth, int dheight, Bool preview)
{
DECOR_SCREEN(w->screen);
if ( !decorWindowUpdate(w, FALSE))
updateWindowDecorationScale(w);
UNWRAP(ds, w->screen, windowResizeNotify);
(*w->screen->windowResizeNotify) (w, dx, dy, dwidth, dheight, preview);
WRAP(ds, w->screen, windowResizeNotify, decorWindowResizeNotify);
}
static void decorWindowStateChangeNotify(CompWindow * w)
{
DECOR_SCREEN(w->screen);
DECOR_WINDOW(w);
if (dw->wd && dw->wd->decor)
{
Decoration *decor = dw->wd->decor;
if ((w->state & MAXIMIZE_STATE) == MAXIMIZE_STATE)
setWindowFrameExtents(w, &decor->maxInput);
else
setWindowFrameExtents(w, &decor->input);
}
UNWRAP(ds, w->screen, windowStateChangeNotify);
(*w->screen->windowStateChangeNotify) (w);
WRAP(ds, w->screen, windowStateChangeNotify,
decorWindowStateChangeNotify);
}
static Bool decorInitDisplay(CompPlugin * p, CompDisplay * d)
{
DecorDisplay *dd;
dd = malloc(sizeof(DecorDisplay));
if (!dd)
return FALSE;
dd->screenPrivateIndex = allocateScreenPrivateIndex(d);
if (dd->screenPrivateIndex < 0)
{
free(dd);
return FALSE;
}
dd->textures = 0;
dd->supportingDmCheckAtom =
XInternAtom(d->display, "_NET_SUPPORTING_DM_CHECK", 0);
dd->winDecorAtom = XInternAtom(d->display, "_NET_WINDOW_DECOR", 0);
dd->decorAtom[DECOR_BARE] =
XInternAtom(d->display, "_NET_WINDOW_DECOR_BARE", 0);
dd->decorAtom[DECOR_NORMAL] =
XInternAtom(d->display, "_NET_WINDOW_DECOR_NORMAL", 0);
dd->decorAtom[DECOR_ACTIVE] =
XInternAtom(d->display, "_NET_WINDOW_DECOR_ACTIVE", 0);
decorDisplayInitOptions(dd);
WRAP(dd, d, handleEvent, decorHandleEvent);
d->privates[displayPrivateIndex].ptr = dd;
return TRUE;
}
static void decorFiniDisplay(CompPlugin * p, CompDisplay * d)
{
DECOR_DISPLAY(d);
freeScreenPrivateIndex(d, dd->screenPrivateIndex);
UNWRAP(dd, d, handleEvent);
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;
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);
s->privates[dd->screenPrivateIndex].ptr = ds;
decorCheckForDmOnScreen(s, FALSE);
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]);
UNWRAP(ds, s, drawWindow);
UNWRAP(ds, s, damageWindowRect);
UNWRAP(ds, s, getOutputExtentsForWindow);
UNWRAP(ds, s, windowMoveNotify);
UNWRAP(ds, s, windowResizeNotify);
UNWRAP(ds, s, windowStateChangeNotify);
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;
w->privates[ds->windowPrivateIndex].ptr = dw;
dw->needsUpdate = False;
if (!w->attrib.override_redirect)
decorWindowUpdateDecoration(w);
if (w->shaded || w->attrib.map_state == IsViewable)
dw->needsUpdate = True;
return TRUE;
}
static void decorFiniWindow(CompPlugin * p, CompWindow * w)
{
DECOR_WINDOW(w);
if (dw->wd)
destroyWindowDecoration(w->screen, dw->wd);
if (dw->decor)
decorReleaseDecoration(w->screen, dw->decor);
free(dw);
}
static Bool decorInit(CompPlugin * p)
{
displayPrivateIndex = allocateDisplayPrivateIndex();
if (displayPrivateIndex < 0)
return FALSE;
return TRUE;
}
static void decorFini(CompPlugin * p)
{
if (displayPrivateIndex >= 0)
freeDisplayPrivateIndex(displayPrivateIndex);
}
static CompPluginVTable decorVTable = {
"decoration",
N_("Window Decoration"),
N_("Window decorations"),
decorInit,
decorFini,
decorInitDisplay,
decorFiniDisplay,
decorInitScreen,
decorFiniScreen,
decorInitWindow,
decorFiniWindow,
decorGetDisplayOptions,
decorSetDisplayOption,
0, /* GetScreenOptions */
0, /* SetScreenOption */
0,
0,
0,
0,
BERYL_ABI_INFO,
"beryl-plugins",
"effects",
0,
0,
True,
};
CompPluginVTable *getCompPluginInfo(void)
{
return &decorVTable;
}
syntax highlighted by Code2HTML, v. 0.9.1