From d634640199edfe68c57eb7bb2b1260f7c7fb61b3 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 27 Feb 2012 01:42:38 -0600 Subject: reorganize tree --- compton.c | 2604 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ compton.h | 355 +++++++++ 2 files changed, 2959 insertions(+) create mode 100644 compton.c create mode 100644 compton.h diff --git a/compton.c b/compton.c new file mode 100644 index 000000000..2b33575d5 --- /dev/null +++ b/compton.c @@ -0,0 +1,2604 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "compton.h" + +/** + * Shared + */ + +win *list; +fade *fades; +Display *dpy; +int scr; + +Window root; +Picture root_picture; +Picture root_buffer; +Picture black_picture; +Picture root_tile; +XserverRegion all_damage; +Bool clip_changed; +#if HAS_NAME_WINDOW_PIXMAP +Bool has_name_pixmap; +#endif +int root_height, root_width; + +/* errors */ +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; +int composite_opcode; + +/* shadows */ +conv *gaussian_map; + +/* for shadow precomputation */ +int Gsize = -1; +unsigned char *shadow_corner = NULL; +unsigned char *shadow_top = NULL; + +/* for root tile */ +static const char *background_props[] = { + "_XROOTPMAP_ID", + "_XSETROOT_ID", + 0, +}; + +/* for expose events */ +XRectangle *expose_rects = 0; +int size_expose = 0; +int n_expose = 0; + +/* atoms */ +Atom extents_atom; +Atom opacity_atom; +Atom win_type_atom; +Atom win_type[NUM_WINTYPES]; +double win_type_opacity[NUM_WINTYPES]; +Bool win_type_shadow[NUM_WINTYPES]; +Bool win_type_fade[NUM_WINTYPES]; + +/** + * Macros + */ + +#define INACTIVE_OPACITY \ +(unsigned long)((double)inactive_opacity * OPAQUE) + +#define IS_NORMAL_WIN(w) \ +((w) && ((w)->window_type == WINTYPE_NORMAL \ + || (w)->window_type == WINTYPE_UTILITY)) + +#define HAS_FRAME_OPACITY(w) (frame_opacity && (w)->top_width) + +/** + * Options + */ + +int shadow_radius = 12; +int shadow_offset_x = -15; +int shadow_offset_y = -15; +double shadow_opacity = .75; + +double fade_in_step = 0.028; +double fade_out_step = 0.03; +int fade_delta = 10; +int fade_time = 0; +Bool fade_trans = False; + +Bool clear_shadow = False; + +double inactive_opacity = 0; +double frame_opacity = 0; + +Bool synchronize = False; + +/** + * Fades + */ + +static int +get_time_in_milliseconds() { + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static fade * +find_fade(win *w) { + fade *f; + + for (f = fades; f; f = f->next) { + if (f->w == w) return f; + } + + return 0; +} + +static void +dequeue_fade(Display *dpy, fade *f) { + fade **prev; + + for (prev = &fades; *prev; prev = &(*prev)->next) { + if (*prev == f) { + *prev = f->next; + if (f->callback) { + (*f->callback)(dpy, f->w); + } + free(f); + break; + } + } +} + +static void +cleanup_fade(Display *dpy, win *w) { + fade *f = find_fade (w); + if (f) { + dequeue_fade(dpy, f); + } +} + +static void +enqueue_fade(Display *dpy, fade *f) { + 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 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 && f->callback) { + (*f->callback)(dpy, f->w); + } + } + + 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; + w->opacity = f->cur * OPAQUE; + + determine_mode(dpy, w); + + if (w->shadow) { + XRenderFreePicture(dpy, w->shadow); + w->shadow = None; + + if (w->extents != None) { + XFixesDestroyRegion(dpy, w->extents); + } + + /* rebuild the shadow */ + w->extents = win_extents(dpy, w); + } + + /* fading windows need to be drawn, mark + them as damaged. when a window maps, + if it tries to fade in but it already + at the right opacity (map/unmap/map fast) + then it will never get drawn without this + until it repaints */ + w->damaged = 1; +} + +static 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; + + return delta; +} + +static void +run_fades(Display *dpy) { + int now = get_time_in_milliseconds(); + fade *next = fades; + int steps; + Bool need_dequeue; + + if (fade_time - now > 0) return; + steps = 1 + (now - fade_time) / fade_delta; + + while (next) { + fade *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; + } + + w->opacity = f->cur * OPAQUE; + need_dequeue = False; + if (f->step > 0) { + if (f->cur >= f->finish) { + w->opacity = f->finish * OPAQUE; + need_dequeue = True; + } + } else { + if (f->cur <= f->finish) { + w->opacity = f->finish * OPAQUE; + need_dequeue = True; + } + } + + determine_mode(dpy, w); + + if (w->shadow) { + XRenderFreePicture(dpy, w->shadow); + w->shadow = None; + + if (w->extents != None) { + XFixesDestroyRegion(dpy, w->extents); + } + + /* rebuild the shadow */ + w->extents = win_extents(dpy, w); + } + + /* Must do this last as it might + destroy f->w in callbacks */ + if (need_dequeue) dequeue_fade(dpy, f); + } + + fade_time = now + fade_delta; +} + +/** + * Shadows + */ + +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; + } + } + + 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 (shadow_corner) free((void *)shadow_corner); + if (shadow_top) free((void *)shadow_top); + + shadow_corner = (unsigned char *)(malloc((Gsize + 1) * (Gsize + 1) * 26)); + shadow_top = (unsigned char *)(malloc((Gsize + 1) * 26)); + + for (x = 0; x <= Gsize; x++) { + shadow_top[25 * (Gsize + 1) + x] = + sum_gaussian(map, 1, x - center, center, Gsize * 2, Gsize * 2); + + for (opacity = 0; opacity < 25; opacity++) { + shadow_top[opacity * (Gsize + 1) + x] = + shadow_top[25 * (Gsize + 1) + x] * opacity / 25; + } + + for (y = 0; y <= x; y++) { + shadow_corner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] + = sum_gaussian(map, 1, x - center, y - center, Gsize * 2, Gsize * 2); + shadow_corner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] + = shadow_corner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; + + for (opacity = 0; opacity < 25; opacity++) { + shadow_corner[opacity * (Gsize + 1) * (Gsize + 1) + + y * (Gsize + 1) + x] + = shadow_corner[opacity * (Gsize + 1) * (Gsize + 1) + + x * (Gsize + 1) + y] + = shadow_corner[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 = gaussian_map->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 (!clear_shadow) { + if (Gsize > 0) { + d = shadow_top[opacity_int * (Gsize + 1) + Gsize]; + } else { + d = sum_gaussian(gaussian_map, + opacity, center, center, width, height); + } + + memset(data, d, sheight * swidth); + } else { + // zero the pixmap + memset(data, 0, 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 = shadow_corner[opacity_int * (Gsize + 1) * (Gsize + 1) + + y * (Gsize + 1) + x]; + } else { + d = sum_gaussian(gaussian_map, + 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 = shadow_top[opacity_int * (Gsize + 1) + y]; + } else { + d = sum_gaussian(gaussian_map, + 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 = shadow_top[opacity_int * (Gsize + 1) + x]; + } else { + d = sum_gaussian(gaussian_map, + 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; + } + } + + // zero extra pixels + if (clear_shadow && width > gsize && height > gsize) { + int r = gsize / 2; + int sr = r - 2; + int er = r + 4; + for (y = sr; y < (sheight - er); y++) { + for (x = sr; x < (swidth - er); x++) { + data[y * swidth + x] = 0; + } + } + } + + 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; + Picture shadow_picture; + 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; + } + + shadow_picture = XRenderCreatePicture(dpy, shadowPixmap, + XRenderFindStandardFormat(dpy, PictStandardA8), 0, 0); + + if (!shadow_picture) { + XDestroyImage(shadowImage); + XFreePixmap(dpy, shadowPixmap); + return None; + } + + gc = XCreateGC(dpy, shadowPixmap, 0, 0); + if (!gc) { + XDestroyImage(shadowImage); + XFreePixmap(dpy, shadowPixmap); + XRenderFreePicture(dpy, shadow_picture); + 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 shadow_picture; +} + +static 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; +} + +/** + * Errors + */ + +static 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; + } + } +} + +static 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; +} + +static int +should_ignore(Display *dpy, unsigned long sequence) { + discard_ignore(dpy, sequence); + return ignore_head && ignore_head->sequence == sequence; +} + +/** + * Windows + */ + +static win * +find_win(Display *dpy, Window id) { + win *w; + + for (w = list; w; w = w->next) { + if (w->id == id && !w->destroyed) + return w; + } + + return 0; +} + +static win * +find_toplevel(Display *dpy, Window id) { + win *w; + + for (w = list; w; w = w->next) { + if (w->client_win == id && !w->destroyed) + return w; + } + + return 0; +} + +static Picture +root_tile_f(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; background_props[p]; p++) { + if (XGetWindowProperty(dpy, root, + XInternAtom(dpy, background_props[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, XRenderFindVisualFormat(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 (!root_tile) { + root_tile = root_tile_f(dpy); + } + + XRenderComposite( + dpy, PictOpSrc, root_tile, None, + root_buffer, 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 (win_type_shadow[w->window_type]) { + XRectangle sr; + + w->shadow_dx = shadow_offset_x; + w->shadow_dy = shadow_offset_y; + + if (!w->shadow) { + double opacity = shadow_opacity; + + if (!clear_shadow) { + if (w->mode != WINDOW_SOLID) { + opacity = opacity * ((double)w->opacity) / ((double)OPAQUE); + } + + if (HAS_FRAME_OPACITY(w)) { + opacity = opacity * frame_opacity; + } + } + + w->shadow = shadow_picture( + dpy, opacity, w->alpha_pict, + w->a.width + w->a.border_width * 2, + w->a.height + w->a.border_width * 2, + &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 Window +find_client_win(Display *dpy, Window win) { + Atom WM_STATE = XInternAtom(dpy, "WM_STATE", False); + + Window root, parent; + Window *children; + unsigned int nchildren; + unsigned int i; + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + Window client = 0; + + XGetWindowProperty( + dpy, win, WM_STATE, 0, 0, False, + AnyPropertyType, &type, &format, &nitems, + &after, &data); + + if (type) return win; + + if (!XQueryTree(dpy, win, &root, + &parent, &children, &nchildren)) { + return 0; + } + + for (i = 0; i < nchildren; i++) { + client = find_client_win(dpy, children[i]); + if (client) break; + } + + if (children) XFree((char *)children); + + return client; +} + +static void +get_frame_extents(Display *dpy, Window w, + unsigned int *left, + unsigned int *right, + unsigned int *top, + unsigned int *bottom) { + long *extents; + Atom type; + int format; + unsigned long nitems, after; + unsigned char *data = NULL; + int result; + + *left = 0; + *right = 0; + *top = 0; + *bottom = 0; + + // w = find_client_win(dpy, w); + if (!w) return; + + result = XGetWindowProperty( + dpy, w, XInternAtom(dpy, "_NET_FRAME_EXTENTS", False), + 0L, 4L, False, AnyPropertyType, + &type, &format, &nitems, &after, + (unsigned char **)&data); + + if (result == Success) { + if (nitems == 4 && after == 0) { + extents = (long *)data; + *left = + (unsigned int)extents[0]; + *right = + (unsigned int)extents[1]; + *top = + (unsigned int)extents[2]; + *bottom = + (unsigned int)extents[3]; + } + XFree(data); + } +} + +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 + root_buffer = root_picture; +#else + if (!root_buffer) { + Pixmap rootPixmap = XCreatePixmap( + dpy, root, root_width, root_height, + DefaultDepth(dpy, scr)); + + root_buffer = XRenderCreatePicture(dpy, rootPixmap, + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + 0, 0); + + XFreePixmap(dpy, rootPixmap); + } +#endif + + XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); + +#if MONITOR_REPAINT + XRenderComposite( + dpy, PictOpSrc, black_picture, None, + root_picture, 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; + + /* if invisible, ignore it */ + 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 (has_name_pixmap && !w->pixmap) { + set_ignore(dpy, NextRequest(dpy)); + w->pixmap = XCompositeNameWindowPixmap(dpy, w->id); + } + if (w->pixmap) draw = w->pixmap; +#endif + + format = XRenderFindVisualFormat(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 (clip_changed) { + if (w->border_size) { + set_ignore(dpy, NextRequest(dpy)); + XFixesDestroyRegion(dpy, w->border_size); + w->border_size = None; + } + if (w->extents) { + XFixesDestroyRegion(dpy, w->extents); + w->extents = None; + } + if (w->border_clip) { + XFixesDestroyRegion(dpy, w->border_clip); + w->border_clip = None; + } + } + + if (!w->border_size) { + w->border_size = border_size (dpy, w); + } + + if (!w->extents) { + w->extents = win_extents(dpy, w); + } + + if (w->mode == WINDOW_SOLID && !HAS_FRAME_OPACITY(w)) { + 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, root_buffer, 0, 0, region); + set_ignore(dpy, NextRequest(dpy)); + + XFixesSubtractRegion(dpy, region, region, w->border_size); + set_ignore(dpy, NextRequest(dpy)); + + XRenderComposite( + dpy, PictOpSrc, w->picture, + None, root_buffer, 0, 0, 0, 0, + x, y, wid, hei); + } + + if (!w->border_clip) { + w->border_clip = XFixesCreateRegion(dpy, 0, 0); + XFixesCopyRegion(dpy, w->border_clip, region); + } + + w->prev_trans = t; + t = w; + } + +#if DEBUG_REPAINT + printf("\n"); + fflush(stdout); +#endif + + XFixesSetPictureClipRegion(dpy, + root_buffer, 0, 0, region); + paint_root(dpy); + + for (w = t; w; w = w->prev_trans) { + XFixesSetPictureClipRegion(dpy, + root_buffer, 0, 0, w->border_clip); + + if (win_type_shadow[w->window_type]) { + XRenderComposite( + dpy, PictOpOver, black_picture, w->shadow, + root_buffer, 0, 0, 0, 0, + w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, + w->shadow_width, w->shadow_height); + } + + if (w->opacity != OPAQUE && !w->alpha_pict) { + w->alpha_pict = solid_picture( + dpy, False, (double)w->opacity / OPAQUE, 0, 0, 0); + } + if (HAS_FRAME_OPACITY(w) && !w->alpha_border_pict) { + w->alpha_border_pict = solid_picture( + dpy, False, frame_opacity, 0, 0, 0); + } + + if (w->mode != WINDOW_SOLID || HAS_FRAME_OPACITY(w)) { + 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 + + set_ignore(dpy, NextRequest(dpy)); + + if (!HAS_FRAME_OPACITY(w)) { + XRenderComposite( + dpy, PictOpOver, w->picture, w->alpha_pict, + root_buffer, 0, 0, 0, 0, x, y, wid, hei); + } else { + unsigned int t = w->top_width; + unsigned int l = w->left_width; + unsigned int b = w->bottom_width; + unsigned int r = w->right_width; + + /* top */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, + 0, 0, 0, 0, x, y, wid, t); + + /* left */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, + 0, t, 0, t, x, y + t, l, hei - t); + + /* bottom */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, + l, hei - b, l, hei - b, x + l, y + hei - b, wid - l - r, b); + + /* right */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, + wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); + + /* body */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->alpha_pict, root_buffer, + l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); + } + } + + XFixesDestroyRegion(dpy, w->border_clip); + w->border_clip = None; + } + + XFixesDestroyRegion(dpy, region); + + if (root_buffer != root_picture) { + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, None); + XRenderComposite( + dpy, PictOpSrc, root_buffer, None, + root_picture, 0, 0, 0, 0, + 0, 0, root_width, root_height); + } +} + +static void +add_damage(Display *dpy, XserverRegion damage) { + if (all_damage) { + XFixesUnionRegion(dpy, all_damage, all_damage, damage); + XFixesDestroyRegion(dpy, damage); + } else { + all_damage = 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 { + 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); + } + + add_damage(dpy, parts); + w->damaged = 1; +} + +#if DEBUG_WINTYPE +static const char* +wintype_name(wintype type) { + const char *t; + + switch (type) { + case WINTYPE_DESKTOP: + t = "desktop"; + break; + case WINTYPE_DOCK: + t = "dock"; + break; + case WINTYPE_TOOLBAR: + t = "toolbar"; + break; + case WINTYPE_MENU: + t = "menu"; + break; + case WINTYPE_UTILITY: + t = "utility"; + break; + case WINTYPE_SPLASH: + t = "slash"; + break; + case WINTYPE_DIALOG: + t = "dialog"; + break; + case WINTYPE_NORMAL: + t = "normal"; + break; + case WINTYPE_DROPDOWN_MENU: + t = "dropdown"; + break; + case WINTYPE_POPUP_MENU: + t = "popup"; + break; + case WINTYPE_TOOLTIP: + t = "tooltip"; + break; + case WINTYPE_NOTIFY: + t = "notification"; + break; + case WINTYPE_COMBO: + t = "combo"; + break; + case WINTYPE_DND: + t = "dnd"; + break; + default: + t = "unknown"; + break; + } + + return t; +} +#endif + +static wintype +get_wintype_prop(Display * dpy, Window w) { + Atom actual; + wintype ret; + int format; + unsigned long n, left, off; + unsigned char *data; + + ret = WINTYPE_UNKNOWN; + off = 0; + + do { + set_ignore(dpy, NextRequest(dpy)); + + int result = XGetWindowProperty( + dpy, w, win_type_atom, off, 1L, False, XA_ATOM, + &actual, &format, &n, &left, &data); + + if (result != Success) break; + + if (data != None) { + int i; + + for (i = 1; i < NUM_WINTYPES; ++i) { + Atom a; + memcpy(&a, data, sizeof(Atom)); + if (a == win_type[i]) { + /* known type */ + ret = i; + break; + } + } + + XFree((void *) data); + } + + ++off; + } while (left >= 4 && ret == WINTYPE_UNKNOWN); + + return ret; +} + +static wintype +determine_wintype(Display *dpy, Window w, Window top) { + Window root_return, parent_return; + Window *children = NULL; + unsigned int nchildren, i; + wintype type; + + type = get_wintype_prop(dpy, w); + if (type != WINTYPE_UNKNOWN) return type; + + set_ignore(dpy, NextRequest(dpy)); + if (!XQueryTree(dpy, w, &root_return, &parent_return, + &children, &nchildren)) { + /* XQueryTree failed. */ + if (children) XFree((void *)children); + return WINTYPE_UNKNOWN; + } + + for (i = 0; i < nchildren; i++) { + type = determine_wintype(dpy, children[i], top); + if (type != WINTYPE_UNKNOWN) return type; + } + + if (children) { + XFree((void *)children); + } + + if (w != top) { + return WINTYPE_UNKNOWN; + } else { + return WINTYPE_NORMAL; + } +} + +static void +map_win(Display *dpy, Window id, + unsigned long sequence, Bool fade, + Bool override_redirect) { + win *w = find_win(dpy, id); + + if (!w) return; + + w->a.map_state = IsViewable; + w->window_type = determine_wintype(dpy, w->id, w->id); + +#if DEBUG_WINTYPE + printf("window 0x%x type %s\n", + w->id, wintype_name(w->window_type)); +#endif + + /* select before reading the property + so that no property changes are lost */ + if (!override_redirect) { + XSelectInput(dpy, id, PropertyChangeMask | FocusChangeMask); + } + + // this causes problems for inactive transparency + //w->opacity = get_opacity_prop(dpy, w, OPAQUE); + + determine_mode(dpy, w); + +#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 && win_type_fade[w->window_type]) { + set_fade( + dpy, w, 0, get_opacity_percent(dpy, w), + fade_in_step, 0, True, True); + } + + /* if any configure events happened while + the window was unmapped, then configure + the window to its correct place */ + if (w->need_configure) { + configure_win(dpy, &w->queue_configure); + } +} + +static void +finish_unmap_win(Display *dpy, win *w) { + w->damaged = 0; +#if CAN_DO_USABLE + w->usable = False; +#endif + + if (w->extents != None) { + /* destroys region */ + add_damage(dpy, w->extents); + 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; + } + + if (w->border_size) { + set_ignore(dpy, NextRequest(dpy)); + XFixesDestroyRegion(dpy, w->border_size); + w->border_size = None; + } + + if (w->shadow) { + XRenderFreePicture(dpy, w->shadow); + w->shadow = None; + } + + if (w->border_clip) { + XFixesDestroyRegion(dpy, w->border_clip); + w->border_clip = None; + } + + clip_changed = True; +} + +#if HAS_NAME_WINDOW_PIXMAP +static void +unmap_callback(Display *dpy, win *w) { + 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; + + /* don't care about properties anymore */ + set_ignore(dpy, NextRequest(dpy)); + XSelectInput(dpy, w->id, 0); + +#if HAS_NAME_WINDOW_PIXMAP + if (w->pixmap && fade && win_type_fade[w->window_type]) { + set_fade(dpy, w, w->opacity * 1.0 / OPAQUE, 0.0, + fade_out_step, unmap_callback, False, True); + } else +#endif + finish_unmap_win(dpy, w); +} + +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, opacity_atom, 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 double +get_opacity_percent(Display *dpy, win *w) { + double def = win_type_opacity[w->window_type]; + unsigned int opacity = + get_opacity_prop(dpy, w, (unsigned int)(OPAQUE * def)); + + return opacity * 1.0 / OPAQUE; +} + +static void +determine_mode(Display *dpy, win *w) { + int mode; + XRenderPictFormat *format; + + /* if trans prop == -1 fall back on previous tests*/ + + if (w->alpha_pict) { + XRenderFreePicture(dpy, w->alpha_pict); + w->alpha_pict = None; + } + + if (w->alpha_border_pict) { + XRenderFreePicture(dpy, w->alpha_border_pict); + w->alpha_border_pict = None; + } + + if (w->shadow_pict) { + XRenderFreePicture(dpy, w->shadow_pict); + w->shadow_pict = 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 void +set_opacity(Display *dpy, win *w, unsigned long opacity) { + w->opacity = opacity; + determine_mode(dpy, w); + if (w->shadow) { + XRenderFreePicture(dpy, w->shadow); + w->shadow = None; + + if (w->extents != None) { + XFixesDestroyRegion(dpy, w->extents); + } + + /* rebuild the shadow */ + w->extents = win_extents(dpy, w); + } +} + +static void +add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { + win *new = malloc(sizeof(win)); + win **p; + + if (!new) return; + + if (prev) { + for (p = &list; *p; p = &(*p)->next) { + if ((*p)->id == prev && !(*p)->destroyed) + 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); + set_ignore(dpy, NextRequest(dpy)); + new->damage = XDamageCreate(dpy, id, XDamageReportNonEmpty); + } + + new->alpha_pict = None; + new->alpha_border_pict = None; + new->shadow_pict = None; + new->border_size = 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->destroyed = False; + new->need_configure = False; + new->window_type = WINTYPE_UNKNOWN; + + new->border_clip = None; + new->prev_trans = 0; + + new->left_width = 0; + new->right_width = 0; + new->top_width = 0; + new->bottom_width = 0; + + new->client_win = 0; + if (!override_redirect) { + Window cw = find_client_win(dpy, new->id); + if (cw) { + get_frame_extents(dpy, cw, + &new->left_width, &new->right_width, + &new->top_width, &new->bottom_width); + new->client_win = cw; + XSelectInput(dpy, cw, PropertyChangeMask); + } + } + + new->next = *p; + *p = new; + + if (new->a.map_state == IsViewable) { + new->window_type = determine_wintype(dpy, id, id); + if (inactive_opacity && IS_NORMAL_WIN(new)) { + new->opacity = INACTIVE_OPACITY; + } + map_win(dpy, id, new->damage_sequence - 1, True, override_redirect); + } +} + +static 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 && !(*prev)->destroyed) + break; + } + + w->next = *prev; + *prev = w; + } +} + +static void +configure_win(Display *dpy, XConfigureEvent *ce) { + win *w = find_win(dpy, ce->window); + XserverRegion damage = None; + + if (!w) { + if (ce->window == root) { + if (root_buffer) { + XRenderFreePicture(dpy, root_buffer); + root_buffer = None; + } + root_width = ce->width; + root_height = ce->height; + } + return; + } + + if (w->a.map_state == IsUnmapped) { + /* save the configure event for when the window maps */ + w->need_configure = True; + w->queue_configure = *ce; + } else { + w->need_configure = False; + +#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; + + if (w->a.map_state != IsUnmapped && damage) { + XserverRegion extents = win_extents(dpy, w); + XFixesUnionRegion(dpy, damage, damage, extents); + XFixesDestroyRegion(dpy, extents); + add_damage(dpy, damage); + } + + clip_changed = True; + } + + w->a.override_redirect = ce->override_redirect; + restack_win(dpy, w, ce->above); +} + +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); + clip_changed = True; +} + +static void +finish_destroy_win(Display *dpy, Window id) { + win **prev, *w; + + for (prev = &list; (w = *prev); prev = &w->next) { + if (w->id == id && w->destroyed) { + finish_unmap_win(dpy, w); + *prev = w->next; + + if (w->alpha_pict) { + XRenderFreePicture(dpy, w->alpha_pict); + w->alpha_pict = None; + } + + if (w->alpha_border_pict) { + XRenderFreePicture(dpy, w->alpha_border_pict); + w->alpha_border_pict = None; + } + + if (w->shadow_pict) { + XRenderFreePicture(dpy, w->shadow_pict); + w->shadow_pict = None; + } + + /* fix leak, from freedesktop repo */ + if (w->shadow) { + XRenderFreePicture (dpy, w->shadow); + w->shadow = 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) { + finish_destroy_win(dpy, w->id); +} +#endif + +static void +destroy_win(Display *dpy, Window id, Bool fade) { + win *w = find_win(dpy, id); + + if (w) w->destroyed = True; + +#if HAS_NAME_WINDOW_PIXMAP + if (w && w->pixmap && fade && win_type_fade[w->window_type]) { + set_fade(dpy, w, w->opacity * 1.0 / OPAQUE, + 0.0, fade_out_step, destroy_callback, + False, True); + } else +#endif + { + finish_destroy_win(dpy, id); + } +} + +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 (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) { + clip_changed = True; + if (win_type_fade[w->window_type]) { + set_fade(dpy, w, 0, get_opacity_percent(dpy, w), + fade_in_step, 0, True, True); + } + w->usable = True; + } + } + + if (w->usable) +#endif + repair_win(dpy, w); +} + +static int +error(Display *dpy, XErrorEvent *ev) { + int o; + const char *name = "Unknown"; + + 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 (%s) request %d minor %d serial %lu\n", + ev->error_code, name, ev->request_code, + ev->minor_code, ev->serial); + + return 0; +} + +static void +expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) { + XserverRegion region = XFixesCreateRegion(dpy, rects, nrects); + add_damage(dpy, region); +} + +#if DEBUG_EVENTS +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) { + return ((XDamageNotifyEvent *)ev)->drawable; + } + return 0; + } +} +#endif + +/** + * Events + */ + +inline static void +ev_focus_in(XFocusChangeEvent *ev) { + if (!inactive_opacity) return; + + win *w = find_win(dpy, ev->window); + if (IS_NORMAL_WIN(w)) { + set_opacity(dpy, w, OPAQUE); + } +} + +inline static void +ev_focus_out(XFocusChangeEvent *ev) { + if (!inactive_opacity) return; + + if (ev->mode == NotifyGrab + || (ev->mode == NotifyNormal + && (ev->detail == NotifyNonlinear + || ev->detail == NotifyNonlinearVirtual))) { + ; + } else { + return; + } + + win *w = find_win(dpy, ev->window); + if (IS_NORMAL_WIN(w)) { + set_opacity(dpy, w, INACTIVE_OPACITY); + } +} + +inline static void +ev_create_notify(XCreateWindowEvent *ev) { + add_win(dpy, ev->window, 0, ev->override_redirect); +} + +inline static void +ev_configure_notify(XConfigureEvent *ev) { + configure_win(dpy, ev); +} + +inline static void +ev_destroy_notify(XDestroyWindowEvent *ev) { + destroy_win(dpy, ev->window, True); +} + +inline static void +ev_map_notify(XMapEvent *ev) { + map_win(dpy, ev->window, ev->serial, True, ev->override_redirect); +} + +inline static void +ev_unmap_notify(XUnmapEvent *ev) { + unmap_win(dpy, ev->window, True); +} + +inline static void +ev_reparent_notify(XReparentEvent *ev) { + if (ev->parent == root) { + add_win(dpy, ev->window, 0, ev->override_redirect); + } else { + destroy_win(dpy, ev->window, True); + } +} + +inline static void +ev_circulate_notify(XCirculateEvent *ev) { + circulate_win(dpy, ev); +} + +inline static void +ev_expose(XExposeEvent *ev) { + if (ev->window == root) { + int more = ev->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->x; + expose_rects[n_expose].y = ev->y; + expose_rects[n_expose].width = ev->width; + expose_rects[n_expose].height = ev->height; + n_expose++; + + if (ev->count == 0) { + expose_root(dpy, root, expose_rects, n_expose); + n_expose = 0; + } + } +} + +inline static void +ev_property_notify(XPropertyEvent *ev) { + int p; + for (p = 0; background_props[p]; p++) { + if (ev->atom == XInternAtom(dpy, background_props[p], False)) { + if (root_tile) { + XClearArea(dpy, root, 0, 0, 0, 0, True); + XRenderFreePicture(dpy, root_tile); + root_tile = None; + break; + } + } + } + + /* check if Trans property was changed */ + if (ev->atom == opacity_atom) { + /* reset mode and redraw window */ + win *w = find_win(dpy, ev->window); + if (w) { + double def = win_type_opacity[w->window_type]; + set_opacity(dpy, w, + get_opacity_prop(dpy, w, (unsigned long)(OPAQUE * def))); + } + } + + if (frame_opacity && ev->atom == extents_atom) { + win *w = find_toplevel(dpy, ev->window); + if (w) { + get_frame_extents(dpy, w->client_win, + &w->left_width, &w->right_width, + &w->top_width, &w->bottom_width); + } + } +} + +inline static void +ev_damage_notify(XDamageNotifyEvent *ev) { + damage_win(dpy, ev); +} + +inline static void +ev_handle(XEvent *ev) { + if ((ev->type & 0x7f) != KeymapNotify) { + discard_ignore(dpy, ev->xany.serial); + } + +#if DEBUG_EVENTS + if (ev->type != damage_event + XDamageNotify) { + printf("event %10.10s serial 0x%08x window 0x%08x\n", + ev_name(ev), ev_serial(ev), ev_window(ev)); + } +#endif + + switch (ev->type) { + case FocusIn: + ev_focus_in((XFocusChangeEvent *)ev); + break; + case FocusOut: + ev_focus_out((XFocusChangeEvent *)ev); + break; + case CreateNotify: + ev_create_notify((XCreateWindowEvent *)ev); + break; + case ConfigureNotify: + ev_configure_notify((XConfigureEvent *)ev); + break; + case DestroyNotify: + ev_destroy_notify((XDestroyWindowEvent *)ev); + break; + case MapNotify: + ev_map_notify((XMapEvent *)ev); + break; + case UnmapNotify: + ev_unmap_notify((XUnmapEvent *)ev); + break; + case ReparentNotify: + ev_reparent_notify((XReparentEvent *)ev); + break; + case CirculateNotify: + ev_circulate_notify((XCirculateEvent *)ev); + break; + case Expose: + ev_expose((XExposeEvent *)ev); + break; + case PropertyNotify: + ev_property_notify((XPropertyEvent *)ev); + break; + default: + if (ev->type == damage_event + XDamageNotify) { + ev_damage_notify((XDamageNotifyEvent *)ev); + } + break; + } +} + +/** + * Main + */ + +static void +usage() { + fprintf(stderr, "compton v0.0.1\n"); + fprintf(stderr, "usage: compton [options]\n"); + + fprintf(stderr, "Options\n"); + fprintf(stderr, + " -d display\n " + "Which display should be managed.\n"); + fprintf(stderr, + " -r radius\n " + "The blur radius for shadows. (default 12)\n"); + fprintf(stderr, + " -o opacity\n " + "The translucency for shadows. (default .75)\n"); + fprintf(stderr, + " -l left-offset\n " + "The left offset for shadows. (default -15)\n"); + fprintf(stderr, + " -t top-offset\n " + "The top offset for shadows. (default -15)\n"); + fprintf(stderr, + " -I fade-in-step\n " + "Opacity change between steps while fading in. (default 0.028)\n"); + fprintf(stderr, + " -O fade-out-step\n " + "Opacity change between steps while fading out. (default 0.03)\n"); + fprintf(stderr, + " -D fade-delta-time\n " + "The time between steps in a fade in milliseconds. (default 10)\n"); + fprintf(stderr, + " -m opacity\n " + "The opacity for menus. (default 1.0)\n"); + fprintf(stderr, + " -c\n " + "Enabled client-side shadows on windows.\n"); + fprintf(stderr, + " -C\n " + "Avoid drawing shadows on dock/panel windows.\n"); + fprintf(stderr, + " -z\n " + "Zero the part of the shadow's mask behind the window (experimental)."); + fprintf(stderr, + " -f\n " + "Fade windows in/out when opening/closing.\n"); + fprintf(stderr, + " -F\n " + "Fade windows during opacity changes.\n"); + fprintf(stderr, + " -i opacity\n " + "Opacity of inactive windows. (0.1 - 1.0)\n"); + fprintf(stderr, + " -e opacity\n " + "Opacity of window titlebars and borders. (0.1 - 1.0)\n"); + fprintf(stderr, + " -G\n " + "Don't draw shadows on DND windows\n"); + fprintf(stderr, + " -b daemonize\n " + "Daemonize process.\n"); + fprintf(stderr, + " -S\n " + "Enable synchronous operation (for debugging).\n"); + + exit(1); +} + +static void +register_cm(int scr) { + Window w; + Atom a; + char *buf; + int len, s; + + if (scr < 0) return; + + w = XCreateSimpleWindow( + dpy, RootWindow(dpy, 0), + 0, 0, 1, 1, 0, None, None); + + Xutf8SetWMProperties( + dpy, w, "xcompmgr", "xcompmgr", + NULL, 0, NULL, NULL, NULL); + + len = strlen(REGISTER_PROP) + 2; + s = scr; + + while (s >= 10) { + ++len; + s /= 10; + } + + buf = malloc(len); + snprintf(buf, len, REGISTER_PROP"%d", scr); + + a = XInternAtom(dpy, buf, False); + free(buf); + + XSetSelectionOwner(dpy, a, w, 0); +} + +static void +fork_after() { + if (getppid() == 1) return; + + int pid = fork(); + + if (pid == -1) { + fprintf(stderr, "Fork failed\n"); + return; + } + + if (pid > 0) _exit(0); + + setsid(); + + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); +} + +static void +get_atoms() { + extents_atom = XInternAtom(dpy, + "_NET_FRAME_EXTENTS", False); + opacity_atom = XInternAtom(dpy, + "_NET_WM_WINDOW_OPACITY", False); + + win_type_atom = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE", False); + win_type[WINTYPE_UNKNOWN] = 0; + win_type[WINTYPE_DESKTOP] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_DESKTOP", False); + win_type[WINTYPE_DOCK] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_DOCK", False); + win_type[WINTYPE_TOOLBAR] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_TOOLBAR", False); + win_type[WINTYPE_MENU] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_MENU", False); + win_type[WINTYPE_UTILITY] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_UTILITY", False); + win_type[WINTYPE_SPLASH] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_SPLASH", False); + win_type[WINTYPE_DIALOG] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_DIALOG", False); + win_type[WINTYPE_NORMAL] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_NORMAL", False); + win_type[WINTYPE_DROPDOWN_MENU] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False); + win_type[WINTYPE_POPUP_MENU] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_POPUP_MENU", False); + win_type[WINTYPE_TOOLTIP] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_TOOLTIP", False); + win_type[WINTYPE_NOTIFY] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); + win_type[WINTYPE_COMBO] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_COMBO", False); + win_type[WINTYPE_DND] = XInternAtom(dpy, + "_NET_WM_WINDOW_TYPE_DND", False); +} + +int +main(int argc, char **argv) { + XEvent ev; + Window root_return, parent_return; + Window *children; + unsigned int nchildren; + int i; + XRenderPictureAttributes pa; + struct pollfd ufd; + int composite_major, composite_minor; + char *display = 0; + int o; + Bool no_dock_shadow = False; + Bool no_dnd_shadow = False; + Bool fork_after_register = False; + + for (i = 0; i < NUM_WINTYPES; ++i) { + win_type_fade[i] = False; + win_type_shadow[i] = False; + win_type_opacity[i] = 1.0; + } + + while ((o = getopt(argc, argv, "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb")) != -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 'c': + for (i = 0; i < NUM_WINTYPES; ++i) { + win_type_shadow[i] = True; + } + win_type_shadow[WINTYPE_DESKTOP] = False; + break; + case 'C': + no_dock_shadow = True; + break; + case 'm': + win_type_opacity[WINTYPE_DROPDOWN_MENU] = atof(optarg); + win_type_opacity[WINTYPE_POPUP_MENU] = atof(optarg); + break; + case 'f': + for (i = 0; i < NUM_WINTYPES; ++i) { + win_type_fade[i] = True; + } + break; + case 'F': + fade_trans = True; + break; + case 'S': + synchronize = True; + break; + case 'r': + shadow_radius = atoi(optarg); + break; + case 'o': + shadow_opacity = atof(optarg); + break; + case 'l': + shadow_offset_x = atoi(optarg); + break; + case 't': + shadow_offset_y = atoi(optarg); + break; + case 'i': + inactive_opacity = (double)atof(optarg); + break; + case 'e': + frame_opacity = (double)atof(optarg); + break; + case 'z': + clear_shadow = True; + break; + case 'n': + case 'a': + case 's': + fprintf(stderr, "Warning: " + "-n, -a, and -s have been removed.\n"); + break; + case 'G': + no_dnd_shadow = True; + break; + case 'b': + fork_after_register = True; + break; + default: + usage(); + break; + } + } + + if (no_dock_shadow) { + win_type_shadow[WINTYPE_DOCK] = False; + } + + if (no_dnd_shadow) { + win_type_shadow[WINTYPE_DND] = False; + } + + 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) { + has_name_pixmap = 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); + } + + register_cm(scr); + + if (fork_after_register) fork_after(); + + get_atoms(); + + pa.subwindow_mode = IncludeInferiors; + + gaussian_map = make_gaussian_map(dpy, shadow_radius); + presum_gaussian(gaussian_map); + + root_width = DisplayWidth(dpy, scr); + root_height = DisplayHeight(dpy, scr); + + root_picture = XRenderCreatePicture(dpy, root, + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + CPSubwindowMode, &pa); + + black_picture = solid_picture(dpy, True, 1, 0, 0, 0); + + all_damage = None; + clip_changed = True; + XGrabServer(dpy); + + 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, False); + } + + XFree(children); + + XUngrabServer(dpy); + + ufd.fd = ConnectionNumber(dpy); + ufd.events = POLLIN; + + paint_all(dpy, None); + + for (;;) { + do { + if (!QLength(dpy)) { + if (poll(&ufd, 1, fade_timeout()) == 0) { + run_fades(dpy); + break; + } + } + + XNextEvent(dpy, &ev); + ev_handle((XEvent *)&ev); + } while (QLength(dpy)); + + if (all_damage) { + static int paint; + paint_all(dpy, all_damage); + paint++; + XSync(dpy, False); + all_damage = None; + clip_changed = False; + } + } +} diff --git a/compton.h b/compton.h new file mode 100644 index 000000000..18e385989 --- /dev/null +++ b/compton.h @@ -0,0 +1,355 @@ +/** + * compton.h + */ + +// Throw everything in here. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 +#define HAS_NAME_WINDOW_PIXMAP 1 +#endif + +#define CAN_DO_USABLE 0 +#define DEBUG_REPAINT 0 +#define DEBUG_EVENTS 0 +#define DEBUG_WINTYPE 0 +#define MONITOR_REPAINT 0 + +#define OPAQUE 0xffffffff +#define REGISTER_PROP "_NET_WM_CM_S" + +#define WINDOW_SOLID 0 +#define WINDOW_TRANS 1 +#define WINDOW_ARGB 2 + +/** + * Types + */ + +typedef enum { + WINTYPE_UNKNOWN, + WINTYPE_DESKTOP, + WINTYPE_DOCK, + WINTYPE_TOOLBAR, + WINTYPE_MENU, + WINTYPE_UTILITY, + WINTYPE_SPLASH, + WINTYPE_DIALOG, + WINTYPE_NORMAL, + WINTYPE_DROPDOWN_MENU, + WINTYPE_POPUP_MENU, + WINTYPE_TOOLTIP, + WINTYPE_NOTIFY, + WINTYPE_COMBO, + WINTYPE_DND, + NUM_WINTYPES +} wintype; + +typedef struct _ignore { + struct _ignore *next; + unsigned long sequence; +} ignore; + +typedef struct _win { + struct _win *next; + Window id; + Window client_win; +#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 + int mode; + int damaged; + Damage damage; + Picture picture; + Picture alpha_pict; + Picture alpha_border_pict; + Picture shadow_pict; + XserverRegion border_size; + XserverRegion extents; + Picture shadow; + int shadow_dx; + int shadow_dy; + int shadow_width; + int shadow_height; + unsigned int opacity; + wintype window_type; + unsigned long damage_sequence; /* sequence when damage was created */ + Bool destroyed; + unsigned int left_width; + unsigned int right_width; + unsigned int top_width; + unsigned int bottom_width; + + Bool need_configure; + XConfigureEvent queue_configure; + + /* for drawing translucent windows */ + XserverRegion border_clip; + 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); + Display *dpy; +} fade; + +/** + * Functions + */ + +static int +get_time_in_milliseconds(); + +static fade * +find_fade(win *w); + +static void +dequeue_fade(Display *dpy, fade *f); + +static void +cleanup_fade(Display *dpy, win *w); + +static void +enqueue_fade(Display *dpy, fade *f); + +static void +set_fade(Display *dpy, win *w, double start, + double finish, double step, + void(*callback) (Display *dpy, win *w), + Bool exec_callback, Bool override); + +static int +fade_timeout(void); + +static void +run_fades(Display *dpy); + +static double +gaussian(double r, double x, double y); + +static conv * +make_gaussian_map(Display *dpy, double r); + +static unsigned char +sum_gaussian(conv *map, double opacity, + int x, int y, int width, int height); + +static void +presum_gaussian(conv *map); + +static XImage * +make_shadow(Display *dpy, double opacity, + int width, int height); + +static Picture +shadow_picture(Display *dpy, double opacity, Picture alpha_pict, + int width, int height, int *wp, int *hp); + +static Picture +solid_picture(Display *dpy, Bool argb, double a, + double r, double g, double b); + +static void +discard_ignore(Display *dpy, unsigned long sequence); + +static void +set_ignore(Display *dpy, unsigned long sequence); + +static int +should_ignore(Display *dpy, unsigned long sequence); + +static win * +find_win(Display *dpy, Window id); + +static win * +find_toplevel(Display *dpy, Window id); + +static Picture +root_tile_f(Display *dpy); + +static void +paint_root(Display *dpy); + +static XserverRegion +win_extents(Display *dpy, win *w); + +static XserverRegion +border_size(Display *dpy, win *w); + +static Window +find_client_win(Display *dpy, Window win); + +static void +get_frame_extents(Display *dpy, Window w, + unsigned int *left, + unsigned int *right, + unsigned int *top, + unsigned int *bottom); + +static void +paint_all(Display *dpy, XserverRegion region); + +static void +add_damage(Display *dpy, XserverRegion damage); + +static void +repair_win(Display *dpy, win *w); + +static wintype +get_wintype_prop(Display * dpy, Window w); + +static wintype +determine_wintype(Display *dpy, Window w, Window top); + +static void +map_win(Display *dpy, Window id, + unsigned long sequence, Bool fade, + Bool override_redirect); + +static void +finish_unmap_win(Display *dpy, win *w); + +#if HAS_NAME_WINDOW_PIXMAP +static void +unmap_callback(Display *dpy, win *w); +#endif + +static void +unmap_win(Display *dpy, Window id, Bool fade); + +static unsigned int +get_opacity_prop(Display *dpy, win *w, unsigned int def); + +static double +get_opacity_percent(Display *dpy, win *w); + +static void +determine_mode(Display *dpy, win *w); + +static void +set_opacity(Display *dpy, win *w, unsigned long opacity); + +static void +add_win(Display *dpy, Window id, Window prev, Bool override_redirect); + +static void +restack_win(Display *dpy, win *w, Window new_above); + +static void +configure_win(Display *dpy, XConfigureEvent *ce); + +static void +circulate_win(Display *dpy, XCirculateEvent *ce); + +static void +finish_destroy_win(Display *dpy, Window id); + +#if HAS_NAME_WINDOW_PIXMAP +static void +destroy_callback(Display *dpy, win *w); +#endif + +static void +destroy_win(Display *dpy, Window id, Bool fade); + +static void +damage_win(Display *dpy, XDamageNotifyEvent *de); + +static int +error(Display *dpy, XErrorEvent *ev); + +static void +expose_root(Display *dpy, Window root, XRectangle *rects, int nrects); + +#if DEBUG_EVENTS +static int +ev_serial(XEvent *ev); + +static char * +ev_name(XEvent *ev); + +static Window +ev_window(XEvent *ev); +#endif + +static void +usage(); + +static void +register_cm(int scr); + +inline static void +ev_focus_in(XFocusChangeEvent *ev); + +inline static void +ev_focus_out(XFocusChangeEvent *ev); + +inline static void +ev_create_notify(XCreateWindowEvent *ev); + +inline static void +ev_configure_notify(XConfigureEvent *ev); + +inline static void +ev_destroy_notify(XDestroyWindowEvent *ev); + +inline static void +ev_map_notify(XMapEvent *ev); + +inline static void +ev_unmap_notify(XUnmapEvent *ev); + +inline static void +ev_reparent_notify(XReparentEvent *ev); + +inline static void +ev_circulate_notify(XCirculateEvent *ev); + +inline static void +ev_expose(XExposeEvent *ev); + +inline static void +ev_property_notify(XPropertyEvent *ev); + +inline static void +ev_damage_notify(XDamageNotifyEvent *ev); + +inline static void +ev_handle(XEvent *ev); + +static void +fork_after(); + +static void +get_atoms(); -- cgit v1.2.1