/*
* Matchbox Window Manager - A lightweight window manager not for the
* desktop.
*
* Authored By Matthew Allum <mallum@o-hand.com>
*
* Copyright (c) 2002, 2004 OpenedHand Ltd - http://o-hand.com
*
* 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, 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.
*
*/
#include "composite-engine.h"
#define DO_TIMINGS 0 /* enable this for lowlight timings */
#if DO_TIMINGS
#include <sys/time.h>
#include <time.h>
#endif
#ifdef USE_COMPOSITE
#include <math.h>
static void
comp_engine_add_damage (Wm *w, XserverRegion damage);
typedef struct _conv {
int size;
double *data;
} conv;
typedef struct StackItem
{
Client *client;
struct StackItem *next;
} StackItem;
/* XXX Ideally get rid of these globals */
static conv *gussianMap;
static StackItem *comp_stack;
/* List for stack rendering of dialogs etc */
/* Shadow Generation */
static double
gaussian (double r, double x, double y)
{
return ((1 / (sqrt (2 * M_PI * r))) *
exp ((- (x * x + y * y)) / (2 * r * r)));
}
static conv *
make_gaussian_map (double r)
{
conv *c;
int size = ((int) ceil ((r * 3)) + 1) & ~1;
int center = size / 2;
int x, y;
double t = 0.0;
double g;
c = malloc (sizeof (conv) + size * size * sizeof (double));
c->size = size;
dbg("%s() map size is %i\n", __func__, size);
c->data = (double *) (c + 1);
for (y = 0; y < size; y++)
for (x = 0; x < size; x++)
{
g = gaussian (r, (double) (x - center), (double) (y - center));
t += g;
c->data[y * size + x] = g;
}
for (y = 0; y < size; y++)
for (x = 0; x < size; x++)
c->data[y*size + x] /= t;
return c;
}
static unsigned char
sum_gaussian (conv *map, double opacity, int x, int y, int width, int height)
{
int fx, fy;
double *g_data;
double *g_line = map->data;
int g_size = map->size;
int center = g_size / 2;
int fx_start, fx_end;
int fy_start, fy_end;
double v;
/*
* Compute set of filter values which are "in range",
* that's the set with:
* 0 <= x + (fx-center) && x + (fx-center) < width &&
* 0 <= y + (fy-center) && y + (fy-center) < height
*
* 0 <= x + (fx - center) x + fx - center < width
* center - x <= fx fx < width + center - x
*/
fx_start = center - x;
if (fx_start < 0)
fx_start = 0;
fx_end = width + center - x;
if (fx_end > g_size)
fx_end = g_size;
fy_start = center - y;
if (fy_start < 0)
fy_start = 0;
fy_end = height + center - y;
if (fy_end > g_size)
fy_end = g_size;
g_line = g_line + fy_start * g_size + fx_start;
v = 0;
for (fy = fy_start; fy < fy_end; fy++)
{
g_data = g_line;
g_line += g_size;
for (fx = fx_start; fx < fx_end; fx++)
v += *g_data++;
}
if (v > 1)
v = 1;
return ((unsigned int) (v * opacity * 255.0));
}
#define MAX_TILE_SZ 16 /* make sure size/2 < MAX_TILE_SZ */
#define WIDTH 320
#define HEIGHT 320
static void
shadow_setup_part (Wm *w,
XImage **ximage,
Picture *pic,
Pixmap *pxm,
int width,
int height)
{
*ximage = XCreateImage (w->dpy, DefaultVisual(w->dpy,DefaultScreen(w->dpy)),
8, ZPixmap, 0, 0,
width, height, 8, width * sizeof (unsigned char));
(*ximage)->data = malloc (width * height * sizeof (unsigned char));
*pxm = XCreatePixmap (w->dpy, w->root, width, height, 8);
*pic = XRenderCreatePicture (w->dpy, *pxm,
XRenderFindStandardFormat (w->dpy,
PictStandardA8),
0, 0);
}
static void
shadow_finalise_part (Wm *w,
XImage *ximage,
Picture pic,
Pixmap pxm,
int width,
int height)
{
GC gc = XCreateGC (w->dpy, pxm, 0, 0);
XPutImage (w->dpy, pxm, gc, ximage, 0, 0, 0, 0, width, height);
XDestroyImage (ximage);
XFreeGC (w->dpy, gc);
XFreePixmap (w->dpy, pxm);
}
static void
shadow_setup (Wm *w)
{
XImage *ximage;
Pixmap pxm;
unsigned char *data;
int size;
int center;
int x, y;
unsigned char d;
int pwidth, pheight;
double opacity = SHADOW_OPACITY;
if (w->config->shadow_style == SHADOW_STYLE_NONE) return;
if (w->config->shadow_style == SHADOW_STYLE_SIMPLE)
{
w->config->shadow_padding_width = 0;
w->config->shadow_padding_height = 0;
return;
}
/* SHADOW_STYLE_GAUSSIAN */
gussianMap = make_gaussian_map (SHADOW_RADIUS); /* XXX must free */
w->config->shadow_padding_width = gussianMap->size;
w->config->shadow_padding_height = gussianMap->size;
size = gussianMap->size;
center = size / 2;
dbg("%s() gussian size is %i\n", __func__, size);
/* Top & bottom */
pwidth = MAX_TILE_SZ;
pheight = size/2;
shadow_setup_part(w, &ximage, &w->shadow_n_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gussianMap, opacity, center, y - center, WIDTH, HEIGHT);
for (x = 0; x < pwidth; x++)
data[y * pwidth + x] = d;
}
shadow_finalise_part (w, ximage, w->shadow_n_pic, pxm, pwidth, pheight);
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
shadow_setup_part(w, &ximage, &w->shadow_s_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gussianMap, opacity, center, y - center, WIDTH, HEIGHT);
for (x = 0; x < pwidth; x++)
data[(pheight - y - 1) * pwidth + x] = d;
}
shadow_finalise_part (w, ximage, w->shadow_s_pic, pxm, pwidth, pheight);
/* Sides */
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
shadow_setup_part(w, &ximage, &w->shadow_w_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (x = 0; x < pwidth; x++)
{
d = sum_gaussian (gussianMap, opacity, x - center, center, WIDTH, HEIGHT);
for (y = 0; y < pheight; y++)
data[y * pwidth + (pwidth - x - 1)] = d;
}
shadow_finalise_part (w, ximage, w->shadow_w_pic, pxm, pwidth, pheight);
shadow_setup_part(w, &ximage, &w->shadow_e_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (x = 0; x < pwidth; x++)
{
d = sum_gaussian (gussianMap, opacity, x - center, center, WIDTH, HEIGHT);
for (y = 0; y < pheight; y++)
data[y * pwidth + x] = d;
}
shadow_finalise_part (w, ximage, w->shadow_e_pic, pxm, pwidth, pheight);
/* Corners */
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
shadow_setup_part(w, &ximage, &w->shadow_nw_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT);
data[y * pwidth + x] = d;
}
shadow_finalise_part (w, ximage, w->shadow_nw_pic, pxm, pwidth, pheight);
shadow_setup_part(w, &ximage, &w->shadow_sw_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT);
data[(pheight - y - 1) * pwidth + x] = d;
}
shadow_finalise_part (w, ximage, w->shadow_sw_pic, pxm, pwidth, pheight);
shadow_setup_part(w, &ximage, &w->shadow_se_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT);
data[(pheight - y - 1) * pwidth + (pwidth - x -1)] = d;
}
shadow_finalise_part (w, ximage, w->shadow_se_pic, pxm, pwidth, pheight);
shadow_setup_part(w, &ximage, &w->shadow_ne_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gussianMap, opacity, x - center, y - center, WIDTH, HEIGHT);
data[y * pwidth + (pwidth - x -1)] = d;
}
shadow_finalise_part (w, ximage, w->shadow_ne_pic, pxm, pwidth, pheight);
/* Finally center */
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
shadow_setup_part(w, &ximage, &w->shadow_pic, &pxm, pwidth, pheight);
data = (unsigned char*)ximage->data;
d = sum_gaussian (gussianMap, opacity, center, center, WIDTH, HEIGHT);
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
data[y * pwidth + x] = d;
shadow_finalise_part (w, ximage, w->shadow_pic, pxm, pwidth, pheight);
}
static Picture
shadow_gaussian_make_picture (Wm *w, int width, int height)
{
Picture pic;
Pixmap pxm;
int pwidth, pheight, x, y, dw, dh;
pxm = XCreatePixmap (w->dpy, w->root, width, height, 8);
pic = XRenderCreatePicture (w->dpy, pxm,
XRenderFindStandardFormat (w->dpy,
PictStandardA8), 0,0);
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
for (x=0; x < width; x += pwidth)
for (y=0; y < height; y += pheight)
{
if ( (y + pheight) > height )
dh = pheight - ((y + pheight)-height);
else
dh = pheight;
if ( (x + pwidth) > width )
dw = pwidth - ((x + pwidth)-width);
else
dw = pwidth;
XRenderComposite (w->dpy, PictOpSrc,
w->shadow_pic, None, pic,
0, 0, 0, 0, x, y, dw, dh);
}
/* Top & bottom */
if ( width > (MAX_TILE_SZ*2) )
{
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
for (x=0; x < width; x += pwidth )
{
if ( (x + pwidth) > width )
dw = pwidth - ((x + pwidth)-width);
else
dw = pwidth;
XRenderComposite (w->dpy, PictOpSrc,
w->shadow_n_pic, None, pic,
0, 0, 0, 0, x, 0, dw, pheight);
XRenderComposite (w->dpy, PictOpSrc,
w->shadow_s_pic, None, pic,
0, 0, 0, 0, x, height - pheight, dw, pheight);
}
}
/* Sides */
if ( height > (MAX_TILE_SZ*2) )
{
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
for (y=0; y < height; y += pheight)
{
if ( (y + pheight) > height )
dh = pheight - ((y + pheight)-height);
else
dh = pheight;
XRenderComposite (w->dpy, PictOpSrc /* PictOpIn */,
w->shadow_e_pic, None, pic,
0, 0, 0, 0, 0, y, pwidth, dh);
XRenderComposite (w->dpy, PictOpSrc /* PictOpIn */,
w->shadow_w_pic, None, pic,
0, 0, 0, 0, width - pwidth, y, pwidth, dh);
}
}
/* Corners */
pwidth = MAX_TILE_SZ; pheight = MAX_TILE_SZ;
XRenderComposite (w->dpy, PictOpSrc, w->shadow_nw_pic, None, pic,
0, 0, 0, 0, 0, 0, pwidth, pheight);
XRenderComposite (w->dpy, PictOpSrc, w->shadow_ne_pic, None, pic,
0, 0, 0, 0, width - pwidth, 0, pwidth, pheight);
XRenderComposite (w->dpy, PictOpSrc, w->shadow_sw_pic, None, pic,
0, 0, 0, 0, 0, height - pheight, pwidth, pheight);
XRenderComposite (w->dpy, PictOpSrc, w->shadow_se_pic, None, pic,
0, 0, 0, 0, width - pwidth, height - pheight,
pwidth, pheight);
XFreePixmap (w->dpy, pxm);
return pic;
}
static XserverRegion
client_win_extents (Wm *w, Client *client)
{
int x, y, width, height;
XRectangle r;
/* XXX make coverage much fast as its now getting called all the time */
client->get_coverage(client, &x, &y, &width, &height);
r.x = x;
r.y = y;
r.width = width;
r.height = height;
if (w->config->shadow_style)
{
if (client->type == MBCLIENT_TYPE_DIALOG
|| client->type == MBCLIENT_TYPE_TASK_MENU
|| client->type == MBCLIENT_TYPE_OVERRIDE)
{
if (w->config->shadow_style == SHADOW_STYLE_SIMPLE)
{
r.width += w->config->shadow_dx;
r.height += w->config->shadow_dy;
} else {
r.x += w->config->shadow_dx;
r.y += w->config->shadow_dy;
r.width += w->config->shadow_padding_width;
r.height += w->config->shadow_padding_height;
}
}
}
dbg("comp %s() +%i+%i , %ix%i\n", __func__, x, y, width, height);
return XFixesCreateRegion (w->dpy, &r, 1);
}
static XserverRegion
client_border_size (Wm *w, Client *c, int x, int y)
{
XserverRegion border;
border = XFixesCreateRegionFromWindow (w->dpy, c->frame, WindowRegionBounding );
/* translate this */
XFixesTranslateRegion (w->dpy, border, x, y);
return border;
}
static Visual*
comp_engine_get_argb32_visual(Wm *w)
{
XVisualInfo *xvi;
XVisualInfo template;
int nvi;
int i;
XRenderPictFormat *format;
Visual *visual = NULL;
template.screen = w->screen;
template.depth = 32;
template.class = TrueColor;
if ((xvi = XGetVisualInfo (w->dpy,
VisualScreenMask |
VisualDepthMask |
VisualClassMask,
&template,
&nvi)) == NULL)
return NULL;
for (i = 0; i < nvi; i++)
{
format = XRenderFindVisualFormat (w->dpy, xvi[i].visual);
if (format->type == PictTypeDirect && format->direct.alphaMask)
{
visual = xvi[i].visual;
break;
}
}
XFree (xvi);
return visual;
}
void
comp_engine_set_defualts(Wm *w)
{
w->config->shadow_style = SHADOW_STYLE_GAUSSIAN;
w->config->shadow_color[0] = 0;
w->config->shadow_color[1] = 0;
w->config->shadow_color[2] = 0;
w->config->shadow_color[3] = 0xff;
w->config->shadow_dx = SHADOW_OFFSET_X;
w->config->shadow_dy = SHADOW_OFFSET_Y;
/* Not really used yet */
w->config->shadow_padding_width = 0;
w->config->shadow_padding_height = 0;
}
void
comp_engine_theme_init(Wm *w)
{
Pixmap transPixmap, blackPixmap, lowlightPixmap,
redPixmap;
XRenderPictureAttributes pa;
XRenderColor c;
int i;
Picture pics_to_free[] = { w->trans_picture,
w->black_picture,
w->lowlight_picture,
w->shadow_n_pic,
w->shadow_e_pic,
w->shadow_s_pic,
w->shadow_w_pic,
w->shadow_ne_pic,
w->shadow_nw_pic,
w->shadow_se_pic,
w->shadow_sw_pic,
w->shadow_pic };
if (!w->have_comp_engine) return;
for (i=0; i < (sizeof(pics_to_free)/sizeof(Picture)); i++)
if (pics_to_free[i] != None) XRenderFreePicture (w->dpy, pics_to_free[i]);
if (w->config->shadow_style == SHADOW_STYLE_NONE) return;
if (w->config->shadow_style == SHADOW_STYLE_GAUSSIAN)
shadow_setup (w);
pa.subwindow_mode = IncludeInferiors;
pa.repeat = True;
transPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 8);
w->trans_picture
= XRenderCreatePicture (w->dpy, transPixmap,
XRenderFindStandardFormat (w->dpy, PictStandardA8),
CPRepeat,
&pa);
c.red = c.green = c.blue = 0;
c.alpha = 0xb0b0;
XRenderFillRectangle (w->dpy, PictOpSrc, w->trans_picture, &c, 0, 0, 1, 1);
/* black pixmap used for shadows */
blackPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 32);
w->black_picture
= XRenderCreatePicture (w->dpy, blackPixmap,
XRenderFindStandardFormat (w->dpy, PictStandardARGB32),
CPRepeat,
&pa);
c.red = w->config->shadow_color[0] << 8;
c.green = w->config->shadow_color[1] << 8;
c.blue = w->config->shadow_color[2] << 8;
if (w->config->shadow_style == SHADOW_STYLE_GAUSSIAN)
c.alpha = 0xffff;
else
c.alpha = w->config->shadow_color[3] << 8;
dbg("%s() shadow alpha is %i\n", __func__, c.alpha);
XRenderFillRectangle (w->dpy, PictOpSrc, w->black_picture, &c, 0, 0, 1, 1);
#if DEBUG
/* for visual composite debugging */
redPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 32);
w->red_picture
= XRenderCreatePicture (w->dpy, redPixmap,
XRenderFindStandardFormat (w->dpy, PictStandardARGB32),
CPRepeat,
&pa);
c.red = 0xffff; c.green = 0; c.blue = 0; c.alpha = 0xffff;
XRenderFillRectangle (w->dpy, PictOpSrc, w->red_picture, &c, 0, 0, 1, 1);
#endif
/* Used for lowlights */
lowlightPixmap = XCreatePixmap (w->dpy, w->root, 1, 1, 32);
w->lowlight_picture
= XRenderCreatePicture (w->dpy, lowlightPixmap,
XRenderFindStandardFormat (w->dpy, PictStandardARGB32),
CPRepeat,
&pa);
if (w->config->dialog_shade)
{
c.red = w->config->lowlight_params[0] << 8;
c.green = w->config->lowlight_params[1] << 8;
c.blue = w->config->lowlight_params[2] << 8;
c.alpha = w->config->lowlight_params[3] << 8;
} else {
c.red = 0;
c.green = 0;
c.blue = 0;
c.alpha = 0x8d8d;
}
XRenderFillRectangle (w->dpy, PictOpSrc, w->lowlight_picture,
&c, 0, 0, 1, 1);
}
/* Shuts the compositing down */
void
comp_engine_deinit(Wm *w)
{
Client *c = NULL;
if (!w->have_comp_engine)
{
w->comp_engine_disabled = True;
return;
}
if (w->comp_engine_disabled) return;
/*
* really shut down the composite engine.
*
*/
XCompositeUnredirectSubwindows (w->dpy, w->root, CompositeRedirectManual);
if (w->root_picture) XRenderFreePicture (w->dpy, w->root_picture);
if (w->root_buffer) XRenderFreePicture (w->dpy, w->root_buffer);
w->root_buffer = None;
w->root_picture = None;
if (w->all_damage) XDamageDestroy (w->dpy, w->all_damage);
/* Free up any client composite resources */
stack_enumerate(w, c)
comp_engine_client_destroy(w, c);
/* XXX should free up any client picture data ? */
w->comp_engine_disabled = True;
w->have_comp_engine = False; /* bad ? */
}
void
comp_engine_reinit(Wm *w)
{
Client *c = NULL;
w->comp_engine_disabled = False;
comp_engine_init (w);
XSync(w->dpy, False);
if (!stack_empty(w))
{
stack_enumerate(w, c)
{
dbg("%s() calling init for '%s'\n", __func__, c->name);
comp_engine_client_init(w, c);
comp_engine_client_show(w, c);
}
comp_engine_render(w, None);
}
}
Bool
comp_engine_init (Wm *w)
{
int event_base, error_base;
int damage_error;
int xfixes_event, xfixes_error;
int render_event, render_error;
XRenderPictureAttributes pa;
Pixmap tmp_pxm;
dbg("%s() called\n", __func__);
if (w->comp_engine_disabled) return False;
if (w->have_comp_engine) return False; /* already running */
w->comp_engine_disabled = True;
if (!XRenderQueryExtension (w->dpy, &render_event, &render_error))
{
fprintf (stderr, "matchbox: No render extension\n");
w->have_comp_engine = False;
return False;
}
if (!XCompositeQueryExtension (w->dpy, &event_base, &error_base))
{
fprintf (stderr, "matchbox: No composite extension\n");
w->have_comp_engine = False;
return False;
}
if (!XDamageQueryExtension (w->dpy, &w->damage_event, &damage_error))
{
fprintf (stderr, "matchbox: No damage extension\n");
w->have_comp_engine = False;
return False;
}
if (!XFixesQueryExtension (w->dpy, &xfixes_event, &xfixes_error))
{
fprintf (stderr, "matchbox: No XFixes extension\n");
w->have_comp_engine = False;
return False;
}
w->have_comp_engine = True;
w->comp_engine_disabled = False;
comp_stack = NULL;
/* Make the shadow tiles */
pa.subwindow_mode = IncludeInferiors;
w->root_picture
= XRenderCreatePicture (w->dpy, w->root,
XRenderFindVisualFormat (w->dpy,
DefaultVisual (w->dpy, w->screen)),
CPSubwindowMode,
&pa);
w->all_damage = None;
XCompositeRedirectSubwindows (w->dpy, w->root, CompositeRedirectManual);
dbg("%s() success \n", __func__);
/* Setup an mbpixbuf reference for 32bit argb rendering */
w->argb_pb = mb_pixbuf_new_extended(w->dpy,
w->screen,
comp_engine_get_argb32_visual(w),
32);
/* A little hack to get the GC right */
tmp_pxm = XCreatePixmap(w->dpy, w->root, 16, 16, 32);
w->argb_pb->gc = XCreateGC(w->dpy, tmp_pxm, 0, NULL);
XFreePixmap(w->dpy, tmp_pxm);
return True;
}
void
comp_engine_client_init(Wm *w, Client *client)
{
XRenderPictFormat *format;
if (!w->have_comp_engine) return;
client->damaged = 0;
client->damage = None;
client->picture = None;
client->shadow = None;
client->borderSize = None;
client->extents = None;
client->transparency = -1;
client->is_argb32 = False;
/* Check visual */
format = XRenderFindVisualFormat (w->dpy, client->visual);
if (format && format->type == PictTypeDirect && format->direct.alphaMask)
{
dbg("%s() client is ARGB32\n", __func__);
client->is_argb32 = True;
}
comp_engine_client_get_trans_prop(w, client);
}
int
comp_engine_client_get_trans_prop(Wm *w, Client *client)
{
Atom actual;
int format;
unsigned long n, left;
char *data;
XGetWindowProperty(w->dpy, client->window, w->atoms[CM_TRANSLUCENCY],
0L, 1L, False, XA_INTEGER, &actual, &format,
&n, &left, (unsigned char **) &data);
if (data != None)
{
client->transparency = (int) *data;
XFree( (void *) data);
return client->transparency;
}
return -1;
}
void
comp_engine_client_show(Wm *w, Client *client)
{
XserverRegion region;
XRenderPictureAttributes pa;
if (!w->have_comp_engine) return;
dbg("%s() called\n", __func__);
/*
* Destroying / Recreating the client pictures should hopefully save
* some memory in the server.
*/
if (client->picture == None)
{
pa.subwindow_mode = IncludeInferiors;
client->picture = XRenderCreatePicture (w->dpy,
client->frame,
XRenderFindVisualFormat (w->dpy,
client->visual),
CPSubwindowMode,
&pa);
}
if (client->damage != None)
XDamageDestroy (w->dpy, client->damage);
client->damage = XDamageCreate (w->dpy, client->frame,
XDamageReportNonEmpty);
region = client_win_extents (w, client);
comp_engine_add_damage (w, region);
}
void
comp_engine_client_hide(Wm *w, Client *client)
{
Client *t = NULL;
if (!w->have_comp_engine) return;
dbg("%s() called\n", __func__);
if (client->flags & CLIENT_IS_MODAL_FLAG
&& ((t = wm_get_visible_main_client(w)) != NULL))
{
/* We need to make sure the any lowlighting on a 'parent'
* modal for app gets cleared. This is kind of a sledgehammer
* approach to it, but more suttle attempts oddly fail at times.
*
* FIXME: keep an eye on this for future revisions of composite
* - there may be a better way.
*/
comp_engine_client_repair (w, t);
comp_engine_add_damage (w, t->extents);
}
if (client->damage != None)
{
XDamageDestroy (w->dpy, client->damage);
client->damage = None;
}
if (client->extents != None)
{
comp_engine_add_damage (w, client->extents);
client->extents = None;
}
if (client->picture)
{
XRenderFreePicture (w->dpy, client->picture);
client->picture = None;
}
}
void
comp_engine_client_destroy(Wm *w, Client *client)
{
if (!w->have_comp_engine) return;
dbg("%s() called\n", __func__);
comp_engine_client_hide(w, client);
if (client->picture)
XRenderFreePicture (w->dpy, client->picture);
if (client->border_clip != None)
XFixesDestroyRegion (w->dpy, client->border_clip);
}
static void
comp_engine_add_damage (Wm *w, XserverRegion damage)
{
if (!w->have_comp_engine) return;
dbg("%s() called\n", __func__);
if (w->all_damage)
{
XFixesUnionRegion (w->dpy, w->all_damage, w->all_damage, damage);
XFixesDestroyRegion (w->dpy, damage);
}
else
w->all_damage = damage;
}
void
comp_engine_client_repair (Wm *w, Client *client)
{
XserverRegion parts;
int x, y, width, height;
if (!w->have_comp_engine) return;
dbg("%s() called for client '%s'\n", __func__, client->name);
parts = XFixesCreateRegion (w->dpy, 0, 0);
/* translate region */
dbg("%s() client damage is %li\n", __func__, client->damage);
XDamageSubtract (w->dpy, client->damage, None, parts);
client->get_coverage(client, &x, &y, &width, &height);
XFixesTranslateRegion (w->dpy, parts, x, y);
comp_engine_add_damage (w, parts);
}
void
comp_engine_client_configure(Wm *w, Client *client)
{
XserverRegion damage = None;
XserverRegion extents = client_win_extents(w, client);
if (client->picture != None)
{
XRenderFreePicture (w->dpy, client->picture);
client->picture = None;
}
damage = XFixesCreateRegion (w->dpy, 0, 0);
if (client->extents != None)
XFixesCopyRegion (w->dpy, damage, client->extents);
XFixesUnionRegion (w->dpy, damage, damage, extents);
XFixesDestroyRegion (w->dpy, extents);
comp_engine_add_damage (w, damage);
}
void
comp_engine_handle_events(Wm *w, XEvent *ev)
{
if (!w->have_comp_engine) return;
if (ev->type == w->damage_event + XDamageNotify)
{
XDamageNotifyEvent *de;
Client *c;
dbg("%s() called have damage event \n", __func__);
de = (XDamageNotifyEvent *)ev;
c = wm_find_client(w, de->drawable, FRAME);
if (c)
comp_engine_client_repair(w, c);
else
{
dbg("%s() failed to find damaged window \n", __func__);
}
}
}
static void
_render_a_client(Wm *w,
Client *client,
XserverRegion region,
int lowlight_type) /* 0 - none, 1 app, 2 full */
{
int x,y,width,height;
XserverRegion winborder;
if (client->picture == None) {
dbg("%s() no pixture for %s\n", __func__, client->name);
return;
}
if (client->extents)
XFixesDestroyRegion (w->dpy, client->extents);
client->extents = client_win_extents (w, client);
client->get_coverage(client, &x, &y, &width, &height);
winborder = client_border_size (w, client, x, y);
/* Transparency only done for dialogs and overides */
if ( (client->transparency == -1
|| client->type == MBCLIENT_TYPE_APP
|| client->type == MBCLIENT_TYPE_DESKTOP
|| client->type == MBCLIENT_TYPE_TOOLBAR
|| client->type == MBCLIENT_TYPE_PANEL) && !client->is_argb32)
{
XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, region);
XFixesSubtractRegion (w->dpy, region, region, winborder);
XRenderComposite (w->dpy, PictOpSrc,
client->picture,
None, w->root_buffer,
0, 0, 0, 0, x, y, width, height);
}
/* Render lowlight dialog modal for app */
if (lowlight_type == 1
&& (client->type & (MBCLIENT_TYPE_APP|MBCLIENT_TYPE_DESKTOP)))
{
int title_offset = 0;
dbg("%s() rendering lowlight\n", __func__);
if (client->type == MBCLIENT_TYPE_APP)
title_offset = main_client_title_height(client);
XRenderComposite (w->dpy, PictOpOver, w->lowlight_picture, None,
w->root_buffer,
0, 0, 0, 0, x, y + title_offset,
width, height - title_offset);
}
/* Render lowlight dialog modal for root - e.g lowlight everything */
if (lowlight_type == 2 && client->win_modal_blocker == None)
XRenderComposite (w->dpy, PictOpOver, w->lowlight_picture, None,
w->root_buffer,
0, 0, 0, 0, x, y,
width, height);
if (client->border_clip != None)
{
XFixesDestroyRegion (w->dpy, client->border_clip);
client->border_clip = None;
}
client->border_clip = XFixesCreateRegion (w->dpy, 0, 0);
XFixesCopyRegion (w->dpy, client->border_clip, region);
XFixesDestroyRegion (w->dpy, winborder); /* XXX the leak plugged ? */
}
void
comp_engine_destroy_root_buffer(Wm *w)
{
if (w->root_buffer)
{
XRenderFreePicture (w->dpy, w->root_buffer);
w->root_buffer = None;
}
}
void
comp_engine_render(Wm *w, XserverRegion region)
{
Client *client_top_app = NULL, *t = NULL;
int x,y,width,height;
int lowlight = 0;
if (!w->have_comp_engine || stack_empty(w)) return;
dbg("%s() called\n", __func__);
if (!region)
{
XRectangle r;
r.x = 0;
r.y = 0;
r.width = w->dpy_width;
r.height = w->dpy_height;
region = XFixesCreateRegion (w->dpy, &r, 1);
}
client_top_app = wm_get_visible_main_client(w);
if (!w->root_buffer)
{
Pixmap rootPixmap = XCreatePixmap (w->dpy, w->root,
w->dpy_width, w->dpy_height,
DefaultDepth (w->dpy, w->screen));
w->root_buffer = XRenderCreatePicture (w->dpy, rootPixmap,
XRenderFindVisualFormat (w->dpy,
DefaultVisual (w->dpy, w->screen)),
0, 0);
XRenderComposite (w->dpy, PictOpSrc, w->black_picture,
None, w->root_buffer, 0, 0, 0, 0, 0, 0,
w->dpy_width, w->dpy_height);
XFreePixmap (w->dpy, rootPixmap);
}
XFixesSetPictureClipRegion (w->dpy, w->root_picture, 0, 0, region);
XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, region);
#if DEBUG
if (w->flags & DEBUG_COMPOSITE_VISIBLE_FLAG)
{
XRenderComposite (w->dpy, PictOpSrc, w->red_picture,
None, w->root_picture,
0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height);
XSync(w->dpy, False);
/* return; */
}
#endif
/* Check initially to see what kind of lowlight todo ( if any ) */
stack_enumerate_reverse(w, t)
{
if (t->type == MBCLIENT_TYPE_DIALOG
&& t->flags & CLIENT_IS_MODAL_FLAG)
{
lowlight = ((t->win_modal_blocker) ? 2 : 1);
}
if (t == client_top_app)
break;
}
/* Render top -> bottom */
stack_enumerate_reverse(w, t)
{
dbg("%s() rendering %s\n", __func__, t->name);
_render_a_client(w, t, region, lowlight);
if (t == client_top_app)
break;
}
if (client_top_app == NULL)
{
/* Render block of boring black in case of no top app or desktop */
XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, region);
XRenderComposite (w->dpy, PictOpSrc, w->black_picture,
None, w->root_buffer, 0, 0, 0, 0, 0, 0,
w->dpy_width, w->dpy_height);
XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None);
client_top_app = w->stack_bottom;
}
XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None);
/* Now render shadows but bottom -> top this time */
for ((t)=client_top_app; (t) != NULL; (t)=(t)->above)
{
dbg("%s() rendering shadow for %s\n", __func__, t->name);
if ((t->type == MBCLIENT_TYPE_DIALOG && t->mapped)
|| t->type == MBCLIENT_TYPE_TASK_MENU
|| t->type == MBCLIENT_TYPE_OVERRIDE)
{
dbg("%s() rendering shadow for %s\n", __func__, t->name);
if (!t->picture)
{
dbg("%s() no pixture for %s\n", __func__, t->name);
continue;
}
if (w->config->shadow_style)
{
Picture shadow_pic;
t->get_coverage(t, &x, &y, &width, &height);
if (w->config->shadow_style == SHADOW_STYLE_SIMPLE)
{
XserverRegion shadow_region;
/* Grab 'shape' region of window */
shadow_region = client_border_size (w, t, x, y);
/* Offset it. */
XFixesTranslateRegion (w->dpy, shadow_region,
w->config->shadow_dx,
w->config->shadow_dy);
/* Intersect it, so only border remains */
XFixesIntersectRegion (w->dpy, shadow_region,
t->border_clip, shadow_region );
XFixesSetPictureClipRegion (w->dpy, w->root_buffer,
0, 0, shadow_region);
/* now paint them */
if (t->is_argb32 )
{
XRenderComposite (w->dpy, PictOpOver,
w->black_picture,
t->picture,
w->root_buffer,
0, 0, 0, 0,
x + w->config->shadow_dx,
y + w->config->shadow_dy,
width + w->config->shadow_padding_width,
height + w->config->shadow_padding_height);
}
else
{
XRenderComposite (w->dpy, PictOpOver,
w->black_picture,
None,
w->root_buffer,
0, 0, 0, 0,
x + w->config->shadow_dx,
y + w->config->shadow_dy,
width + w->config->shadow_padding_width,
height + w->config->shadow_padding_height);
}
/* Paint any transparent window contents */
if (t->transparency != -1 || t->is_argb32 )
{
XFixesDestroyRegion (w->dpy, shadow_region);
shadow_region = client_border_size (w, t, x, y);
XFixesIntersectRegion (w->dpy, shadow_region,
t->border_clip, shadow_region );
XFixesSetPictureClipRegion (w->dpy, w->root_buffer,
0, 0, shadow_region);
if (t->is_argb32)
XRenderComposite (w->dpy, PictOpOver,
t->picture, None,
w->root_buffer, 0, 0, 0, 0,
x, y, width, height);
else
XRenderComposite (w->dpy, PictOpOver,
t->picture, w->trans_picture,
w->root_buffer, 0, 0, 0, 0,
x, y, width, height);
}
XFixesDestroyRegion (w->dpy, shadow_region);
}
else /* GAUSSIAN */
{
XFixesSetPictureClipRegion (w->dpy, w->root_buffer,
0, 0, t->border_clip);
if (t->transparency != -1 || t->is_argb32 )
{
/* No shadows currently for transparent windows */
XRenderComposite (w->dpy, PictOpOver,
t->picture, w->trans_picture,
w->root_buffer, 0, 0, 0, 0,
x, y, width, height);
} else {
/* Combine pregenerated shadow tiles */
shadow_pic
= shadow_gaussian_make_picture (w,
width + w->config->shadow_padding_width,
height + w->config->shadow_padding_height);
XRenderComposite (w->dpy, PictOpOver, w->black_picture,
shadow_pic,
w->root_buffer,
0, 0, 0, 0,
x + w->config->shadow_dx,
y + w->config->shadow_dy,
width + w->config->shadow_padding_width,
height + w->config->shadow_padding_height);
XRenderFreePicture (w->dpy, shadow_pic);
}
}
}
}
}
XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None);
XRenderComposite (w->dpy, PictOpSrc, w->root_buffer, None, w->root_picture,
0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height);
XSync(w->dpy, False);
}
#endif
void
comp_engine_time(Wm *w)
{
#ifdef STANDALONE /* No timings for standalone */
return;
#endif /* STANDALONE */
#if DO_TIMINGS
struct timeval tv_start, tv_end;
struct timezone tz;
long diff;
#ifdef USE_COMPOSITE
XSync(w->dpy, False);
gettimeofday(&tv_start, &tz);
XFixesSetPictureClipRegion (w->dpy, w->root_buffer, 0, 0, None);
XFixesSetPictureClipRegion (w->dpy, w->root_picture, 0, 0, None);
XRenderComposite (w->dpy, PictOpOver, w->lowlight_picture, None,
w->root_buffer,
0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height);
XRenderComposite (w->dpy, PictOpSrc, w->root_buffer, None, w->root_picture,
0, 0, 0, 0, 0, 0, w->dpy_width, w->dpy_height);
XSync(w->dpy, False);
gettimeofday(&tv_end, &tz);
diff = ((tv_end.tv_sec * 1000000) + tv_end.tv_usec) - ((tv_start.tv_sec * 1000000) + tv_start.tv_usec);
fprintf(stderr, "COMPOSITE LOWLIGHT TIMING: %li us\n", diff);
sleep(1);
comp_engine_render(w, None);
#else
#ifndef STANDALONE
MBPixbufImage *img;
int x, y;
Pixmap pxm_tmp;
Window win;
XSetWindowAttributes attr;
attr.override_redirect = True;
attr.event_mask = ChildMask|ButtonPressMask|ExposureMask;
XSync(w->dpy, False);
gettimeofday(&tv_start, &tz);
win = XCreateWindow(w->dpy, w->root, 0, 0,
w->dpy_width, w->dpy_height, 0,
CopyFromParent,
CopyFromParent,
CopyFromParent,
CWOverrideRedirect|CWEventMask,
&attr);
pxm_tmp = XCreatePixmap(w->dpy, win,
w->dpy_width,
w->dpy_height ,
w->pb->depth);
img = mb_pixbuf_img_new_from_x_drawable(w->pb, w->root,
None, 0, 0,
w->dpy_width,
w->dpy_height,
True);
if (!img)
{
fprintf(stderr, "%s(): failed to grab root image\n", __func__);
return;
}
for (x = 0; x < w->dpy_width; x++)
for (y = 0; y < w->dpy_height; y++)
mb_pixbuf_img_plot_pixel_with_alpha(w->pb,
img, x, y,
w->config->lowlight_params[0],
w->config->lowlight_params[1],
w->config->lowlight_params[2],
w->config->lowlight_params[3]
);
mb_pixbuf_img_render_to_drawable(w->pb, img, pxm_tmp, 0, 0);
XSetWindowBackgroundPixmap(w->dpy, win, pxm_tmp);
XClearWindow(w->dpy, win);
XMapRaised(w->dpy, win);
XSync(w->dpy, False);
gettimeofday(&tv_end, &tz);
diff = ((tv_end.tv_sec * 1000000) + tv_end.tv_usec) - ((tv_start.tv_sec * 1000000) + tv_start.tv_usec);
fprintf(stderr, "XIMAGE LOWLIGHT TIMING: %li us\n", diff);
mb_pixbuf_img_free(w->pb, img);
XFreePixmap(w->dpy, pxm_tmp);
XSync(w->dpy, False);
sleep(1);
XDestroyWindow(w->dpy, win);
#endif /* STANDALONE */
#endif /* USE_COMPOSITE */
#else
fprintf(stderr, "matchbox-window-manager: timing functionality disabled\n");
#endif /* DO TIMINGS */
}
syntax highlighted by Code2HTML, v. 0.9.1