0ce8449e85
svn path=/trunk/kdebase/kwin/; revision=379504
2596 lines
66 KiB
C
2596 lines
66 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
* Copyright © 2003 Keith Packard
|
|
*
|
|
* 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 Keith Packard not be used in
|
|
* advertising or publicity pertaining to distribution of the software without
|
|
* specific, written prior permission. Keith Packard makes no
|
|
* representations about the suitability of this software for any purpose. It
|
|
* is provided "as is" without express or implied warranty.
|
|
*
|
|
* KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL KEITH PACKARD 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.
|
|
*/
|
|
|
|
|
|
/* Modified by Matthew Hawn. I don't know what to say here so follow what it
|
|
says above. Not that I can really do anything about it
|
|
*/
|
|
|
|
/* Modified by Dan Doel*/
|
|
|
|
/*
|
|
Version 2.x of xcompmgr, kompmgr changes by Thomas Lübking and Heiko Przybyl
|
|
check baghira.sf.net for more infos
|
|
*/
|
|
#define _VERSION_ 2.02
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/extensions/Xcomposite.h>
|
|
#include <X11/extensions/Xdamage.h>
|
|
#include <X11/extensions/Xrender.h>
|
|
|
|
#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2
|
|
#define HAS_NAME_WINDOW_PIXMAP 1
|
|
#endif
|
|
|
|
#define CAN_DO_USABLE 0
|
|
|
|
typedef struct _ignore {
|
|
struct _ignore *next;
|
|
unsigned long sequence;
|
|
} ignore;
|
|
|
|
typedef struct _win {
|
|
struct _win *next;
|
|
Window id;
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
Pixmap pixmap;
|
|
#endif
|
|
XWindowAttributes a;
|
|
#if CAN_DO_USABLE
|
|
Bool usable; /* mapped and all damaged at one point */
|
|
XRectangle damage_bounds; /* bounds of damage */
|
|
#endif
|
|
Bool isInFade;
|
|
int mode;
|
|
int damaged;
|
|
Damage damage;
|
|
Picture picture;
|
|
Picture alphaPict;
|
|
Picture shadowPict;
|
|
XserverRegion borderSize;
|
|
XserverRegion extents;
|
|
unsigned int preShadeOpacity;
|
|
Picture shadow;
|
|
int shadow_dx;
|
|
int shadow_dy;
|
|
int shadow_width;
|
|
int shadow_height;
|
|
unsigned int opacity;
|
|
unsigned int shadowSize;
|
|
Atom windowType;
|
|
unsigned long damage_sequence; /* sequence when damage was created */
|
|
|
|
/* for drawing translucent windows */
|
|
XserverRegion borderClip;
|
|
struct _win *prev_trans;
|
|
} win;
|
|
|
|
typedef struct _conv {
|
|
int size;
|
|
double *data;
|
|
} conv;
|
|
|
|
typedef struct _fade {
|
|
struct _fade *next;
|
|
win *w;
|
|
double cur;
|
|
double finish;
|
|
double step;
|
|
void (*callback) (Display *dpy, win *w, Bool gone);
|
|
Display *dpy;
|
|
Bool gone;
|
|
} fade;
|
|
|
|
win *list;
|
|
fade *fades;
|
|
Display *dpy;
|
|
char *display = 0;
|
|
int scr;
|
|
Window root;
|
|
Picture rootPicture;
|
|
Picture rootBuffer;
|
|
Picture blackPicture;
|
|
Picture transBlackPicture;
|
|
Picture rootTile;
|
|
XserverRegion allDamage;
|
|
Bool clipChanged;
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
Bool hasNamePixmap;
|
|
#endif
|
|
int root_height, root_width;
|
|
ignore *ignore_head, **ignore_tail = &ignore_head;
|
|
int xfixes_event, xfixes_error;
|
|
int damage_event, damage_error;
|
|
int composite_event, composite_error;
|
|
int render_event, render_error;
|
|
Bool synchronize;
|
|
int composite_opcode;
|
|
|
|
/* find these once and be done with it */
|
|
Atom opacityAtom;
|
|
Atom shadowAtom;
|
|
Atom shadeAtom;
|
|
Atom winTypeAtom;
|
|
Atom winDesktopAtom;
|
|
Atom winDockAtom;
|
|
Atom winToolbarAtom;
|
|
Atom winMenuAtom;
|
|
Atom winUtilAtom;
|
|
Atom winSplashAtom;
|
|
Atom winDialogAtom;
|
|
Atom winNormalAtom;
|
|
|
|
/* opacity property name; sometime soon I'll write up an EWMH spec for it */
|
|
#define OPACITY_PROP "_KDE_WM_WINDOW_OPACITY"
|
|
#define SHADOW_PROP "_KDE_WM_WINDOW_SHADOW"
|
|
#define SHADE_PROP "_KDE_WM_WINDOW_SHADE"
|
|
|
|
#define TRANSLUCENT 0xe0000000
|
|
#define OPAQUE 0xffffffff
|
|
|
|
conv *gaussianMap;
|
|
|
|
#define WINDOW_SOLID 0
|
|
#define WINDOW_TRANS 1
|
|
#define WINDOW_ARGB 2
|
|
|
|
#define TRANS_OPACITY 0.75
|
|
|
|
#define DEBUG_REPAINT 0
|
|
#define DEBUG_EVENTS 0
|
|
#define MONITOR_REPAINT 0
|
|
|
|
#define SHADOWS 1
|
|
#define SHARP_SHADOW 0
|
|
|
|
typedef enum _compMode {
|
|
CompSimple, /* looks like a regular X server */
|
|
CompServerShadows, /* use window alpha for shadow; sharp, but precise */
|
|
CompClientShadows, /* use window extents for shadow, blurred */
|
|
} CompMode;
|
|
|
|
static void
|
|
determine_mode(Display *dpy, win *w);
|
|
|
|
static double
|
|
get_opacity_percent(Display *dpy, win *w, double def);
|
|
|
|
static XserverRegion
|
|
win_extents (Display *dpy, win *w);
|
|
|
|
CompMode compMode = CompSimple;
|
|
|
|
int shadowRadius = 12;
|
|
int shadowOffsetX = 0;
|
|
int shadowOffsetY = 0;
|
|
double shadowOpacity = .75;
|
|
XRenderColor shadowColor;
|
|
|
|
double fade_in_step = 0.028;
|
|
double fade_out_step = 0.03;
|
|
int fade_delta = 10;
|
|
int fade_time = 0;
|
|
Bool fadeWindows = False;
|
|
Bool excludeDockShadows = False;
|
|
Bool fadeTrans = False;
|
|
|
|
Bool autoRedirect = False;
|
|
|
|
/* For shadow precomputation */
|
|
int Gsize = -1;
|
|
unsigned char *shadowCorner = NULL;
|
|
unsigned char *shadowTop = NULL;
|
|
|
|
sXRenderFindVisualFormat(Display *dpy, _Xconst Visual *visual)
|
|
{
|
|
XRenderPictFormat* format = XRenderFindVisualFormat(dpy,visual);
|
|
if (format)
|
|
return format;
|
|
else
|
|
return XRenderFindStandardFormat (dpy, PictStandardA8);
|
|
}
|
|
|
|
int
|
|
get_time_in_milliseconds ()
|
|
{
|
|
struct timeval tv;
|
|
|
|
gettimeofday (&tv, NULL);
|
|
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
|
}
|
|
|
|
fade *
|
|
find_fade (win *w)
|
|
{
|
|
fade *f;
|
|
|
|
for (f = fades; f; f = f->next)
|
|
{
|
|
if (f->w == w)
|
|
return f;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
dequeue_fade (Display *dpy, fade *f)
|
|
{
|
|
f->w->isInFade = False;
|
|
fade **prev;
|
|
|
|
for (prev = &fades; *prev; prev = &(*prev)->next)
|
|
if (*prev == f)
|
|
{
|
|
*prev = f->next;
|
|
if (f->callback)
|
|
(*f->callback) (dpy, f->w, f->gone);
|
|
free (f);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
cleanup_fade (Display *dpy, win *w)
|
|
{
|
|
fade *f = find_fade (w);
|
|
if (f)
|
|
dequeue_fade (dpy, f);
|
|
}
|
|
|
|
void
|
|
enqueue_fade (Display *dpy, fade *f)
|
|
{
|
|
f->w->isInFade = True;
|
|
if (!fades)
|
|
fade_time = get_time_in_milliseconds () + fade_delta;
|
|
f->next = fades;
|
|
fades = f;
|
|
}
|
|
|
|
static void
|
|
set_fade (Display *dpy, win *w, double start, double finish, double step,
|
|
void (*callback) (Display *dpy, win *w, Bool gone),
|
|
Bool gone, Bool exec_callback, Bool override)
|
|
{
|
|
fade *f;
|
|
|
|
f = find_fade (w);
|
|
if (!f)
|
|
{
|
|
f = malloc (sizeof (fade));
|
|
f->next = 0;
|
|
f->w = w;
|
|
f->cur = start;
|
|
enqueue_fade (dpy, f);
|
|
}
|
|
else if(!override)
|
|
return;
|
|
else
|
|
{
|
|
if (exec_callback)
|
|
if (f->callback)
|
|
(*f->callback)(dpy, f->w, f->gone);
|
|
}
|
|
|
|
if (finish < 0)
|
|
finish = 0;
|
|
if (finish > 1)
|
|
finish = 1;
|
|
f->finish = finish;
|
|
if (f->cur < finish)
|
|
f->step = step;
|
|
else if (f->cur > finish)
|
|
f->step = -step;
|
|
f->callback = callback;
|
|
f->gone = gone;
|
|
w->opacity = f->cur * OPAQUE;
|
|
#if 0
|
|
printf ("set_fade start %g step %g\n", f->cur, f->step);
|
|
#endif
|
|
determine_mode (dpy, w);
|
|
if (w->shadow)
|
|
{
|
|
XRenderFreePicture (dpy, w->shadow);
|
|
w->shadow = None;
|
|
w->extents = win_extents (dpy, w);
|
|
}
|
|
}
|
|
|
|
int
|
|
fade_timeout (void)
|
|
{
|
|
int now;
|
|
int delta;
|
|
if (!fades)
|
|
return -1;
|
|
now = get_time_in_milliseconds();
|
|
delta = fade_time - now;
|
|
if (delta < 0)
|
|
delta = 0;
|
|
/* printf ("timeout %d\n", delta); */
|
|
return delta;
|
|
}
|
|
|
|
void
|
|
run_fades (Display *dpy)
|
|
{
|
|
int now = get_time_in_milliseconds();
|
|
fade *f, *next;
|
|
int steps;
|
|
|
|
#if 0
|
|
printf ("run fades\n");
|
|
#endif
|
|
if (fade_time - now > 0)
|
|
return;
|
|
steps = 1 + (now - fade_time) / fade_delta;
|
|
for (next = fades; f = next; )
|
|
{
|
|
win *w = f->w;
|
|
next = f->next;
|
|
f->cur += f->step * steps;
|
|
if (f->cur >= 1)
|
|
f->cur = 1;
|
|
else if (f->cur < 0)
|
|
f->cur = 0;
|
|
#if 0
|
|
printf ("opacity now %g -> %g\n", f->cur, f->finish);
|
|
#endif
|
|
w->opacity = f->cur * OPAQUE;
|
|
if (f->step > 0)
|
|
{
|
|
if (f->cur >= f->finish)
|
|
{
|
|
w->opacity = f->finish*OPAQUE;
|
|
dequeue_fade (dpy, f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (f->cur <= f->finish)
|
|
{
|
|
w->opacity = f->finish*OPAQUE;
|
|
dequeue_fade (dpy, f);
|
|
}
|
|
}
|
|
determine_mode (dpy, w);
|
|
if (w->shadow)
|
|
{
|
|
XRenderFreePicture (dpy, w->shadow);
|
|
w->shadow = None;
|
|
w->extents = win_extents(dpy, w);
|
|
}
|
|
}
|
|
fade_time = now + fade_delta;
|
|
}
|
|
|
|
#define SHADOW_OFFSET_X ((-shadowRadius * 7 / 5) - shadowOffsetX * shadowRadius / 100) * w->shadowSize
|
|
#define SHADOW_OFFSET_Y ((-shadowRadius * 7 / 5) - shadowOffsetY * shadowRadius / 100) * w->shadowSize
|
|
/*#define SHADOW_OFFSET_X (w->shadowSize * -shadowRadius * 7 / 500) - w->shadowSize * shadowOffsetX * shadowRadius / 10000
|
|
#define SHADOW_OFFSET_Y (w->shadowSize * -shadowRadius * 7 / 500) - w->shadowSize * shadowOffsetY * shadowRadius / 10000*/
|
|
|
|
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 (Display *dpy, double r)
|
|
{
|
|
conv *c;
|
|
int size = ((int) ceil ((r * 3)) + 1) & ~1;
|
|
int center = size / 2;
|
|
int x, y;
|
|
double t;
|
|
double g;
|
|
|
|
c = malloc (sizeof (conv) + size * size * sizeof (double));
|
|
c->size = size;
|
|
c->data = (double *) (c + 1);
|
|
t = 0.0;
|
|
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;
|
|
}
|
|
/* printf ("gaussian total %f\n", t); */
|
|
for (y = 0; y < size; y++)
|
|
for (x = 0; x < size; x++)
|
|
{
|
|
c->data[y*size + x] /= t;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* A picture will help
|
|
*
|
|
* -center 0 width width+center
|
|
* -center +-----+-------------------+-----+
|
|
* | | | |
|
|
* | | | |
|
|
* 0 +-----+-------------------+-----+
|
|
* | | | |
|
|
* | | | |
|
|
* | | | |
|
|
* height +-----+-------------------+-----+
|
|
* | | | |
|
|
* height+ | | | |
|
|
* center +-----+-------------------+-----+
|
|
*/
|
|
|
|
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 char) (v * opacity * 255.0));
|
|
}
|
|
|
|
/* precompute shadow corners and sides to save time for large windows */
|
|
static void
|
|
presum_gaussian (conv *map)
|
|
{
|
|
int center = map->size/2;
|
|
int opacity, x, y;
|
|
|
|
Gsize = map->size;
|
|
|
|
if (shadowCorner)
|
|
free ((void *)shadowCorner);
|
|
if (shadowTop)
|
|
free ((void *)shadowTop);
|
|
|
|
shadowCorner = (unsigned char *)(malloc ((Gsize + 1) * (Gsize + 1) * 26));
|
|
shadowTop = (unsigned char *)(malloc ((Gsize + 1) * 26));
|
|
|
|
for (x = 0; x <= Gsize; x++)
|
|
{
|
|
shadowTop[25 * (Gsize + 1) + x] = sum_gaussian (map, 1, x - center, center, Gsize * 2, Gsize * 2);
|
|
for(opacity = 0; opacity < 25; opacity++)
|
|
shadowTop[opacity * (Gsize + 1) + x] = shadowTop[25 * (Gsize + 1) + x] * opacity / 25;
|
|
for(y = 0; y <= x; y++)
|
|
{
|
|
shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]
|
|
= sum_gaussian (map, 1, x - center, y - center, Gsize * 2, Gsize * 2);
|
|
shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y]
|
|
= shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x];
|
|
for(opacity = 0; opacity < 25; opacity++)
|
|
shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]
|
|
= shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y]
|
|
= shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] * opacity / 25;
|
|
}
|
|
}
|
|
}
|
|
|
|
static XImage *
|
|
make_shadow (Display *dpy, double opacity, int width, int height)
|
|
{
|
|
XImage *ximage;
|
|
unsigned char *data;
|
|
int gsize = gaussianMap->size;
|
|
int ylimit, xlimit;
|
|
int swidth = width + gsize;
|
|
int sheight = height + gsize;
|
|
int center = gsize / 2;
|
|
int x, y;
|
|
unsigned char d;
|
|
int x_diff;
|
|
int opacity_int = (int)(opacity * 25);
|
|
data = malloc (swidth * sheight * sizeof (unsigned char));
|
|
if (!data)
|
|
return 0;
|
|
ximage = XCreateImage (dpy,
|
|
DefaultVisual(dpy, DefaultScreen(dpy)),
|
|
8,
|
|
ZPixmap,
|
|
0,
|
|
(char *) data,
|
|
swidth, sheight, 8, swidth * sizeof (unsigned char));
|
|
if (!ximage)
|
|
{
|
|
free (data);
|
|
return 0;
|
|
}
|
|
/*
|
|
* Build the gaussian in sections
|
|
*/
|
|
|
|
/*
|
|
* center (fill the complete data array)
|
|
*/
|
|
if (Gsize > 0)
|
|
d = shadowTop[opacity_int * (Gsize + 1) + Gsize];
|
|
else
|
|
d = sum_gaussian (gaussianMap, opacity, center, center, width, height);
|
|
memset(data, d, sheight * swidth);
|
|
|
|
/*
|
|
* corners
|
|
*/
|
|
ylimit = gsize;
|
|
if (ylimit > sheight / 2)
|
|
ylimit = (sheight + 1) / 2;
|
|
xlimit = gsize;
|
|
if (xlimit > swidth / 2)
|
|
xlimit = (swidth + 1) / 2;
|
|
|
|
for (y = 0; y < ylimit; y++)
|
|
for (x = 0; x < xlimit; x++)
|
|
{
|
|
if (xlimit == Gsize && ylimit == Gsize)
|
|
d = shadowCorner[opacity_int * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x];
|
|
else
|
|
d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height);
|
|
data[y * swidth + x] = d;
|
|
data[(sheight - y - 1) * swidth + x] = d;
|
|
data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d;
|
|
data[y * swidth + (swidth - x - 1)] = d;
|
|
}
|
|
|
|
/*
|
|
* top/bottom
|
|
*/
|
|
x_diff = swidth - (gsize * 2);
|
|
if (x_diff > 0 && ylimit > 0)
|
|
{
|
|
for (y = 0; y < ylimit; y++)
|
|
{
|
|
if (ylimit == Gsize)
|
|
d = shadowTop[opacity_int * (Gsize + 1) + y];
|
|
else
|
|
d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height);
|
|
memset (&data[y * swidth + gsize], d, x_diff);
|
|
memset (&data[(sheight - y - 1) * swidth + gsize], d, x_diff);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* sides
|
|
*/
|
|
|
|
for (x = 0; x < xlimit; x++)
|
|
{
|
|
if (xlimit == Gsize)
|
|
d = shadowTop[opacity_int * (Gsize + 1) + x];
|
|
else
|
|
d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height);
|
|
for (y = gsize; y < sheight - gsize; y++)
|
|
{
|
|
data[y * swidth + x] = d;
|
|
data[y * swidth + (swidth - x - 1)] = d;
|
|
}
|
|
}
|
|
|
|
return ximage;
|
|
}
|
|
|
|
static Picture
|
|
shadow_picture (Display *dpy, double opacity, Picture alpha_pict, int width, int height, int *wp, int *hp)
|
|
{
|
|
XImage *shadowImage;
|
|
Pixmap shadowPixmap;
|
|
Pixmap finalPixmap;
|
|
Picture shadowPicture;
|
|
Picture finalPicture;
|
|
GC gc;
|
|
|
|
shadowImage = make_shadow (dpy, opacity, width, height);
|
|
if (!shadowImage)
|
|
return None;
|
|
shadowPixmap = XCreatePixmap (dpy, root,
|
|
shadowImage->width,
|
|
shadowImage->height,
|
|
8);
|
|
if (!shadowPixmap)
|
|
{
|
|
XDestroyImage (shadowImage);
|
|
return None;
|
|
}
|
|
|
|
shadowPicture = XRenderCreatePicture (dpy, shadowPixmap,
|
|
XRenderFindStandardFormat (dpy, PictStandardA8),
|
|
0, 0);
|
|
if (!shadowPicture)
|
|
{
|
|
XDestroyImage (shadowImage);
|
|
XFreePixmap (dpy, shadowPixmap);
|
|
return None;
|
|
}
|
|
|
|
gc = XCreateGC (dpy, shadowPixmap, 0, 0);
|
|
if (!gc)
|
|
{
|
|
XDestroyImage (shadowImage);
|
|
XFreePixmap (dpy, shadowPixmap);
|
|
XRenderFreePicture (dpy, shadowPicture);
|
|
return None;
|
|
}
|
|
|
|
XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0,
|
|
shadowImage->width,
|
|
shadowImage->height);
|
|
*wp = shadowImage->width;
|
|
*hp = shadowImage->height;
|
|
XFreeGC (dpy, gc);
|
|
XDestroyImage (shadowImage);
|
|
XFreePixmap (dpy, shadowPixmap);
|
|
return shadowPicture;
|
|
}
|
|
|
|
Picture
|
|
solid_picture (Display *dpy, Bool argb, double a, double r, double g, double b)
|
|
{
|
|
Pixmap pixmap;
|
|
Picture picture;
|
|
XRenderPictureAttributes pa;
|
|
XRenderColor c;
|
|
|
|
pixmap = XCreatePixmap (dpy, root, 1, 1, argb ? 32 : 8);
|
|
if (!pixmap)
|
|
return None;
|
|
|
|
pa.repeat = True;
|
|
picture = XRenderCreatePicture (dpy, pixmap,
|
|
XRenderFindStandardFormat (dpy, argb ? PictStandardARGB32 : PictStandardA8),
|
|
CPRepeat,
|
|
&pa);
|
|
if (!picture)
|
|
{
|
|
XFreePixmap (dpy, pixmap);
|
|
return None;
|
|
}
|
|
|
|
c.alpha = a * 0xffff;
|
|
c.red = r * 0xffff;
|
|
c.green = g * 0xffff;
|
|
c.blue = b * 0xffff;
|
|
XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 0, 0, 1, 1);
|
|
XFreePixmap (dpy, pixmap);
|
|
return picture;
|
|
}
|
|
|
|
void
|
|
discard_ignore (Display *dpy, unsigned long sequence)
|
|
{
|
|
while (ignore_head)
|
|
{
|
|
if ((long) (sequence - ignore_head->sequence) > 0)
|
|
{
|
|
ignore *next = ignore_head->next;
|
|
free (ignore_head);
|
|
ignore_head = next;
|
|
if (!ignore_head)
|
|
ignore_tail = &ignore_head;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
set_ignore (Display *dpy, unsigned long sequence)
|
|
{
|
|
ignore *i = malloc (sizeof (ignore));
|
|
if (!i)
|
|
return;
|
|
i->sequence = sequence;
|
|
i->next = 0;
|
|
*ignore_tail = i;
|
|
ignore_tail = &i->next;
|
|
}
|
|
|
|
int
|
|
should_ignore (Display *dpy, unsigned long sequence)
|
|
{
|
|
discard_ignore (dpy, sequence);
|
|
return ignore_head && ignore_head->sequence == sequence;
|
|
}
|
|
|
|
static win *
|
|
find_win (Display *dpy, Window id)
|
|
{
|
|
win *w;
|
|
|
|
for (w = list; w; w = w->next)
|
|
if (w->id == id)
|
|
return w;
|
|
return 0;
|
|
}
|
|
|
|
static char *backgroundProps[] = {
|
|
"_XROOTPMAP_ID",
|
|
"_XSETROOT_ID",
|
|
0,
|
|
};
|
|
|
|
static Picture
|
|
root_tile (Display *dpy)
|
|
{
|
|
Picture picture;
|
|
Atom actual_type;
|
|
Pixmap pixmap;
|
|
int actual_format;
|
|
unsigned long nitems;
|
|
unsigned long bytes_after;
|
|
unsigned char *prop;
|
|
Bool fill;
|
|
XRenderPictureAttributes pa;
|
|
int p;
|
|
|
|
pixmap = None;
|
|
for (p = 0; backgroundProps[p]; p++)
|
|
{
|
|
if (XGetWindowProperty (dpy, root, XInternAtom (dpy, backgroundProps[p], False),
|
|
0, 4, False, AnyPropertyType,
|
|
&actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success &&
|
|
actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1)
|
|
{
|
|
memcpy (&pixmap, prop, 4);
|
|
XFree (prop);
|
|
fill = False;
|
|
break;
|
|
}
|
|
}
|
|
if (!pixmap)
|
|
{
|
|
pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr));
|
|
fill = True;
|
|
}
|
|
pa.repeat = True;
|
|
picture = XRenderCreatePicture (dpy, pixmap,
|
|
sXRenderFindVisualFormat (dpy,
|
|
DefaultVisual (dpy, scr)),
|
|
CPRepeat, &pa);
|
|
if (fill)
|
|
{
|
|
XRenderColor c;
|
|
|
|
c.red = c.green = c.blue = 0x8080;
|
|
c.alpha = 0xffff;
|
|
XRenderFillRectangle (dpy, PictOpSrc, picture, &c,
|
|
0, 0, 1, 1);
|
|
}
|
|
return picture;
|
|
}
|
|
|
|
static void
|
|
paint_root (Display *dpy)
|
|
{
|
|
if (!rootTile)
|
|
rootTile = root_tile (dpy);
|
|
|
|
XRenderComposite (dpy, PictOpSrc,
|
|
rootTile, None, rootBuffer,
|
|
0, 0, 0, 0, 0, 0, root_width, root_height);
|
|
}
|
|
|
|
static XserverRegion
|
|
win_extents (Display *dpy, win *w)
|
|
{
|
|
XRectangle r;
|
|
|
|
r.x = w->a.x;
|
|
r.y = w->a.y;
|
|
r.width = w->a.width + w->a.border_width * 2;
|
|
r.height = w->a.height + w->a.border_width * 2;
|
|
if (compMode != CompSimple && w->shadowSize > 0 && !(w->windowType == winDockAtom && excludeDockShadows))
|
|
{
|
|
if (compMode == CompServerShadows || w->mode != WINDOW_ARGB)
|
|
{
|
|
XRectangle sr;
|
|
|
|
if (compMode == CompServerShadows)
|
|
{
|
|
w->shadow_dx = 2;
|
|
w->shadow_dy = 7;
|
|
w->shadow_width = w->a.width;
|
|
w->shadow_height = w->a.height;
|
|
}
|
|
else
|
|
{
|
|
w->shadow_dx = SHADOW_OFFSET_X;
|
|
w->shadow_dx = w->shadow_dx / 100;
|
|
w->shadow_dy = SHADOW_OFFSET_Y;
|
|
w->shadow_dy = w->shadow_dy / 100;
|
|
if (!w->shadow)
|
|
{
|
|
double opacity = shadowOpacity;
|
|
if (w->shadowSize > 100)
|
|
opacity = opacity/(w->shadowSize*0.01);
|
|
if (w->mode == WINDOW_TRANS)
|
|
opacity = opacity * ((double)w->opacity)/((double)OPAQUE);
|
|
w->shadow = shadow_picture (dpy, opacity, w->alphaPict,
|
|
w->a.width + w->a.border_width * 2 - 2*(shadowRadius - (w->shadowSize*shadowRadius/100)) ,
|
|
w->a.height + w->a.border_width * 2 - 2*(shadowRadius - (w->shadowSize*shadowRadius/100)),
|
|
&w->shadow_width, &w->shadow_height);
|
|
}
|
|
}
|
|
sr.x = w->a.x + w->shadow_dx;
|
|
sr.y = w->a.y + w->shadow_dy;
|
|
sr.width = w->shadow_width;
|
|
sr.height = w->shadow_height;
|
|
if (sr.x < r.x)
|
|
{
|
|
r.width = (r.x + r.width) - sr.x;
|
|
r.x = sr.x;
|
|
}
|
|
if (sr.y < r.y)
|
|
{
|
|
r.height = (r.y + r.height) - sr.y;
|
|
r.y = sr.y;
|
|
}
|
|
if (sr.x + sr.width > r.x + r.width)
|
|
r.width = sr.x + sr.width - r.x;
|
|
if (sr.y + sr.height > r.y + r.height)
|
|
r.height = sr.y + sr.height - r.y;
|
|
}
|
|
}
|
|
return XFixesCreateRegion (dpy, &r, 1);
|
|
}
|
|
|
|
static XserverRegion
|
|
border_size (Display *dpy, win *w)
|
|
{
|
|
XserverRegion border;
|
|
/*
|
|
* if window doesn't exist anymore, this will generate an error
|
|
* as well as not generate a region. Perhaps a better XFixes
|
|
* architecture would be to have a request that copies instead
|
|
* of creates, that way you'd just end up with an empty region
|
|
* instead of an invalid XID.
|
|
*/
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding);
|
|
/* translate this */
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XFixesTranslateRegion (dpy, border,
|
|
w->a.x + w->a.border_width,
|
|
w->a.y + w->a.border_width);
|
|
return border;
|
|
}
|
|
|
|
static void
|
|
paint_all (Display *dpy, XserverRegion region)
|
|
{
|
|
win *w;
|
|
win *t = 0;
|
|
|
|
if (!region)
|
|
{
|
|
XRectangle r;
|
|
r.x = 0;
|
|
r.y = 0;
|
|
r.width = root_width;
|
|
r.height = root_height;
|
|
region = XFixesCreateRegion (dpy, &r, 1);
|
|
}
|
|
#if MONITOR_REPAINT
|
|
rootBuffer = rootPicture;
|
|
#else
|
|
if (!rootBuffer)
|
|
{
|
|
Pixmap rootPixmap = XCreatePixmap (dpy, root, root_width, root_height,
|
|
DefaultDepth (dpy, scr));
|
|
rootBuffer = XRenderCreatePicture (dpy, rootPixmap,
|
|
sXRenderFindVisualFormat (dpy,
|
|
DefaultVisual (dpy, scr)),
|
|
0, 0);
|
|
XFreePixmap (dpy, rootPixmap);
|
|
}
|
|
#endif
|
|
XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region);
|
|
#if MONITOR_REPAINT
|
|
XRenderComposite (dpy, PictOpSrc, blackPicture, None, rootPicture,
|
|
0, 0, 0, 0, 0, 0, root_width, root_height);
|
|
#endif
|
|
#if DEBUG_REPAINT
|
|
printf ("paint:");
|
|
#endif
|
|
for (w = list; w; w = w->next)
|
|
{
|
|
#if CAN_DO_USABLE
|
|
if (!w->usable)
|
|
continue;
|
|
#endif
|
|
/* never painted, ignore it */
|
|
if (!w->damaged)
|
|
continue;
|
|
|
|
/* skip invisible windows */
|
|
if (w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= root_width || w->a.y >= root_height)
|
|
continue;
|
|
|
|
if (!w->picture)
|
|
{
|
|
XRenderPictureAttributes pa;
|
|
XRenderPictFormat *format;
|
|
Drawable draw = w->id;
|
|
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
if (hasNamePixmap && !w->pixmap)
|
|
w->pixmap = XCompositeNameWindowPixmap (dpy, w->id);
|
|
if (w->pixmap)
|
|
draw = w->pixmap;
|
|
#endif
|
|
format = sXRenderFindVisualFormat (dpy, w->a.visual);
|
|
pa.subwindow_mode = IncludeInferiors;
|
|
w->picture = XRenderCreatePicture (dpy, draw,
|
|
format,
|
|
CPSubwindowMode,
|
|
&pa);
|
|
}
|
|
#if DEBUG_REPAINT
|
|
printf (" 0x%x", w->id);
|
|
#endif
|
|
if (clipChanged)
|
|
{
|
|
if (w->borderSize)
|
|
{
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XFixesDestroyRegion (dpy, w->borderSize);
|
|
w->borderSize = None;
|
|
}
|
|
if (w->extents)
|
|
{
|
|
XFixesDestroyRegion (dpy, w->extents);
|
|
w->extents = None;
|
|
}
|
|
if (w->borderClip)
|
|
{
|
|
XFixesDestroyRegion (dpy, w->borderClip);
|
|
w->borderClip = None;
|
|
}
|
|
}
|
|
if (!w->borderSize)
|
|
w->borderSize = border_size (dpy, w);
|
|
if (!w->extents)
|
|
w->extents = win_extents (dpy, w);
|
|
if (w->mode == WINDOW_SOLID)
|
|
{
|
|
int x, y, wid, hei;
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
x = w->a.x;
|
|
y = w->a.y;
|
|
wid = w->a.width + w->a.border_width * 2;
|
|
hei = w->a.height + w->a.border_width * 2;
|
|
#else
|
|
x = w->a.x + w->a.border_width;
|
|
y = w->a.y + w->a.border_width;
|
|
wid = w->a.width;
|
|
hei = w->a.height;
|
|
#endif
|
|
XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XFixesSubtractRegion (dpy, region, region, w->borderSize);
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer,
|
|
0, 0, 0, 0,
|
|
x, y, wid, hei);
|
|
}
|
|
if (!w->borderClip)
|
|
{
|
|
w->borderClip = XFixesCreateRegion (dpy, 0, 0);
|
|
XFixesCopyRegion (dpy, w->borderClip, region);
|
|
}
|
|
w->prev_trans = t;
|
|
t = w;
|
|
}
|
|
#if DEBUG_REPAINT
|
|
printf ("\n");
|
|
fflush (stdout);
|
|
#endif
|
|
XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region);
|
|
paint_root (dpy);
|
|
for (w = t; w; w = w->prev_trans)
|
|
{
|
|
/*skip desktop */
|
|
if (w->windowType == winDesktopAtom)
|
|
continue;
|
|
|
|
if (w->shadowSize > 0){
|
|
switch (compMode) {
|
|
case CompSimple:
|
|
break;
|
|
case CompServerShadows:
|
|
XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
if (w->opacity != OPAQUE && !w->shadowPict)
|
|
w->shadowPict = solid_picture (dpy, True,
|
|
(double) w->opacity / OPAQUE * 0.3,
|
|
0, 0, 0);
|
|
XRenderComposite (dpy, PictOpOver,
|
|
w->shadowPict ? w->shadowPict : transBlackPicture,
|
|
w->picture, rootBuffer,
|
|
0, 0, 0, 0,
|
|
w->a.x + w->shadow_dx,
|
|
w->a.y + w->shadow_dy,
|
|
w->shadow_width, w->shadow_height);
|
|
break;
|
|
case CompClientShadows:
|
|
if (w->shadow && w->windowType != winDesktopAtom)
|
|
{
|
|
XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
|
|
XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer,
|
|
0, 0, 0, 0,
|
|
w->a.x + w->shadow_dx,
|
|
w->a.y + w->shadow_dy,
|
|
w->shadow_width, w->shadow_height);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (w->opacity != OPAQUE && !w->alphaPict)
|
|
w->alphaPict = solid_picture (dpy, False,
|
|
(double) w->opacity / OPAQUE, shadowColor.red, shadowColor.green, shadowColor.blue);
|
|
if (w->mode == WINDOW_TRANS)
|
|
{
|
|
int x, y, wid, hei;
|
|
XFixesIntersectRegion (dpy, w->borderClip, w->borderClip, w->borderSize);
|
|
XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
x = w->a.x;
|
|
y = w->a.y;
|
|
wid = w->a.width + w->a.border_width * 2;
|
|
hei = w->a.height + w->a.border_width * 2;
|
|
#else
|
|
x = w->a.x + w->a.border_width;
|
|
y = w->a.y + w->a.border_width;
|
|
wid = w->a.width;
|
|
hei = w->a.height;
|
|
#endif
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer,
|
|
0, 0, 0, 0,
|
|
x, y, wid, hei);
|
|
}
|
|
else if (w->mode == WINDOW_ARGB)
|
|
{
|
|
int x, y, wid, hei;
|
|
XFixesIntersectRegion (dpy, w->borderClip, w->borderClip, w->borderSize);
|
|
XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip);
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
x = w->a.x;
|
|
y = w->a.y;
|
|
wid = w->a.width + w->a.border_width * 2;
|
|
hei = w->a.height + w->a.border_width * 2;
|
|
#else
|
|
x = w->a.x + w->a.border_width;
|
|
y = w->a.y + w->a.border_width;
|
|
wid = w->a.width;
|
|
hei = w->a.height;
|
|
#endif
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer,
|
|
0, 0, 0, 0,
|
|
x, y, wid, hei);
|
|
}
|
|
XFixesDestroyRegion (dpy, w->borderClip);
|
|
w->borderClip = None;
|
|
}
|
|
XFixesDestroyRegion (dpy, region);
|
|
if (rootBuffer != rootPicture)
|
|
{
|
|
#if 0
|
|
XTransform t;
|
|
t.matrix[0][0] = XDoubleToFixed (3.0 /*/ scale*/);
|
|
t.matrix[0][1] = 0.0;
|
|
t.matrix[0][2] = 0.0;
|
|
|
|
t.matrix[1][0] = 0.0;
|
|
t.matrix[1][1] = XDoubleToFixed (1.0 /*/ scale*/);
|
|
t.matrix[1][2] = 0.0;
|
|
|
|
t.matrix[2][0] = 0.0;
|
|
t.matrix[2][1] = 0.0;
|
|
t.matrix[2][2] = XDoubleToFixed (1.0);
|
|
|
|
XRenderSetPictureTransform (dpy, rootBuffer, &t);
|
|
#endif
|
|
XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None);
|
|
XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture,
|
|
0, 0, 0, 0, 0, 0, root_width, root_height);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_damage (Display *dpy, XserverRegion damage)
|
|
{
|
|
if (allDamage)
|
|
{
|
|
XFixesUnionRegion (dpy, allDamage, allDamage, damage);
|
|
XFixesDestroyRegion (dpy, damage);
|
|
}
|
|
else
|
|
allDamage = damage;
|
|
}
|
|
|
|
static void
|
|
repair_win (Display *dpy, win *w)
|
|
{
|
|
XserverRegion parts;
|
|
|
|
if (!w->damaged)
|
|
{
|
|
parts = win_extents (dpy, w);
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XDamageSubtract (dpy, w->damage, None, None);
|
|
}
|
|
else
|
|
{
|
|
XserverRegion o;
|
|
parts = XFixesCreateRegion (dpy, 0, 0);
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XDamageSubtract (dpy, w->damage, None, parts);
|
|
XFixesTranslateRegion (dpy, parts,
|
|
w->a.x + w->a.border_width,
|
|
w->a.y + w->a.border_width);
|
|
if (compMode == CompServerShadows)
|
|
{
|
|
o = XFixesCreateRegion (dpy, 0, 0);
|
|
XFixesCopyRegion (dpy, o, parts);
|
|
XFixesTranslateRegion (dpy, o, w->shadow_dx, w->shadow_dy);
|
|
XFixesUnionRegion (dpy, parts, parts, o);
|
|
XFixesDestroyRegion (dpy, o);
|
|
}
|
|
}
|
|
add_damage (dpy, parts);
|
|
w->damaged = 1;
|
|
}
|
|
|
|
static void
|
|
map_win (Display *dpy, Window id, unsigned long sequence, Bool fade)
|
|
{
|
|
win *w = find_win (dpy, id);
|
|
Drawable back;
|
|
|
|
if (!w)
|
|
return;
|
|
|
|
w->a.map_state = IsViewable;
|
|
|
|
/* This needs to be here or else we lose transparency messages */
|
|
XSelectInput (dpy, id, PropertyChangeMask);
|
|
|
|
#if CAN_DO_USABLE
|
|
w->damage_bounds.x = w->damage_bounds.y = 0;
|
|
w->damage_bounds.width = w->damage_bounds.height = 0;
|
|
#endif
|
|
w->damaged = 0;
|
|
|
|
if (fade && fadeWindows)
|
|
set_fade (dpy, w, 0, get_opacity_percent (dpy, w, 1.0), fade_in_step, 0, False, True, True);
|
|
}
|
|
|
|
static void
|
|
finish_unmap_win (Display *dpy, win *w)
|
|
{
|
|
w->damaged = 0;
|
|
#if CAN_DO_USABLE
|
|
w->usable = False;
|
|
#endif
|
|
if (w->extents != None)
|
|
{
|
|
add_damage (dpy, w->extents); /* destroys region */
|
|
w->extents = None;
|
|
}
|
|
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
if (w->pixmap)
|
|
{
|
|
XFreePixmap (dpy, w->pixmap);
|
|
w->pixmap = None;
|
|
}
|
|
#endif
|
|
|
|
if (w->picture)
|
|
{
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XRenderFreePicture (dpy, w->picture);
|
|
w->picture = None;
|
|
}
|
|
|
|
/* don't care about properties anymore */
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XSelectInput(dpy, w->id, 0);
|
|
|
|
if (w->borderSize)
|
|
{
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XFixesDestroyRegion (dpy, w->borderSize);
|
|
w->borderSize = None;
|
|
}
|
|
if (w->shadow)
|
|
{
|
|
XRenderFreePicture (dpy, w->shadow);
|
|
w->shadow = None;
|
|
}
|
|
if (w->borderClip)
|
|
{
|
|
XFixesDestroyRegion (dpy, w->borderClip);
|
|
w->borderClip = None;
|
|
}
|
|
|
|
clipChanged = True;
|
|
}
|
|
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
static void
|
|
unmap_callback (Display *dpy, win *w, Bool gone)
|
|
{
|
|
finish_unmap_win (dpy, w);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
unmap_win (Display *dpy, Window id, Bool fade)
|
|
{
|
|
win *w = find_win (dpy, id);
|
|
if (!w)
|
|
return;
|
|
w->a.map_state = IsUnmapped;
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
if (w->pixmap && fade && fadeWindows)
|
|
set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, unmap_callback, False, False, True);
|
|
else
|
|
#endif
|
|
finish_unmap_win (dpy, w);
|
|
}
|
|
|
|
/* Get the opacity prop from window
|
|
not found: default
|
|
otherwise the value
|
|
*/
|
|
static unsigned int
|
|
get_opacity_prop(Display *dpy, win *w, unsigned int def)
|
|
{
|
|
Atom actual;
|
|
int format;
|
|
unsigned long n, left;
|
|
|
|
unsigned char *data;
|
|
int result = XGetWindowProperty(dpy, w->id, opacityAtom, 0L, 1L, False,
|
|
XA_CARDINAL, &actual, &format,
|
|
&n, &left, &data);
|
|
if (result == Success && data != NULL)
|
|
{
|
|
unsigned int i;
|
|
memcpy (&i, data, sizeof (unsigned int));
|
|
XFree( (void *) data);
|
|
return i;
|
|
}
|
|
return def;
|
|
}
|
|
|
|
static unsigned int
|
|
get_shadow_prop(Display *dpy, win *w)
|
|
{
|
|
Atom actual;
|
|
int format;
|
|
unsigned long n, left;
|
|
|
|
unsigned char *data = NULL;
|
|
int result = XGetWindowProperty(dpy, w->id, shadowAtom, 0L, 1L, False,
|
|
XA_CARDINAL, &actual, &format,
|
|
&n, &left, &data);
|
|
if (result == Success && data != NULL)
|
|
{
|
|
unsigned int i;
|
|
memcpy (&i, data, sizeof (unsigned int));
|
|
XFree( (void *) data);
|
|
/*i added this for security reaons but limiting a value to 200% is somewhat indiscriminate
|
|
if (i > 200)
|
|
return 200;
|
|
else*/
|
|
return i;
|
|
}
|
|
return 100;
|
|
}
|
|
|
|
static unsigned int
|
|
get_shade_prop(Display *dpy, win *w)
|
|
{
|
|
Atom actual;
|
|
int format;
|
|
unsigned long n, left;
|
|
|
|
unsigned char *data = NULL;
|
|
int result = XGetWindowProperty(dpy, w->id, shadeAtom, 0L, 1L, False,
|
|
XA_CARDINAL, &actual, &format,
|
|
&n, &left, &data);
|
|
if (result == Success && data != NULL)
|
|
{
|
|
unsigned int i;
|
|
memcpy (&i, data, sizeof (unsigned int));
|
|
XFree( (void *) data);
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Get the opacity property from the window in a percent format
|
|
not found: default
|
|
otherwise: the value
|
|
*/
|
|
static double
|
|
get_opacity_percent(Display *dpy, win *w, double def)
|
|
{
|
|
if (w && w->isInFade)
|
|
{
|
|
fade *f = find_fade(w);
|
|
return f->finish;
|
|
}
|
|
else
|
|
{
|
|
unsigned int opacity = get_opacity_prop (dpy, w, (unsigned int)(OPAQUE*def));
|
|
return opacity*1.0/OPAQUE;
|
|
}
|
|
}
|
|
|
|
/* determine mode for window all in one place.
|
|
Future might check for menu flag and other cool things
|
|
*/
|
|
|
|
static Atom
|
|
get_wintype_prop(Display * dpy, Window w)
|
|
{
|
|
Atom actual;
|
|
int format;
|
|
unsigned long n, left;
|
|
|
|
unsigned char *data;
|
|
int result = XGetWindowProperty (dpy, w, winTypeAtom, 0L, 1L, False,
|
|
XA_ATOM, &actual, &format,
|
|
&n, &left, &data);
|
|
|
|
if (result == Success && data != None)
|
|
{
|
|
Atom a;
|
|
memcpy (&a, data, sizeof (Atom));
|
|
XFree ( (void *) data);
|
|
return a;
|
|
}
|
|
return winNormalAtom;
|
|
}
|
|
|
|
static void
|
|
determine_mode(Display *dpy, win *w)
|
|
{
|
|
int mode;
|
|
XRenderPictFormat *format;
|
|
unsigned int default_opacity;
|
|
|
|
/* if trans prop == -1 fall back on previous tests*/
|
|
|
|
if (w->alphaPict)
|
|
{
|
|
XRenderFreePicture (dpy, w->alphaPict);
|
|
w->alphaPict = None;
|
|
}
|
|
if (w->shadowPict)
|
|
{
|
|
XRenderFreePicture (dpy, w->shadowPict);
|
|
w->shadowPict = None;
|
|
}
|
|
|
|
if (w->a.class == InputOnly)
|
|
{
|
|
format = 0;
|
|
}
|
|
else
|
|
{
|
|
format = XRenderFindVisualFormat (dpy, w->a.visual);
|
|
}
|
|
|
|
if (format && format->type == PictTypeDirect && format->direct.alphaMask)
|
|
{
|
|
mode = WINDOW_ARGB;
|
|
}
|
|
else if (w->opacity != OPAQUE)
|
|
{
|
|
mode = WINDOW_TRANS;
|
|
}
|
|
else
|
|
{
|
|
mode = WINDOW_SOLID;
|
|
}
|
|
w->mode = mode;
|
|
if (w->extents)
|
|
{
|
|
XserverRegion damage;
|
|
damage = XFixesCreateRegion (dpy, 0, 0);
|
|
XFixesCopyRegion (dpy, damage, w->extents);
|
|
add_damage (dpy, damage);
|
|
}
|
|
}
|
|
|
|
static Atom
|
|
determine_wintype (Display *dpy, Window w)
|
|
{
|
|
Window root_return, parent_return;
|
|
Window *children = NULL;
|
|
unsigned int nchildren, i;
|
|
Atom type;
|
|
|
|
type = get_wintype_prop (dpy, w);
|
|
if (type != winNormalAtom)
|
|
return type;
|
|
|
|
if (!XQueryTree (dpy, w, &root_return, &parent_return, &children,
|
|
&nchildren))
|
|
{
|
|
/* XQueryTree failed. */
|
|
if (children)
|
|
XFree ((void *)children);
|
|
return winNormalAtom;
|
|
}
|
|
|
|
for (i = 0;i < nchildren;i++)
|
|
{
|
|
type = determine_wintype (dpy, children[i]);
|
|
if (type != winNormalAtom)
|
|
return type;
|
|
}
|
|
|
|
if (children)
|
|
XFree ((void *)children);
|
|
|
|
return winNormalAtom;
|
|
}
|
|
|
|
static void
|
|
add_win (Display *dpy, Window id, Window prev)
|
|
{
|
|
win *new = malloc (sizeof (win));
|
|
win **p;
|
|
|
|
if (!new)
|
|
return;
|
|
if (prev)
|
|
{
|
|
for (p = &list; *p; p = &(*p)->next)
|
|
if ((*p)->id == prev)
|
|
break;
|
|
}
|
|
else
|
|
p = &list;
|
|
new->id = id;
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
if (!XGetWindowAttributes (dpy, id, &new->a))
|
|
{
|
|
free (new);
|
|
return;
|
|
}
|
|
new->damaged = 0;
|
|
#if CAN_DO_USABLE
|
|
new->usable = False;
|
|
#endif
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
new->pixmap = None;
|
|
#endif
|
|
new->picture = None;
|
|
if (new->a.class == InputOnly)
|
|
{
|
|
new->damage_sequence = 0;
|
|
new->damage = None;
|
|
}
|
|
else
|
|
{
|
|
new->damage_sequence = NextRequest (dpy);
|
|
new->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty);
|
|
}
|
|
new->isInFade = False;
|
|
new->alphaPict = None;
|
|
new->shadowPict = None;
|
|
new->borderSize = None;
|
|
new->extents = None;
|
|
new->shadow = None;
|
|
new->shadow_dx = 0;
|
|
new->shadow_dy = 0;
|
|
new->shadow_width = 0;
|
|
new->shadow_height = 0;
|
|
new->opacity = OPAQUE;
|
|
new->shadowSize = 100;
|
|
|
|
new->borderClip = None;
|
|
new->prev_trans = 0;
|
|
|
|
/* moved mode setting to one place */
|
|
new->opacity = get_opacity_prop (dpy, new, OPAQUE);
|
|
new->shadowSize = get_shadow_prop (dpy, new);
|
|
new->windowType = determine_wintype (dpy, new->id);
|
|
determine_mode (dpy, new);
|
|
|
|
new->next = *p;
|
|
*p = new;
|
|
if (new->a.map_state == IsViewable)
|
|
map_win (dpy, id, new->damage_sequence - 1, True);
|
|
}
|
|
|
|
void
|
|
restack_win (Display *dpy, win *w, Window new_above)
|
|
{
|
|
Window old_above;
|
|
|
|
if (w->next)
|
|
old_above = w->next->id;
|
|
else
|
|
old_above = None;
|
|
if (old_above != new_above)
|
|
{
|
|
win **prev;
|
|
|
|
/* unhook */
|
|
for (prev = &list; *prev; prev = &(*prev)->next)
|
|
if ((*prev) == w)
|
|
break;
|
|
*prev = w->next;
|
|
|
|
/* rehook */
|
|
for (prev = &list; *prev; prev = &(*prev)->next)
|
|
{
|
|
if ((*prev)->id == new_above)
|
|
break;
|
|
}
|
|
w->next = *prev;
|
|
*prev = w;
|
|
}
|
|
}
|
|
|
|
static void
|
|
configure_win (Display *dpy, XConfigureEvent *ce)
|
|
{
|
|
win *w = find_win (dpy, ce->window);
|
|
Window above;
|
|
XserverRegion damage = None;
|
|
|
|
if (!w)
|
|
{
|
|
if (ce->window == root)
|
|
{
|
|
if (rootBuffer)
|
|
{
|
|
XRenderFreePicture (dpy, rootBuffer);
|
|
rootBuffer = None;
|
|
}
|
|
root_width = ce->width;
|
|
root_height = ce->height;
|
|
}
|
|
return;
|
|
}
|
|
#if CAN_DO_USABLE
|
|
if (w->usable)
|
|
#endif
|
|
{
|
|
damage = XFixesCreateRegion (dpy, 0, 0);
|
|
if (w->extents != None)
|
|
XFixesCopyRegion (dpy, damage, w->extents);
|
|
}
|
|
w->a.x = ce->x;
|
|
w->a.y = ce->y;
|
|
if (w->a.width != ce->width || w->a.height != ce->height)
|
|
{
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
if (w->pixmap)
|
|
{
|
|
XFreePixmap (dpy, w->pixmap);
|
|
w->pixmap = None;
|
|
if (w->picture)
|
|
{
|
|
XRenderFreePicture (dpy, w->picture);
|
|
w->picture = None;
|
|
}
|
|
}
|
|
#endif
|
|
if (w->shadow)
|
|
{
|
|
XRenderFreePicture (dpy, w->shadow);
|
|
w->shadow = None;
|
|
}
|
|
}
|
|
w->a.width = ce->width;
|
|
w->a.height = ce->height;
|
|
w->a.border_width = ce->border_width;
|
|
w->a.override_redirect = ce->override_redirect;
|
|
restack_win (dpy, w, ce->above);
|
|
if (damage)
|
|
{
|
|
XserverRegion extents = win_extents (dpy, w);
|
|
XFixesUnionRegion (dpy, damage, damage, extents);
|
|
XFixesDestroyRegion (dpy, extents);
|
|
add_damage (dpy, damage);
|
|
}
|
|
clipChanged = True;
|
|
}
|
|
|
|
static void
|
|
circulate_win (Display *dpy, XCirculateEvent *ce)
|
|
{
|
|
win *w = find_win (dpy, ce->window);
|
|
Window new_above;
|
|
|
|
if (!w)
|
|
return;
|
|
|
|
if (ce->place == PlaceOnTop)
|
|
new_above = list->id;
|
|
else
|
|
new_above = None;
|
|
restack_win (dpy, w, new_above);
|
|
clipChanged = True;
|
|
}
|
|
|
|
static void
|
|
finish_destroy_win (Display *dpy, Window id, Bool gone)
|
|
{
|
|
win **prev, *w;
|
|
|
|
for (prev = &list; (w = *prev); prev = &w->next)
|
|
if (w->id == id)
|
|
{
|
|
if (!gone)
|
|
finish_unmap_win (dpy, w);
|
|
*prev = w->next;
|
|
if (w->picture)
|
|
{
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XRenderFreePicture (dpy, w->picture);
|
|
w->picture = None;
|
|
}
|
|
if (w->alphaPict)
|
|
{
|
|
XRenderFreePicture (dpy, w->alphaPict);
|
|
w->alphaPict = None;
|
|
}
|
|
if (w->shadowPict)
|
|
{
|
|
XRenderFreePicture (dpy, w->shadowPict);
|
|
w->shadowPict = None;
|
|
}
|
|
if (w->damage != None)
|
|
{
|
|
set_ignore (dpy, NextRequest (dpy));
|
|
XDamageDestroy (dpy, w->damage);
|
|
w->damage = None;
|
|
}
|
|
cleanup_fade (dpy, w);
|
|
free (w);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
static void
|
|
destroy_callback (Display *dpy, win *w, Bool gone)
|
|
{
|
|
finish_destroy_win (dpy, w->id, gone);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
destroy_win (Display *dpy, Window id, Bool gone, Bool fade)
|
|
{
|
|
win *w = find_win (dpy, id);
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
if (w && w->pixmap && fade && fadeWindows)
|
|
set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, destroy_callback, gone, False, True);
|
|
else
|
|
#endif
|
|
{
|
|
finish_destroy_win (dpy, id, gone);
|
|
}
|
|
}
|
|
|
|
/*
|
|
static void
|
|
dump_win (win *w)
|
|
{
|
|
printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id,
|
|
w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width);
|
|
}
|
|
|
|
|
|
static void
|
|
dump_wins (void)
|
|
{
|
|
win *w;
|
|
|
|
printf ("windows:\n");
|
|
for (w = list; w; w = w->next)
|
|
dump_win (w);
|
|
}
|
|
*/
|
|
|
|
static void
|
|
damage_win (Display *dpy, XDamageNotifyEvent *de)
|
|
{
|
|
win *w = find_win (dpy, de->drawable);
|
|
|
|
if (!w)
|
|
return;
|
|
#if CAN_DO_USABLE
|
|
if (!w->usable)
|
|
{
|
|
if (w->damage_bounds.width == 0 || w->damage_bounds.height == 0)
|
|
{
|
|
w->damage_bounds = de->area;
|
|
}
|
|
else
|
|
{
|
|
if (de->area.x < w->damage_bounds.x)
|
|
{
|
|
w->damage_bounds.width += (w->damage_bounds.x - de->area.x);
|
|
w->damage_bounds.x = de->area.x;
|
|
}
|
|
if (de->area.y < w->damage_bounds.y)
|
|
{
|
|
w->damage_bounds.height += (w->damage_bounds.y - de->area.y);
|
|
w->damage_bounds.y = de->area.y;
|
|
}
|
|
if (de->area.x + de->area.width > w->damage_bounds.x + w->damage_bounds.width)
|
|
w->damage_bounds.width = de->area.x + de->area.width - w->damage_bounds.x;
|
|
if (de->area.y + de->area.height > w->damage_bounds.y + w->damage_bounds.height)
|
|
w->damage_bounds.height = de->area.y + de->area.height - w->damage_bounds.y;
|
|
}
|
|
#if 0
|
|
printf ("unusable damage %d, %d: %d x %d bounds %d, %d: %d x %d\n",
|
|
de->area.x,
|
|
de->area.y,
|
|
de->area.width,
|
|
de->area.height,
|
|
w->damage_bounds.x,
|
|
w->damage_bounds.y,
|
|
w->damage_bounds.width,
|
|
w->damage_bounds.height);
|
|
#endif
|
|
if (w->damage_bounds.x <= 0 &&
|
|
w->damage_bounds.y <= 0 &&
|
|
w->a.width <= w->damage_bounds.x + w->damage_bounds.width &&
|
|
w->a.height <= w->damage_bounds.y + w->damage_bounds.height)
|
|
{
|
|
clipChanged = True;
|
|
if (fadeWindows)
|
|
set_fade (dpy, w, 0, get_opacity_percent (dpy, w, 1.0), fade_in_step, 0, False, True, True);
|
|
w->usable = True;
|
|
}
|
|
}
|
|
if (w->usable)
|
|
#endif
|
|
repair_win (dpy, w);
|
|
}
|
|
|
|
static int
|
|
error (Display *dpy, XErrorEvent *ev)
|
|
{
|
|
int o;
|
|
char *name = 0;
|
|
|
|
if (should_ignore (dpy, ev->serial))
|
|
return 0;
|
|
|
|
if (ev->request_code == composite_opcode &&
|
|
ev->minor_code == X_CompositeRedirectSubwindows)
|
|
{
|
|
fprintf (stderr, "Another composite manager is already running\n");
|
|
exit (1);
|
|
}
|
|
|
|
o = ev->error_code - xfixes_error;
|
|
switch (o) {
|
|
case BadRegion: name = "BadRegion"; break;
|
|
default: break;
|
|
}
|
|
o = ev->error_code - damage_error;
|
|
switch (o) {
|
|
case BadDamage: name = "BadDamage"; break;
|
|
default: break;
|
|
}
|
|
o = ev->error_code - render_error;
|
|
switch (o) {
|
|
case BadPictFormat: name ="BadPictFormat"; break;
|
|
case BadPicture: name ="BadPicture"; break;
|
|
case BadPictOp: name ="BadPictOp"; break;
|
|
case BadGlyphSet: name ="BadGlyphSet"; break;
|
|
case BadGlyph: name ="BadGlyph"; break;
|
|
default: break;
|
|
}
|
|
|
|
printf ("error %d request %d minor %d serial %d\n",
|
|
ev->error_code, ev->request_code, ev->minor_code, ev->serial);
|
|
|
|
/* abort (); this is just annoying to most people */
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
expose_root (Display *dpy, Window root, XRectangle *rects, int nrects)
|
|
{
|
|
XserverRegion region = XFixesCreateRegion (dpy, rects, nrects);
|
|
|
|
add_damage (dpy, region);
|
|
}
|
|
|
|
|
|
static int
|
|
ev_serial (XEvent *ev)
|
|
{
|
|
if (ev->type & 0x7f != KeymapNotify)
|
|
return ev->xany.serial;
|
|
return NextRequest (ev->xany.display);
|
|
}
|
|
|
|
|
|
static char *
|
|
ev_name (XEvent *ev)
|
|
{
|
|
static char buf[128];
|
|
switch (ev->type & 0x7f) {
|
|
case Expose:
|
|
return "Expose";
|
|
case MapNotify:
|
|
return "Map";
|
|
case UnmapNotify:
|
|
return "Unmap";
|
|
case ReparentNotify:
|
|
return "Reparent";
|
|
case CirculateNotify:
|
|
return "Circulate";
|
|
default:
|
|
if (ev->type == damage_event + XDamageNotify)
|
|
return "Damage";
|
|
sprintf (buf, "Event %d", ev->type);
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
static Window
|
|
ev_window (XEvent *ev)
|
|
{
|
|
switch (ev->type) {
|
|
case Expose:
|
|
return ev->xexpose.window;
|
|
case MapNotify:
|
|
return ev->xmap.window;
|
|
case UnmapNotify:
|
|
return ev->xunmap.window;
|
|
case ReparentNotify:
|
|
return ev->xreparent.window;
|
|
case CirculateNotify:
|
|
return ev->xcirculate.window;
|
|
default:
|
|
if (ev->type == damage_event + XDamageNotify){
|
|
fprintf(stderr, "%d", ev->type);
|
|
return ((XDamageNotifyEvent *) ev)->drawable;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
setShadowColor(char *value){ /*format nach #xxxxxx (html) ändern?*/
|
|
unsigned int tmp;
|
|
char **res = NULL;
|
|
tmp = strtoul(value, res, 16);
|
|
if( !value || strlen(value) < 6 || strlen(value) > 8 || (*(value+1) == 'x' && strlen(value) < 8) || res != NULL ){
|
|
shadowColor.red = 0;
|
|
shadowColor.green = 0;
|
|
shadowColor.blue = 0;
|
|
printf("wrong hexadecimal (use 0xXXXXXX or XXXXXX)! defaulting to black...\n");
|
|
return;
|
|
}
|
|
shadowColor.blue = tmp&0xff;
|
|
tmp >>= 8;
|
|
shadowColor.green = tmp&0xff;
|
|
tmp >>= 8;
|
|
shadowColor.red = tmp&0xff;
|
|
}
|
|
|
|
typedef enum _option{
|
|
Display_=0,
|
|
Compmode,
|
|
ExcludeDockShadows,
|
|
FadeWindows,
|
|
FadeTrans,
|
|
AutoRedirect,
|
|
Synchronize,
|
|
ShadowColor,
|
|
ShadowRadius,
|
|
ShadowOpacity,
|
|
ShadowOffsetX,
|
|
ShadowOffsetY,
|
|
FadeOutStep,
|
|
FadeInStep,
|
|
FadeDelta,
|
|
NUMBEROFOPTIONS
|
|
} Option;
|
|
|
|
char *
|
|
options[NUMBEROFOPTIONS] = {
|
|
"Display", /*0*/
|
|
"Compmode", /*1*/
|
|
"ExcludeDockShadows", /*2*/
|
|
"FadeWindows", /*3*/
|
|
"FadeTrans", /*4*/
|
|
"AutoRedirect", /*5*/
|
|
"Synchronize", /*6*/
|
|
"ShadowColor", /*7*/
|
|
"ShadowRadius", /*8*/
|
|
"ShadowOpacity", /*9*/
|
|
"ShadowOffsetX", /*10*/
|
|
"ShadowOffsetY", /*11*/
|
|
"FadeOutStep", /*12*/
|
|
"FadeInStep", /*13*/
|
|
"FadeDelta", /*14*/
|
|
/*put your thingy in here...*/
|
|
};
|
|
|
|
void
|
|
setValue(Option option, char *value ){
|
|
switch(option){ /*please keep that upside-down, because this way adding a new option is easier (all in one view)*/
|
|
|
|
case FadeDelta:
|
|
fade_delta = atoi(value);
|
|
if (fade_delta < 1)
|
|
fade_delta = 10;
|
|
printf("config: using fade_delta: \t\t%d\n", fade_delta);
|
|
break;
|
|
case FadeInStep:
|
|
fade_in_step = atof(value);
|
|
if (fade_in_step <= 0)
|
|
fade_in_step = 0.01;
|
|
printf("config: using fade_in_step: \t\t%f\n", fade_in_step);
|
|
break;
|
|
case FadeOutStep:
|
|
fade_out_step = atof(value);
|
|
if (fade_out_step <= 0)
|
|
fade_out_step = 0.01;
|
|
printf("config: using fade_out_step: \t\t%f\n", fade_out_step);
|
|
break;
|
|
case ShadowOffsetY:
|
|
shadowOffsetY = atoi(value);
|
|
printf("config: using shadowOffsetY: \t\t%d\n", shadowOffsetY);
|
|
break;
|
|
case ShadowOffsetX:
|
|
shadowOffsetX = atoi(value);
|
|
printf("config: using shadowOffsetX: \t\t%d\n", shadowOffsetX);
|
|
break;
|
|
case ShadowOpacity:
|
|
shadowOpacity = atof(value);
|
|
printf("config: using shadowOpacity: \t\t%f\n", shadowOpacity);
|
|
break;
|
|
case ShadowRadius:
|
|
shadowRadius = atoi(value);
|
|
printf("config: using shadowRadius: \t\t%d\n", shadowRadius);
|
|
break;
|
|
case ShadowColor:
|
|
setShadowColor(value);
|
|
break;
|
|
case Synchronize:
|
|
if( strcasecmp(value, "true") == 0 ){
|
|
synchronize = True;
|
|
printf("config: using synchronize: \t\tTrue\n");
|
|
}
|
|
else{
|
|
synchronize = False;
|
|
printf("config: using synchronize: \t\tFalse\n");
|
|
}
|
|
break;
|
|
case AutoRedirect:
|
|
if( strcasecmp(value, "true") == 0 ){
|
|
autoRedirect = True;
|
|
printf("config: using autoredirect: \t\tTrue\n");
|
|
}
|
|
else{
|
|
autoRedirect = False;
|
|
printf("config: using autoredirect: \t\tFalse\n");
|
|
}
|
|
break;
|
|
case FadeTrans:
|
|
if( strcasecmp(value, "true") == 0 ){
|
|
fadeTrans = True;
|
|
printf("config: using fadetrans: \t\tTrue\n");
|
|
}
|
|
else{
|
|
fadeTrans = False;
|
|
printf("config: using fadetrans: \t\tFalse\n");
|
|
}
|
|
break;
|
|
case FadeWindows:
|
|
if( strcasecmp(value, "true") == 0 ){
|
|
fadeWindows = True;
|
|
printf("config: using fadewindows: \t\tTrue\n");
|
|
}
|
|
else{
|
|
fadeWindows = False;
|
|
printf("config: using fadewindows: \t\tFalse\n");
|
|
}
|
|
break;
|
|
case ExcludeDockShadows:
|
|
if( strcasecmp(value, "true") == 0 ){
|
|
excludeDockShadows = True;
|
|
printf("config: using excludeDockShadows: \tTrue\n");
|
|
}
|
|
else{
|
|
excludeDockShadows = False;
|
|
printf("config: using excludeDockShadows: \tFalse\n");
|
|
}
|
|
break;
|
|
case Compmode:
|
|
if( strcasecmp(value, "CompClientShadows") == 0 ){
|
|
compMode = CompClientShadows;
|
|
printf("config: using compMode: \t\tCompClientShadows\n");
|
|
}
|
|
else if( strcasecmp(value, "CompServerShadows") == 0 ){
|
|
compMode = CompServerShadows;
|
|
printf("config: using compMode: \t\tCompServerShadows\n");
|
|
}
|
|
else{
|
|
compMode = CompSimple; /*default*/
|
|
printf("config: using compMode: \t\tCompSimple\n");
|
|
}
|
|
break;
|
|
case Display_:
|
|
display = strdup(value);
|
|
printf("config: using display: \t\t\t%s\n", display);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
setParameter(char *line){
|
|
char *name = strtok(line, "=");
|
|
char *value = line+strlen(name)+1;
|
|
Option i;
|
|
for(i=Display_; i < NUMBEROFOPTIONS; i++){
|
|
if( strcasecmp(name, *(options+i) ) == 0 ){
|
|
setValue(i, value);
|
|
name = value = NULL;
|
|
return 1;
|
|
}
|
|
}
|
|
printf("ignored unknown option: <%s>\n", name);
|
|
name = value = NULL;
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
loadConfig(char *filename){
|
|
FILE *file = NULL;
|
|
char line[ 1024 ];
|
|
size_t length = 0;
|
|
Bool wasNull = False;
|
|
Bool section = False;
|
|
|
|
if( filename == NULL ){
|
|
wasNull = True;
|
|
const char *home = getenv("HOME");
|
|
const char *configfile = "/.xcompmgrrc";
|
|
filename = (char*)malloc((strlen(home)+strlen(configfile))*sizeof(char));
|
|
|
|
strcat(filename, home);
|
|
strcat(filename, configfile);
|
|
}
|
|
|
|
printf("trying '%s' as configfile\n\n", filename);
|
|
|
|
if( (file = fopen(filename, "r")) == NULL ){
|
|
printf("failed to open config file. does it exist?\n");
|
|
if( wasNull ){
|
|
free(filename);
|
|
filename = NULL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*find section*/
|
|
while( !section && fgets(line, 1023, file) != -1 ){
|
|
if( strcmp(line, "[xcompmgr]\n") == 0 )
|
|
section = True;
|
|
}
|
|
/*read and set values*/
|
|
while( section && fgets(line, 1023, file) != NULL ){
|
|
int ret = strlen( line );
|
|
if( ret > 1 ){
|
|
if( *line == '[' )/*found new section - maybe check for '\n'?*/
|
|
break;
|
|
*(line+ret-1) = '\0';
|
|
setParameter(line);
|
|
}
|
|
}
|
|
printf("\nfinished parsing the config file\n");
|
|
fclose(file);
|
|
if( wasNull ){
|
|
free(filename);
|
|
filename = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
usage (char *program)
|
|
{
|
|
fprintf (stderr, "%s v1.0\n", program);
|
|
fprintf (stderr, "usage: %s [options]\n", program);
|
|
fprintf (stderr, "Options\n");
|
|
fprintf (stderr, " -d display\n Specifies which display should be managed.\n");
|
|
fprintf (stderr, " -r radius\n Specifies the blur radius for client-side shadows. (default 12)\n");
|
|
fprintf (stderr, " -o opacity\n Specifies the translucency for client-side shadows. (default .75)\n");
|
|
fprintf (stderr, " -l left-offset\n Specifies the left offset for client-side shadows. (default -15)\n");
|
|
fprintf (stderr, " -t top-offset\n Specifies the top offset for clinet-side shadows. (default -15)\n");
|
|
fprintf (stderr, " -I fade-in-step\n Specifies the opacity change between steps while fading in. (default 0.028)\n");
|
|
fprintf (stderr, " -O fade-out-step\n Specifies the opacity change between steps while fading out. (default 0.03)\n");
|
|
fprintf (stderr, " -D fade-delta-time\n Specifies the time between steps in a fade in milliseconds. (default 10)\n");
|
|
fprintf (stderr, " -a\n Use automatic server-side compositing. Faster, but no special effects.\n");
|
|
fprintf (stderr, " -c\n Draw client-side shadows with fuzzy edges.\n");
|
|
fprintf (stderr, " -C\n Avoid drawing shadows on dock/panel windows.\n");
|
|
fprintf (stderr, " -f\n Fade windows in/out when opening/closing.\n");
|
|
fprintf (stderr, " -F\n Fade windows during opacity changes.\n");
|
|
fprintf (stderr, " -n\n Normal client-side compositing with transparency support\n");
|
|
fprintf (stderr, " -s\n Draw server-side shadows with sharp edges.\n");
|
|
fprintf (stderr, " -S\n Enable synchronous operation (for debugging).\n");
|
|
fprintf (stderr, " -x [0x]XXXXXX\n Choose Custom Color in hex format\n");
|
|
fprintf (stderr, " -v\n Print version Number and exit\n");
|
|
fprintf (stderr, " -h\n Print this help\n");
|
|
exit (1);
|
|
}
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
XEvent ev;
|
|
Window root_return, parent_return;
|
|
Window *children;
|
|
Pixmap transPixmap;
|
|
Pixmap blackPixmap;
|
|
unsigned int nchildren;
|
|
int i;
|
|
XRenderPictureAttributes pa;
|
|
XRenderColor c;
|
|
XRectangle *expose_rects = 0;
|
|
int size_expose = 0;
|
|
int n_expose = 0;
|
|
struct pollfd ufd;
|
|
int n;
|
|
int last_update;
|
|
int now;
|
|
int p;
|
|
int composite_major, composite_minor;
|
|
|
|
int o;
|
|
|
|
shadowColor.red = 0;
|
|
shadowColor.green = 0;
|
|
shadowColor.blue = 0;
|
|
|
|
loadConfig(NULL); /*we do that before cmdline-parsing, so config-values can be overridden*/
|
|
/*used for shadow colors*/
|
|
char **res = NULL;
|
|
while ((o = getopt (argc, argv, "D:I:O:d:r:o:l:t:scnfFCaSx:vh")) != -1)
|
|
{
|
|
switch (o) {
|
|
case 'd':
|
|
display = optarg;
|
|
break;
|
|
case 'D':
|
|
fade_delta = atoi (optarg);
|
|
if (fade_delta < 1)
|
|
fade_delta = 10;
|
|
break;
|
|
case 'I':
|
|
fade_in_step = atof (optarg);
|
|
if (fade_in_step <= 0)
|
|
fade_in_step = 0.01;
|
|
break;
|
|
case 'O':
|
|
fade_out_step = atof (optarg);
|
|
if (fade_out_step <= 0)
|
|
fade_out_step = 0.01;
|
|
break;
|
|
case 's':
|
|
compMode = CompServerShadows;
|
|
break;
|
|
case 'c':
|
|
compMode = CompClientShadows;
|
|
break;
|
|
case 'C':
|
|
excludeDockShadows = True;
|
|
break;
|
|
case 'n':
|
|
compMode = CompSimple;
|
|
break;
|
|
case 'f':
|
|
fadeWindows = True;
|
|
break;
|
|
case 'F':
|
|
fadeTrans = True;
|
|
break;
|
|
case 'a':
|
|
autoRedirect = True;
|
|
break;
|
|
case 'S':
|
|
synchronize = True;
|
|
break;
|
|
case 'r':
|
|
shadowRadius = atoi (optarg);
|
|
break;
|
|
case 'o':
|
|
shadowOpacity = atof (optarg);
|
|
break;
|
|
case 'l':
|
|
shadowOffsetX = atoi (optarg);
|
|
break;
|
|
case 't':
|
|
shadowOffsetY = atoi (optarg);
|
|
break;
|
|
case 'x':
|
|
if( compMode != CompClientShadows ){
|
|
fprintf(stderr, "sorry, but we need ClientShadows (-c) for coloring to work properly!\ndefaulting to black...\n");
|
|
break;
|
|
}
|
|
setShadowColor(optarg);
|
|
break;
|
|
case 'v': fprintf (stderr, "%s v%-3.2f\n", argv[0], _VERSION_); exit (0);
|
|
case 'h':
|
|
default:
|
|
usage (argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
dpy = XOpenDisplay (display);
|
|
if (!dpy)
|
|
{
|
|
fprintf (stderr, "Can't open display\n");
|
|
exit (1);
|
|
}
|
|
XSetErrorHandler (error);
|
|
if (synchronize)
|
|
XSynchronize (dpy, 1);
|
|
scr = DefaultScreen (dpy);
|
|
root = RootWindow (dpy, scr);
|
|
|
|
if (!XRenderQueryExtension (dpy, &render_event, &render_error))
|
|
{
|
|
fprintf (stderr, "No render extension\n");
|
|
exit (1);
|
|
}
|
|
if (!XQueryExtension (dpy, COMPOSITE_NAME, &composite_opcode,
|
|
&composite_event, &composite_error))
|
|
{
|
|
fprintf (stderr, "No composite extension\n");
|
|
exit (1);
|
|
}
|
|
XCompositeQueryVersion (dpy, &composite_major, &composite_minor);
|
|
#if HAS_NAME_WINDOW_PIXMAP
|
|
if (composite_major > 0 || composite_minor >= 2)
|
|
hasNamePixmap = True;
|
|
#endif
|
|
|
|
if (!XDamageQueryExtension (dpy, &damage_event, &damage_error))
|
|
{
|
|
fprintf (stderr, "No damage extension\n");
|
|
exit (1);
|
|
}
|
|
if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error))
|
|
{
|
|
fprintf (stderr, "No XFixes extension\n");
|
|
exit (1);
|
|
}
|
|
|
|
fprintf(stderr, "Started");
|
|
/* get atoms */
|
|
shadowAtom = XInternAtom (dpy, SHADOW_PROP, False);
|
|
opacityAtom = XInternAtom (dpy, OPACITY_PROP, False);
|
|
shadeAtom = XInternAtom (dpy, SHADE_PROP, False);
|
|
winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", False);
|
|
winDesktopAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
|
|
winDockAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
|
|
winToolbarAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
|
|
winMenuAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", False);
|
|
winUtilAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False);
|
|
winSplashAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False);
|
|
winDialogAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
|
|
winNormalAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
|
|
|
|
pa.subwindow_mode = IncludeInferiors;
|
|
|
|
if (compMode == CompClientShadows)
|
|
{
|
|
gaussianMap = make_gaussian_map(dpy, shadowRadius);
|
|
presum_gaussian (gaussianMap);
|
|
}
|
|
|
|
root_width = DisplayWidth (dpy, scr);
|
|
root_height = DisplayHeight (dpy, scr);
|
|
|
|
rootPicture = XRenderCreatePicture (dpy, root,
|
|
sXRenderFindVisualFormat (dpy,
|
|
DefaultVisual (dpy, scr)),
|
|
CPSubwindowMode,
|
|
&pa);
|
|
blackPicture = solid_picture (dpy, True, 1, (double)(shadowColor.red)/0xff, (double)(shadowColor.green)/0xff, (double)(shadowColor.blue)/0xff);
|
|
if (compMode == CompServerShadows)
|
|
transBlackPicture = solid_picture (dpy, True, 0.3, 0, 0, 0);
|
|
allDamage = None;
|
|
clipChanged = True;
|
|
XGrabServer (dpy);
|
|
if (autoRedirect)
|
|
XCompositeRedirectSubwindows (dpy, root, CompositeRedirectAutomatic);
|
|
else
|
|
{
|
|
XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual);
|
|
XSelectInput (dpy, root,
|
|
SubstructureNotifyMask|
|
|
ExposureMask|
|
|
StructureNotifyMask|
|
|
PropertyChangeMask);
|
|
XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren);
|
|
for (i = 0; i < nchildren; i++)
|
|
add_win (dpy, children[i], i ? children[i-1] : None);
|
|
XFree (children);
|
|
}
|
|
XUngrabServer (dpy);
|
|
ufd.fd = ConnectionNumber (dpy);
|
|
ufd.events = POLLIN;
|
|
if (!autoRedirect)
|
|
paint_all (dpy, None);
|
|
for (;;)
|
|
{
|
|
/* dump_wins (); */
|
|
do {
|
|
if (autoRedirect)
|
|
XFlush (dpy);
|
|
if (!QLength (dpy))
|
|
{
|
|
if (poll (&ufd, 1, fade_timeout()) == 0)
|
|
{
|
|
run_fades (dpy);
|
|
break;
|
|
}
|
|
}
|
|
|
|
XNextEvent (dpy, &ev);
|
|
if (ev.type & 0x7f != KeymapNotify)
|
|
discard_ignore (dpy, ev.xany.serial);
|
|
#if DEBUG_EVENTS
|
|
printf ("event %10.10s serial 0x%08x window 0x%08x\n",
|
|
ev_name(&ev), ev_serial (&ev), ev_window (&ev));
|
|
#endif
|
|
if (!autoRedirect) switch (ev.type) {
|
|
case CreateNotify:
|
|
add_win (dpy, ev.xcreatewindow.window, 0);
|
|
break;
|
|
case ConfigureNotify:
|
|
configure_win (dpy, &ev.xconfigure);
|
|
break;
|
|
case DestroyNotify:
|
|
destroy_win (dpy, ev.xdestroywindow.window, True, True);
|
|
break;
|
|
case MapNotify:
|
|
map_win (dpy, ev.xmap.window, ev.xmap.serial, True);
|
|
break;
|
|
case UnmapNotify:
|
|
unmap_win (dpy, ev.xunmap.window, True);
|
|
break;
|
|
case ReparentNotify:
|
|
if (ev.xreparent.parent == root)
|
|
add_win (dpy, ev.xreparent.window, 0);
|
|
else
|
|
destroy_win (dpy, ev.xreparent.window, False, True);
|
|
break;
|
|
case CirculateNotify:
|
|
circulate_win (dpy, &ev.xcirculate);
|
|
break;
|
|
case Expose:
|
|
if (ev.xexpose.window == root)
|
|
{
|
|
int more = ev.xexpose.count + 1;
|
|
if (n_expose == size_expose)
|
|
{
|
|
if (expose_rects)
|
|
{
|
|
expose_rects = realloc (expose_rects,
|
|
(size_expose + more) *
|
|
sizeof (XRectangle));
|
|
size_expose += more;
|
|
}
|
|
else
|
|
{
|
|
expose_rects = malloc (more * sizeof (XRectangle));
|
|
size_expose = more;
|
|
}
|
|
}
|
|
expose_rects[n_expose].x = ev.xexpose.x;
|
|
expose_rects[n_expose].y = ev.xexpose.y;
|
|
expose_rects[n_expose].width = ev.xexpose.width;
|
|
expose_rects[n_expose].height = ev.xexpose.height;
|
|
n_expose++;
|
|
if (ev.xexpose.count == 0)
|
|
{
|
|
expose_root (dpy, root, expose_rects, n_expose);
|
|
n_expose = 0;
|
|
}
|
|
}
|
|
break;
|
|
case PropertyNotify:
|
|
for (p = 0; backgroundProps[p]; p++)
|
|
{
|
|
if (ev.xproperty.atom == XInternAtom (dpy, backgroundProps[p], False))
|
|
{
|
|
if (rootTile)
|
|
{
|
|
XClearArea (dpy, root, 0, 0, 0, 0, True);
|
|
XRenderFreePicture (dpy, rootTile);
|
|
rootTile = None;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* Window set shade? */
|
|
if (ev.xproperty.atom == shadeAtom)
|
|
{
|
|
win * w = find_win(dpy, ev.xproperty.window);
|
|
unsigned int tmp = get_shade_prop(dpy, w);
|
|
if (tmp)
|
|
{
|
|
if (tmp == 1)
|
|
{
|
|
w->preShadeOpacity = w->opacity;
|
|
w->opacity = 0;
|
|
determine_mode(dpy, w);
|
|
}
|
|
else if (tmp == 2)
|
|
{
|
|
w->opacity = w->preShadeOpacity;
|
|
determine_mode(dpy, w);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
/* check if Trans or Shadow property was changed */
|
|
else if (ev.xproperty.atom == opacityAtom || ev.xproperty.atom == shadowAtom)
|
|
{
|
|
/* reset mode and redraw window */
|
|
win * w = find_win(dpy, ev.xproperty.window);
|
|
unsigned int oldShadowSize = w->shadowSize;
|
|
unsigned int tmp;
|
|
if (w)
|
|
{
|
|
if (ev.xproperty.atom == opacityAtom)
|
|
{
|
|
tmp = get_opacity_prop(dpy, w, OPAQUE);
|
|
if (tmp == w->opacity)
|
|
break; /*skip if opacity does not change*/
|
|
if (fadeTrans)
|
|
{
|
|
set_fade (dpy, w, w->opacity*1.0/OPAQUE, (tmp*1.0)/OPAQUE,
|
|
fade_out_step, 0, False, True, False);
|
|
break;
|
|
}
|
|
else
|
|
w->opacity = tmp;
|
|
}
|
|
else
|
|
{
|
|
tmp = get_shadow_prop(dpy, w);
|
|
if (tmp == w->shadowSize)
|
|
break; /*skip if shadow does not change*/
|
|
w->shadowSize = tmp;
|
|
}
|
|
if (w->shadow)
|
|
{
|
|
XRenderFreePicture (dpy, w->shadow);
|
|
w->shadow = None;
|
|
}
|
|
if (oldShadowSize < w->shadowSize) /* this is important to catch size changes on cleanup with determine_mode*/
|
|
{
|
|
w->extents = win_extents (dpy, w);
|
|
determine_mode(dpy, w);
|
|
}
|
|
else
|
|
{
|
|
determine_mode(dpy, w);
|
|
w->extents = win_extents (dpy, w);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (ev.type == damage_event + XDamageNotify)
|
|
damage_win (dpy, (XDamageNotifyEvent *) &ev);
|
|
break;
|
|
}
|
|
} while (QLength (dpy));
|
|
if (allDamage && !autoRedirect)
|
|
{
|
|
static int paint;
|
|
paint_all (dpy, allDamage);
|
|
paint++;
|
|
XSync (dpy, False);
|
|
allDamage = None;
|
|
clipChanged = False;
|
|
}
|
|
}
|
|
}
|