From d634640199edfe68c57eb7bb2b1260f7c7fb61b3 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 27 Feb 2012 01:42:38 -0600 Subject: [PATCH 001/178] 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(); From 614a1deb9ef3d6b3d2ca8a88b01d6b6dd7dc4d19 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 27 Feb 2012 06:49:50 -0600 Subject: [PATCH 002/178] minor changes --- compton.c | 106 +++++++++++++++++++++++------------------------------- 1 file changed, 44 insertions(+), 62 deletions(-) diff --git a/compton.c b/compton.c index 2b33575d5..1e2444b79 100644 --- a/compton.c +++ b/compton.c @@ -79,7 +79,8 @@ Bool win_type_fade[NUM_WINTYPES]; ((w) && ((w)->window_type == WINTYPE_NORMAL \ || (w)->window_type == WINTYPE_UTILITY)) -#define HAS_FRAME_OPACITY(w) (frame_opacity && (w)->top_width) +#define HAS_FRAME_OPACITY(w) \ + (frame_opacity && (w)->top_width) /** * Options @@ -298,7 +299,7 @@ run_fades(Display *dpy) { static double gaussian(double r, double x, double y) { return ((1 / (sqrt(2 * M_PI * r))) * - exp((- (x * x + y * y)) / (2 * r * r))); + exp((- (x * x + y * y)) / (2 * r * r))); } static conv * @@ -401,9 +402,10 @@ sum_gaussian(conv *map, double opacity, /* precompute shadow corners and sides to save time for large windows */ + static void presum_gaussian(conv *map) { - int center = map->size/2; + int center = map->size / 2; int opacity, x, y; Gsize = map->size; @@ -990,7 +992,6 @@ paint_all(Display *dpy, XserverRegion region) { #endif for (w = list; w; w = w->next) { - #if CAN_DO_USABLE if (!w->usable) continue; #endif @@ -2214,65 +2215,46 @@ 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"); + "Options\n" + "-d display\n" + " Which display should be managed.\n" + "-r radius\n" + " The blur radius for shadows. (default 12)\n" + "-o opacity\n" + " The translucency for shadows. (default .75)\n" + "-l left-offset\n" + " The left offset for shadows. (default -15)\n" + "-t top-offset\n" + " The top offset for shadows. (default -15)\n" + "-I fade-in-step\n" + " Opacity change between steps while fading in. (default 0.028)\n" + "-O fade-out-step\n" + " Opacity change between steps while fading out. (default 0.03)\n" + "-D fade-delta-time\n" + " The time between steps in a fade in milliseconds. (default 10)\n" + "-m opacity\n" + " The opacity for menus. (default 1.0)\n" + "-c\n" + " Enabled client-side shadows on windows.\n" + "-C\n" + " Avoid drawing shadows on dock/panel windows.\n" + "-z\n" + " Zero the part of the shadow's mask behind the window (experimental)." + "-f\n" + " Fade windows in/out when opening/closing.\n" + "-F\n" + " Fade windows during opacity changes.\n" + "-i opacity\n" + " Opacity of inactive windows. (0.1 - 1.0)\n" + "-e opacity\n" + " Opacity of window titlebars and borders. (0.1 - 1.0)\n" + "-G\n" + " Don't draw shadows on DND windows\n" + "-b daemonize\n" + " Daemonize process.\n" + "-S\n" + " Enable synchronous operation (for debugging).\n"); exit(1); } From 85de312fe7a794c4263b518098f248168cad242b Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 28 Feb 2012 01:51:21 -0600 Subject: [PATCH 003/178] stop listening for property events on client window after unmap --- compton.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compton.c b/compton.c index 1e2444b79..681a532db 100644 --- a/compton.c +++ b/compton.c @@ -1462,6 +1462,11 @@ unmap_win(Display *dpy, Window id, Bool fade) { set_ignore(dpy, NextRequest(dpy)); XSelectInput(dpy, w->id, 0); + if (w->client_win) { + set_ignore(dpy, NextRequest(dpy)); + XSelectInput(dpy, w->client_win, 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, From 02a5747f34d6699ac4ff7ca316ba0a88e912de7f Mon Sep 17 00:00:00 2001 From: Tim van Dalen Date: Sat, 17 Mar 2012 17:29:01 +0100 Subject: [PATCH 004/178] Fixed a (very) small 'bug' in the usage text, -f wasn't printed on a new line. --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 681a532db..90475a03d 100644 --- a/compton.c +++ b/compton.c @@ -2245,7 +2245,7 @@ usage() { "-C\n" " Avoid drawing shadows on dock/panel windows.\n" "-z\n" - " Zero the part of the shadow's mask behind the window (experimental)." + " Zero the part of the shadow's mask behind the window (experimental).\n" "-f\n" " Fade windows in/out when opening/closing.\n" "-F\n" From 8610fd1f020e0b41a8820feccea2d5c6825330d0 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 3 Jun 2012 11:08:50 -0500 Subject: [PATCH 005/178] refactor, rename --- compton.c | 97 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/compton.c b/compton.c index 90475a03d..1ea726fbf 100644 --- a/compton.c +++ b/compton.c @@ -43,7 +43,7 @@ int composite_opcode; conv *gaussian_map; /* for shadow precomputation */ -int Gsize = -1; +int cgsize = -1; unsigned char *shadow_corner = NULL; unsigned char *shadow_top = NULL; @@ -408,36 +408,36 @@ presum_gaussian(conv *map) { int center = map->size / 2; int opacity, x, y; - Gsize = map->size; + cgsize = 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)); + shadow_corner = (unsigned char *)(malloc((cgsize + 1) * (cgsize + 1) * 26)); + shadow_top = (unsigned char *)(malloc((cgsize + 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 (x = 0; x <= cgsize; x++) { + shadow_top[25 * (cgsize + 1) + x] = + sum_gaussian(map, 1, x - center, center, cgsize * 2, cgsize * 2); for (opacity = 0; opacity < 25; opacity++) { - shadow_top[opacity * (Gsize + 1) + x] = - shadow_top[25 * (Gsize + 1) + x] * opacity / 25; + shadow_top[opacity * (cgsize + 1) + x] = + shadow_top[25 * (cgsize + 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]; + shadow_corner[25 * (cgsize + 1) * (cgsize + 1) + y * (cgsize + 1) + x] + = sum_gaussian(map, 1, x - center, y - center, cgsize * 2, cgsize * 2); + shadow_corner[25 * (cgsize + 1) * (cgsize + 1) + x * (cgsize + 1) + y] + = shadow_corner[25 * (cgsize + 1) * (cgsize + 1) + y * (cgsize + 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; + shadow_corner[opacity * (cgsize + 1) * (cgsize + 1) + + y * (cgsize + 1) + x] + = shadow_corner[opacity * (cgsize + 1) * (cgsize + 1) + + x * (cgsize + 1) + y] + = shadow_corner[25 * (cgsize + 1) * (cgsize + 1) + + y * (cgsize + 1) + x] * opacity / 25; } } } @@ -480,8 +480,8 @@ make_shadow(Display *dpy, double opacity, */ if (!clear_shadow) { - if (Gsize > 0) { - d = shadow_top[opacity_int * (Gsize + 1) + Gsize]; + if (cgsize > 0) { + d = shadow_top[opacity_int * (cgsize + 1) + cgsize]; } else { d = sum_gaussian(gaussian_map, opacity, center, center, width, height); @@ -505,9 +505,9 @@ make_shadow(Display *dpy, double opacity, 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]; + if (xlimit == cgsize && ylimit == cgsize) { + d = shadow_corner[opacity_int * (cgsize + 1) * (cgsize + 1) + + y * (cgsize + 1) + x]; } else { d = sum_gaussian(gaussian_map, opacity, x - center, y - center, width, height); @@ -526,8 +526,8 @@ make_shadow(Display *dpy, double opacity, 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]; + if (ylimit == cgsize) { + d = shadow_top[opacity_int * (cgsize + 1) + y]; } else { d = sum_gaussian(gaussian_map, opacity, center, y - center, width, height); @@ -542,8 +542,8 @@ make_shadow(Display *dpy, double opacity, */ for (x = 0; x < xlimit; x++) { - if (xlimit == Gsize) { - d = shadow_top[opacity_int * (Gsize + 1) + x]; + if (xlimit == cgsize) { + d = shadow_top[opacity_int * (cgsize + 1) + x]; } else { d = sum_gaussian(gaussian_map, opacity, x - center, center, width, height); @@ -572,48 +572,48 @@ make_shadow(Display *dpy, double opacity, static Picture shadow_picture(Display *dpy, double opacity, Picture alpha_pict, int width, int height, int *wp, int *hp) { - XImage *shadowImage; - Pixmap shadowPixmap; + XImage *shadow_image; + Pixmap shadow_pixmap; Picture shadow_picture; GC gc; - shadowImage = make_shadow(dpy, opacity, width, height); - if (!shadowImage) return None; + shadow_image = make_shadow(dpy, opacity, width, height); + if (!shadow_image) return None; - shadowPixmap = XCreatePixmap(dpy, root, - shadowImage->width, shadowImage->height, 8); + shadow_pixmap = XCreatePixmap(dpy, root, + shadow_image->width, shadow_image->height, 8); - if (!shadowPixmap) { - XDestroyImage(shadowImage); + if (!shadow_pixmap) { + XDestroyImage(shadow_image); return None; } - shadow_picture = XRenderCreatePicture(dpy, shadowPixmap, + shadow_picture = XRenderCreatePicture(dpy, shadow_pixmap, XRenderFindStandardFormat(dpy, PictStandardA8), 0, 0); if (!shadow_picture) { - XDestroyImage(shadowImage); - XFreePixmap(dpy, shadowPixmap); + XDestroyImage(shadow_image); + XFreePixmap(dpy, shadow_pixmap); return None; } - gc = XCreateGC(dpy, shadowPixmap, 0, 0); + gc = XCreateGC(dpy, shadow_pixmap, 0, 0); if (!gc) { - XDestroyImage(shadowImage); - XFreePixmap(dpy, shadowPixmap); + XDestroyImage(shadow_image); + XFreePixmap(dpy, shadow_pixmap); XRenderFreePicture(dpy, shadow_picture); return None; } XPutImage( - dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, - shadowImage->width, shadowImage->height); + dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, + shadow_image->width, shadow_image->height); - *wp = shadowImage->width; - *hp = shadowImage->height; + *wp = shadow_image->width; + *hp = shadow_image->height; XFreeGC(dpy, gc); - XDestroyImage(shadowImage); - XFreePixmap(dpy, shadowPixmap); + XDestroyImage(shadow_image); + XFreePixmap(dpy, shadow_pixmap); return shadow_picture; } @@ -1113,6 +1113,7 @@ paint_all(Display *dpy, XserverRegion region) { 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); From b42eee1ef38d77b3e8dee33af3a961854e2d050a Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 3 Jun 2012 11:12:30 -0500 Subject: [PATCH 006/178] rename, refactor again. --- compton.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compton.c b/compton.c index 1ea726fbf..60948ca32 100644 --- a/compton.c +++ b/compton.c @@ -966,15 +966,15 @@ paint_all(Display *dpy, XserverRegion region) { root_buffer = root_picture; #else if (!root_buffer) { - Pixmap rootPixmap = XCreatePixmap( + Pixmap root_pixmap = XCreatePixmap( dpy, root, root_width, root_height, DefaultDepth(dpy, scr)); - root_buffer = XRenderCreatePicture(dpy, rootPixmap, + root_buffer = XRenderCreatePicture(dpy, root_pixmap, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), 0, 0); - XFreePixmap(dpy, rootPixmap); + XFreePixmap(dpy, root_pixmap); } #endif @@ -1045,7 +1045,7 @@ paint_all(Display *dpy, XserverRegion region) { } if (!w->border_size) { - w->border_size = border_size (dpy, w); + w->border_size = border_size(dpy, w); } if (!w->extents) { @@ -1220,7 +1220,7 @@ repair_win(Display *dpy, win *w) { } #if DEBUG_WINTYPE -static const char* +static const char * wintype_name(wintype type) { const char *t; @@ -1427,8 +1427,8 @@ finish_unmap_win(Display *dpy, win *w) { if (w->border_size) { set_ignore(dpy, NextRequest(dpy)); - XFixesDestroyRegion(dpy, w->border_size); - w->border_size = None; + XFixesDestroyRegion(dpy, w->border_size); + w->border_size = None; } if (w->shadow) { @@ -1512,7 +1512,7 @@ determine_mode(Display *dpy, win *w) { int mode; XRenderPictFormat *format; - /* if trans prop == -1 fall back on previous tests*/ + /* if trans prop == -1 fall back on previous tests */ if (w->alpha_pict) { XRenderFreePicture(dpy, w->alpha_pict); @@ -1806,7 +1806,7 @@ finish_destroy_win(Display *dpy, Window id) { /* fix leak, from freedesktop repo */ if (w->shadow) { - XRenderFreePicture (dpy, w->shadow); + XRenderFreePicture(dpy, w->shadow); w->shadow = None; } From c10cd64f1fb6fc083b81981b561824b5da5275f4 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 7 Sep 2012 11:51:08 -0500 Subject: [PATCH 007/178] apply patch from richardgv. fixes #5. --- compton.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 60948ca32..b81117d46 100644 --- a/compton.c +++ b/compton.c @@ -1712,7 +1712,10 @@ configure_win(Display *dpy, XConfigureEvent *ce) { /* save the configure event for when the window maps */ w->need_configure = True; w->queue_configure = *ce; + restack_win(dpy, w, ce->above); } else { + if (!(w->need_configure)) + restack_win(dpy, w, ce->above); w->need_configure = False; #if CAN_DO_USABLE @@ -1760,7 +1763,6 @@ configure_win(Display *dpy, XConfigureEvent *ce) { } w->a.override_redirect = ce->override_redirect; - restack_win(dpy, w, ce->above); } static void From e29714d41dabf9db6eea16583994ea061dba8505 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 8 Sep 2012 21:04:44 -0500 Subject: [PATCH 008/178] add richardgv's patch. see #31. --- compton.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compton.c b/compton.c index b81117d46..021174804 100644 --- a/compton.c +++ b/compton.c @@ -1578,6 +1578,11 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { if (!new) return; + if (find_win(dpy, id)) { + free(new); + return; + } + if (prev) { for (p = &list; *p; p = &(*p)->next) { if ((*p)->id == prev && !(*p)->destroyed) From 4f11c53a6d948be29a69b4b3b0a40679e9dd01dd Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sat, 8 Sep 2012 21:13:56 -0500 Subject: [PATCH 009/178] avoid allocating a new win struct if possible --- compton.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compton.c b/compton.c index 021174804..b72dc59bd 100644 --- a/compton.c +++ b/compton.c @@ -1573,16 +1573,15 @@ set_opacity(Display *dpy, win *w, unsigned long opacity) { static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { + if (find_win(dpy, id)) { + return; + } + win *new = malloc(sizeof(win)); win **p; if (!new) return; - if (find_win(dpy, id)) { - free(new); - return; - } - if (prev) { for (p = &list; *p; p = &(*p)->next) { if ((*p)->id == prev && !(*p)->destroyed) From fe811d6451944e6643a8bfae13050b23bf7254c9 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 11 Sep 2012 21:11:06 +0800 Subject: [PATCH 010/178] Bug fix: Issue #38, fixes painting specially-shaped semi-transparent windows Fix taken from xcompmgr. --- compton.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compton.c b/compton.c index b72dc59bd..0272615ba 100644 --- a/compton.c +++ b/compton.c @@ -1122,6 +1122,10 @@ paint_all(Display *dpy, XserverRegion region) { if (w->mode != WINDOW_SOLID || HAS_FRAME_OPACITY(w)) { int x, y, wid, hei; + // Necessary to make sure specially-shaped windows are + // painted correctly + XFixesIntersectRegion(dpy, w->border_clip, w->border_clip, w->border_size); + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, w->border_clip); #if HAS_NAME_WINDOW_PIXMAP x = w->a.x; y = w->a.y; From f7bf27f8380cda8c8c27cbfa19c9a656d638b39a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 11 Sep 2012 21:33:03 +0800 Subject: [PATCH 011/178] Bug fix: Issue #36: Chromium window painting problems More descriptions on issue #36. - Listens ShapeNotify event to get around the Chromium window painting issues. - Adds dependency on X Shape extension. - Adds a few functions for convenience, so a bit code clean up. - Better event debug support, adds restack_win() debug. --- compton.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++------ compton.h | 39 ++++++++++ 2 files changed, 244 insertions(+), 24 deletions(-) diff --git a/compton.c b/compton.c index 0272615ba..c3af63213 100644 --- a/compton.c +++ b/compton.c @@ -10,6 +10,10 @@ #include "compton.h" +#if DEBUG_EVENTS +static int window_get_name(Window w, char **name); +#endif + /** * Shared */ @@ -36,6 +40,10 @@ ignore *ignore_head, **ignore_tail = &ignore_head; int xfixes_event, xfixes_error; int damage_event, damage_error; int composite_event, composite_error; +/// Whether X Shape extension exists. +Bool shape_exists = True; +/// Event base number and error base number for X Shape extension. +int shape_event, shape_error; int render_event, render_error; int composite_opcode; @@ -954,12 +962,7 @@ paint_all(Display *dpy, XserverRegion region) { 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); + region = get_screen_region(dpy); } #if MONITOR_REPAINT @@ -1029,11 +1032,7 @@ paint_all(Display *dpy, XserverRegion region) { #endif if (clip_changed) { - if (w->border_size) { - set_ignore(dpy, NextRequest(dpy)); - XFixesDestroyRegion(dpy, w->border_size); - w->border_size = None; - } + win_free_border_size(dpy, w); if (w->extents) { XFixesDestroyRegion(dpy, w->extents); w->extents = None; @@ -1376,6 +1375,10 @@ map_win(Display *dpy, Window id, so that no property changes are lost */ if (!override_redirect) { XSelectInput(dpy, id, PropertyChangeMask | FocusChangeMask); + // Notify compton when the shape of a window changes + if (shape_exists) { + XShapeSelectInput(dpy, id, ShapeNotifyMask); + } } // this causes problems for inactive transparency @@ -1429,11 +1432,7 @@ finish_unmap_win(Display *dpy, win *w) { w->picture = None; } - if (w->border_size) { - set_ignore(dpy, NextRequest(dpy)); - XFixesDestroyRegion(dpy, w->border_size); - w->border_size = None; - } + win_free_border_size(dpy, w); if (w->shadow) { XRenderFreePicture(dpy, w->shadow); @@ -1696,6 +1695,33 @@ restack_win(Display *dpy, win *w, Window new_above) { w->next = *prev; *prev = w; + +#if DEBUG_RESTACK + { + const char *desc; + char *window_name; + Bool to_free; + win* c = list; + + printf("restack_win(%#010lx, %#010lx): Window stack modified. Current stack:\n", w->id, new_above); + for (; c; c = c->next) { + window_name = "(Failed to get title)"; + if (root == c->id) + window_name = "(Root window)"; + else + to_free = window_get_name(c->id, &window_name); + desc = ""; + if (c->destroyed) + desc = "(D) "; + printf("%#010lx \"%s\" %s-> ", c->id, window_name, desc); + if (to_free) { + XFree(window_name); + window_name = NULL; + } + } + fputs("\n", stdout); + } +#endif } } @@ -1961,6 +1987,60 @@ error(Display *dpy, XErrorEvent *ev) { break; } + switch (ev->error_code) { + case BadAccess: + name = "BadAccess"; + break; + case BadAlloc: + name = "BadAlloc"; + break; + case BadAtom: + name = "BadAtom"; + break; + case BadColor: + name = "BadColor"; + break; + case BadCursor: + name = "BadCursor"; + break; + case BadDrawable: + name = "BadDrawable"; + break; + case BadFont: + name = "BadFont"; + break; + case BadGC: + name = "BadGC"; + break; + case BadIDChoice: + name = "BadIDChoice"; + break; + case BadImplementation: + name = "BadImplementation"; + break; + case BadLength: + name = "BadLength"; + break; + case BadMatch: + name = "BadMatch"; + break; + case BadName: + name = "BadName"; + break; + case BadPixmap: + name = "BadPixmap"; + break; + case BadRequest: + name = "BadRequest"; + break; + case BadValue: + name = "BadValue"; + break; + case BadWindow: + name = "BadWindow"; + break; + } + printf("error %d (%s) request %d minor %d serial %lu\n", ev->error_code, name, ev->request_code, ev->minor_code, ev->serial); @@ -1974,10 +2054,36 @@ expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) { add_damage(dpy, region); } -#if DEBUG_EVENTS +#if DEBUG_EVENTS || DEBUG_RESTACK +static int window_get_name(Window w, char **name) { + Atom prop = XInternAtom(dpy, "_NET_WM_NAME", False); + Atom utf8_type = XInternAtom(dpy, "UTF8_STRING", False); + Atom actual_type; + int actual_format; + unsigned long nitems; + unsigned long leftover; + char *data = NULL; + Status ret; + + set_ignore(dpy, NextRequest(dpy)); + if (Success != (ret = XGetWindowProperty(dpy, w, prop, 0L, (long) BUFSIZ, + False, utf8_type, &actual_type, &actual_format, &nitems, + &leftover, (unsigned char **) &data))) { + if (BadWindow == ret) + return 0; + set_ignore(dpy, NextRequest(dpy)); + printf("Window %#010lx: _NET_WM_NAME unset, falling back to WM_NAME.\n", w); + if (!XFetchName(dpy, w, &data)) + return 0; + } + // if (actual_type == utf8_type && actual_format == 8) + *name = (char *) data; + return 1; +} + static int ev_serial(XEvent *ev) { - if (ev->type & 0x7f != KeymapNotify) { + if ((ev->type & 0x7f) != KeymapNotify) { return ev->xany.serial; } return NextRequest(ev->xany.display); @@ -1987,8 +2093,16 @@ static char * ev_name(XEvent *ev) { static char buf[128]; switch (ev->type & 0x7f) { - case Expose: - return "Expose"; + case FocusIn: + return "FocusIn"; + case FocusOut: + return "FocusOut"; + case CreateNotify: + return "CreateNotify"; + case ConfigureNotify: + return "ConfigureNotify"; + case DestroyNotify: + return "DestroyNotify"; case MapNotify: return "Map"; case UnmapNotify: @@ -1997,10 +2111,16 @@ ev_name(XEvent *ev) { return "Reparent"; case CirculateNotify: return "Circulate"; + case Expose: + return "Expose"; + case PropertyNotify: + return "PropertyNotify"; default: if (ev->type == damage_event + XDamageNotify) { return "Damage"; } + if (shape_exists && ev->type == shape_event) + return "ShapeNotify"; sprintf(buf, "Event %d", ev->type); return buf; } @@ -2009,8 +2129,13 @@ ev_name(XEvent *ev) { static Window ev_window(XEvent *ev) { switch (ev->type) { - case Expose: - return ev->xexpose.window; + case FocusIn: + case FocusOut: + return ev->xfocus.window; + case CreateNotify: + return ev->xcreatewindow.window; + case ConfigureNotify: + return ev->xconfigure.window; case MapNotify: return ev->xmap.window; case UnmapNotify: @@ -2019,10 +2144,16 @@ ev_window(XEvent *ev) { return ev->xreparent.window; case CirculateNotify: return ev->xcirculate.window; + case Expose: + return ev->xexpose.window; + case PropertyNotify: + return ev->xproperty.window; default: if (ev->type == damage_event + XDamageNotify) { return ((XDamageNotifyEvent *)ev)->drawable; } + if (shape_exists && ev->type == shape_event) + return ((XShapeEvent *) ev)->window; return 0; } } @@ -2068,6 +2199,9 @@ ev_create_notify(XCreateWindowEvent *ev) { inline static void ev_configure_notify(XConfigureEvent *ev) { +#if DEBUG_EVENTS + printf("{ send_event: %d, above: %#010lx, override_redirect: %d }\n", ev->send_event, ev->above, ev->override_redirect); +#endif configure_win(dpy, ev); } @@ -2168,16 +2302,56 @@ ev_damage_notify(XDamageNotifyEvent *ev) { damage_win(dpy, ev); } +static void ev_shape_notify(XShapeEvent *ev) { + win *w = find_win(dpy, ev->window); + + /* + * Empty border_size may indicated an + * unmapped/destroyed window, in which case + * seemingly BadRegion errors would be triggered + * if we attempt to rebuild border_size + */ + if (w->border_size) { + // Mark the old border_size as damaged + add_damage(dpy, w->border_size); + + w->border_size = border_size(dpy, w); + + // Mark the new border_size as damaged + add_damage(dpy, copy_region(dpy, w->border_size)); + } + +} + inline static void ev_handle(XEvent *ev) { + +#if DEBUG_EVENTS + Window w; + char *window_name; + Bool to_free = False; +#endif + if ((ev->type & 0x7f) != KeymapNotify) { discard_ignore(dpy, ev->xany.serial); } #if DEBUG_EVENTS + w = ev_window(ev); + window_name = "(Failed to get title)"; + if (w) { + if (root == w) + window_name = "(Root window)"; + else + to_free = (Bool) window_get_name(w, &window_name); + } 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)); + printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", + ev_name(ev), ev_serial(ev), w, window_name); + } + if (to_free) { + XFree(window_name); + window_name = NULL; } #endif @@ -2216,6 +2390,10 @@ ev_handle(XEvent *ev) { ev_property_notify((XPropertyEvent *)ev); break; default: + if (shape_exists && ev->type == shape_event) { + ev_shape_notify((XShapeEvent *) ev); + break; + } if (ev->type == damage_event + XDamageNotify) { ev_damage_notify((XDamageNotifyEvent *)ev); } @@ -2528,6 +2706,9 @@ main(int argc, char **argv) { exit(1); } + if (!XShapeQueryExtension(dpy, &shape_event, &shape_error)) + shape_exists = False; + register_cm(scr); if (fork_after_register) fork_after(); diff --git a/compton.h b/compton.h index 18e385989..6f30391d0 100644 --- a/compton.h +++ b/compton.h @@ -20,6 +20,7 @@ #include #include #include +#include #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 #define HAS_NAME_WINDOW_PIXMAP 1 @@ -28,6 +29,7 @@ #define CAN_DO_USABLE 0 #define DEBUG_REPAINT 0 #define DEBUG_EVENTS 0 +#define DEBUG_RESTACK 0 #define DEBUG_WINTYPE 0 #define MONITOR_REPAINT 0 @@ -124,6 +126,7 @@ typedef struct _fade { Display *dpy; } fade; +extern int root_height, root_width; /** * Functions */ @@ -345,6 +348,42 @@ ev_property_notify(XPropertyEvent *ev); inline static void ev_damage_notify(XDamageNotifyEvent *ev); +/** + * Destory the cached border_size of a window. + */ +inline static void win_free_border_size(Display *dpy, win *w) { + if (w->border_size) { + set_ignore(dpy, NextRequest(dpy)); + XFixesDestroyRegion(dpy, w->border_size); + w->border_size = None; + } +} + +/** + * Get a region of the screen size. + */ +inline static XserverRegion get_screen_region(Display *dpy) { + XRectangle r; + + r.x = 0; + r.y = 0; + r.width = root_width; + r.height = root_height; + return XFixesCreateRegion(dpy, &r, 1); +} + +/** + * Copies a region + */ +inline static XserverRegion copy_region(Display *dpy, + XserverRegion oldregion) { + XserverRegion region = XFixesCreateRegion(dpy, NULL, 0); + + XFixesCopyRegion(dpy, region, oldregion); + + return region; +} + inline static void ev_handle(XEvent *ev); From bbf35f81556f3309b320705db1cd98830b4cf552 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 11 Sep 2012 21:57:50 +0800 Subject: [PATCH 012/178] Feature: Issue #35, Add colored shadows - Use getopt_long() instead of getopt() for argument parsing, making long options possible. - Add support of colored shadows with 3 commandline switches. --- compton.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- compton.h | 12 ++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/compton.c b/compton.c index c3af63213..9f5e25958 100644 --- a/compton.c +++ b/compton.c @@ -27,6 +27,7 @@ Window root; Picture root_picture; Picture root_buffer; Picture black_picture; +Picture cshadow_picture; Picture root_tile; XserverRegion all_damage; Bool clip_changed; @@ -1102,7 +1103,7 @@ paint_all(Display *dpy, XserverRegion region) { if (win_type_shadow[w->window_type]) { XRenderComposite( - dpy, PictOpOver, black_picture, w->shadow, + dpy, PictOpOver, cshadow_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); @@ -2448,7 +2449,14 @@ usage() { "-b daemonize\n" " Daemonize process.\n" "-S\n" - " Enable synchronous operation (for debugging).\n"); + " Enable synchronous operation (for debugging).\n" + "--shadow-red value\n" + " Red color value of shadow (0.0 - 1.0, defaults to 0).\n" + "--shadow-green value\n" + " Green color value of shadow (0.0 - 1.0, defaults to 0).\n" + "--shadow-blue value\n" + " Blue color value of shadow (0.0 - 1.0, defaults to 0).\n" + ); exit(1); } @@ -2549,6 +2557,12 @@ get_atoms() { int main(int argc, char **argv) { + const static struct option longopt[] = { + { "shadow-red", required_argument, NULL, 0 }, + { "shadow-green", required_argument, NULL, 0 }, + { "shadow-blue", required_argument, NULL, 0 }, + }; + XEvent ev; Window root_return, parent_return; Window *children; @@ -2559,9 +2573,13 @@ main(int argc, char **argv) { int composite_major, composite_minor; char *display = 0; int o; + int longopt_idx; Bool no_dock_shadow = False; Bool no_dnd_shadow = False; Bool fork_after_register = False; + double shadow_red = 0.0; + double shadow_green = 0.0; + double shadow_blue = 0.0; for (i = 0; i < NUM_WINTYPES; ++i) { win_type_fade[i] = False; @@ -2569,8 +2587,25 @@ main(int argc, char **argv) { win_type_opacity[i] = 1.0; } - while ((o = getopt(argc, argv, "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb")) != -1) { + while ((o = getopt_long(argc, argv, + "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb", + longopt, &longopt_idx)) != -1) { switch (o) { + // Long options + case 0: + switch (longopt_idx) { + case 0: + shadow_red = normalize_d(atof(optarg)); + break; + case 1: + shadow_green = normalize_d(atof(optarg)); + break; + case 2: + shadow_blue = normalize_d(atof(optarg)); + break; + } + break; + // Short options case 'd': display = optarg; break; @@ -2729,6 +2764,14 @@ main(int argc, char **argv) { black_picture = solid_picture(dpy, True, 1, 0, 0, 0); + // Generates another Picture for shadows if the color is modified by + // user + if (!shadow_red && !shadow_green && !shadow_blue) + cshadow_picture = black_picture; + else + cshadow_picture = solid_picture(dpy, True, 1, + shadow_red, shadow_green, shadow_blue); + all_damage = None; clip_changed = True; XGrabServer(dpy); diff --git a/compton.h b/compton.h index 6f30391d0..517dc462e 100644 --- a/compton.h +++ b/compton.h @@ -131,6 +131,18 @@ extern int root_height, root_width; * Functions */ +// inline functions must be made static to compile correctly under clang: +// http://clang.llvm.org/compatibility.html#inline + +static inline double normalize_d(double d) { + if (d > 1.0) + return 1.0; + if (d < 0.0) + return 0.0; + + return d; +} + static int get_time_in_milliseconds(); From 00d29b07488415661f9306b9cd5fffc855c9c358 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 11 Sep 2012 22:22:58 +0800 Subject: [PATCH 013/178] Bug fix: Issue #37, fix 5 opacity-related bugs More details in the bug report. - Rewritten much of the opacity calculation, code cleanup. - Commandline switch --inactive_opacity_override to restore the old behavior in which inactive_opacity has higher priority than _NET_WM_OPACITY. --- compton.c | 167 ++++++++++++++++++++++++++++++++++++++---------------- compton.h | 41 ++++++++++++-- 2 files changed, 154 insertions(+), 54 deletions(-) diff --git a/compton.c b/compton.c index 9f5e25958..9e1eef336 100644 --- a/compton.c +++ b/compton.c @@ -81,9 +81,6 @@ 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)) @@ -108,8 +105,14 @@ Bool fade_trans = False; Bool clear_shadow = False; -double inactive_opacity = 0; -double frame_opacity = 0; +/// Default opacity for inactive windows. +/// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for +/// not enabled, default. +opacity_t inactive_opacity = 0; +/// Whether inactive_opacity overrides the opacity set by window +/// attributes. +Bool inactive_opacity_override = False; +double frame_opacity = 0.0; Bool synchronize = False; @@ -203,21 +206,7 @@ set_fade(Display *dpy, win *w, double start, } 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); - } + set_opacity(dpy, w, f->cur * OPAQUE); /* fading windows need to be drawn, mark them as damaged. when a window maps, @@ -1364,6 +1353,7 @@ map_win(Display *dpy, Window id, if (!w) return; + w->focused = False; w->a.map_state = IsViewable; w->window_type = determine_wintype(dpy, w->id, w->id); @@ -1382,8 +1372,7 @@ map_win(Display *dpy, Window id, } } - // this causes problems for inactive transparency - //w->opacity = get_opacity_prop(dpy, w, OPAQUE); + calc_opacity(dpy, w, True); determine_mode(dpy, w); @@ -1481,8 +1470,7 @@ unmap_win(Display *dpy, Window id, Bool fade) { finish_unmap_win(dpy, w); } -static unsigned int -get_opacity_prop(Display *dpy, win *w, unsigned int def) { +opacity_t get_opacity_prop(Display *dpy, win *w, opacity_t def) { Atom actual; int format; unsigned long n, left; @@ -1493,9 +1481,8 @@ get_opacity_prop(Display *dpy, win *w, unsigned int def) { XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL) { - unsigned int i; - memcpy(&i, data, sizeof(unsigned int)); - XFree((void *)data); + opacity_t i = *((opacity_t *) data); + XFree(data); return i; } @@ -1504,11 +1491,7 @@ get_opacity_prop(Display *dpy, win *w, unsigned int 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; + return w->opacity * 1.0 / OPAQUE; } static void @@ -1558,8 +1541,11 @@ determine_mode(Display *dpy, win *w) { } } -static void -set_opacity(Display *dpy, win *w, unsigned long opacity) { +void set_opacity(Display *dpy, win *w, opacity_t opacity) { + // Do nothing if the opacity does not change + if (w->opacity == opacity) + return; + w->opacity = opacity; determine_mode(dpy, w); if (w->shadow) { @@ -1575,6 +1561,53 @@ set_opacity(Display *dpy, win *w, unsigned long opacity) { } } +/** + * Calculate and set the opacity of a window. + * + * If window is inactive and inactive_opacity_override is set, the + * priority is: (Simulates the old behavior) + * + * inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) + * > window type default opacity + * + * Otherwise: + * + * _NET_WM_WINDOW_OPACITY (if not opaque) + * > window type default opacity (if not opaque) + * > inactive_opacity + * + * @param dpy X display to use + * @param w struct _win object representing the window + * @param refetch_prop whether _NET_WM_OPACITY of the window needs to be + * refetched + */ +void calc_opacity(Display *dpy, win *w, Bool refetch_prop) { + opacity_t opacity; + + // Do nothing for unmapped window, calc_opacity() will be called + // when it's mapped + // I suppose I need not to check for IsUnviewable here? + if (IsViewable != w->a.map_state) + return; + + // Do not refetch the opacity window attribute unless necessary, this + // is probably an expensive operation in some cases + if (refetch_prop) + w->opacity_prop = get_opacity_prop(dpy, w, OPAQUE); + + if (OPAQUE == (opacity = w->opacity_prop)) { + if (OPAQUE != win_type_opacity[w->window_type]) + opacity = win_type_opacity[w->window_type] * OPAQUE; + } + + // Respect inactive_opacity in some cases + if (IS_NORMAL_WIN(w) && False == w->focused && inactive_opacity + && (OPAQUE == opacity || inactive_opacity_override)) + opacity = inactive_opacity; + + set_opacity(dpy, w, opacity); +} + static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { if (find_win(dpy, id)) { @@ -1632,6 +1665,8 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_width = 0; new->shadow_height = 0; new->opacity = OPAQUE; + new->opacity_prop = OPAQUE; + new->focused = False; new->destroyed = False; new->need_configure = False; new->window_type = WINTYPE_UNKNOWN; @@ -1661,9 +1696,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { 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); } } @@ -1875,7 +1907,7 @@ destroy_win(Display *dpy, Window id, Bool fade) { #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, + set_fade(dpy, w, get_opacity_percent(dpy, w), 0.0, fade_out_step, destroy_callback, False, True); } else @@ -2169,9 +2201,9 @@ 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); - } + + w->focused = True; + calc_opacity(dpy, w, False); } inline static void @@ -2188,9 +2220,9 @@ ev_focus_out(XFocusChangeEvent *ev) { } win *w = find_win(dpy, ev->window); - if (IS_NORMAL_WIN(w)) { - set_opacity(dpy, w, INACTIVE_OPACITY); - } + + w->focused = False; + calc_opacity(dpy, w, False); } inline static void @@ -2282,9 +2314,7 @@ ev_property_notify(XPropertyEvent *ev) { /* 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))); + calc_opacity(dpy, w, True); } } @@ -2456,6 +2486,8 @@ usage() { " Green color value of shadow (0.0 - 1.0, defaults to 0).\n" "--shadow-blue value\n" " Blue color value of shadow (0.0 - 1.0, defaults to 0).\n" + "--inactive-opacity-override\n" + " Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n" ); exit(1); @@ -2561,6 +2593,7 @@ main(int argc, char **argv) { { "shadow-red", required_argument, NULL, 0 }, { "shadow-green", required_argument, NULL, 0 }, { "shadow-blue", required_argument, NULL, 0 }, + { "inactive-opacity-override", no_argument, NULL, 0 }, }; XEvent ev; @@ -2603,6 +2636,9 @@ main(int argc, char **argv) { case 2: shadow_blue = normalize_d(atof(optarg)); break; + case 3: + inactive_opacity_override = True; + break; } break; // Short options @@ -2664,10 +2700,12 @@ main(int argc, char **argv) { shadow_offset_y = atoi(optarg); break; case 'i': - inactive_opacity = (double)atof(optarg); + inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); + if (OPAQUE == inactive_opacity) + inactive_opacity = 0; break; case 'e': - frame_opacity = (double)atof(optarg); + frame_opacity = normalize_d(atof(optarg)); break; case 'z': clear_shadow = True; @@ -2792,6 +2830,37 @@ main(int argc, char **argv) { add_win(dpy, children[i], i ? children[i-1] : None, False); } + // Check the currently focused window so we can apply appropriate + // opacity on it + { + Window wid = 0; + int revert_to; + win *w = NULL; + + XGetInputFocus(dpy, &wid, &revert_to); + + // XGetInputFocus seemingly returns the application window focused + // instead of the WM window frame, so we traverse through its + // ancestors to find out the frame + while(wid && wid != root + && !array_wid_exists(children, nchildren, wid)) { + Window troot; + Window parent; + Window *tchildren; + unsigned tnchildren; + + XQueryTree(dpy, wid, &troot, &parent, &tchildren, &tnchildren); + XFree(tchildren); + wid = parent; + } + + // And we set the focus state and opacity here + if (wid && wid != root && (w = find_win(dpy, wid))) { + w->focused = True; + calc_opacity(dpy, w, False); + } + } + XFree(children); XUngrabServer(dpy); diff --git a/compton.h b/compton.h index 517dc462e..21cccd826 100644 --- a/compton.h +++ b/compton.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ * Types */ +typedef uint32_t opacity_t; + typedef enum { WINTYPE_UNKNOWN, WINTYPE_DESKTOP, @@ -94,8 +97,12 @@ typedef struct _win { int shadow_dy; int shadow_width; int shadow_height; - unsigned int opacity; + opacity_t opacity; + /// Cached value of opacity window attribute. + opacity_t opacity_prop; wintype window_type; + /// Whether the window is focused. + Bool focused; unsigned long damage_sequence; /* sequence when damage was created */ Bool destroyed; unsigned int left_width; @@ -127,6 +134,7 @@ typedef struct _fade { } fade; extern int root_height, root_width; + /** * Functions */ @@ -134,6 +142,11 @@ extern int root_height, root_width; // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline +/** + * Normalize a double value to 0.\ 0 - 1.\ 0. + * + * @param d double value to normalize + */ static inline double normalize_d(double d) { if (d > 1.0) return 1.0; @@ -143,6 +156,23 @@ static inline double normalize_d(double d) { return d; } +/** + * Check if a window ID exists in an array of window IDs. + * + * @param arr the array of window IDs + * @param count amount of elements in the array + * @param wid window ID to search for + */ +static inline Bool array_wid_exists(const Window *arr, + int count, Window wid) { + while (count--) { + if (arr[count] == wid) + return True; + } + + return False; +} + static int get_time_in_milliseconds(); @@ -263,8 +293,8 @@ unmap_callback(Display *dpy, win *w); static void unmap_win(Display *dpy, Window id, Bool fade); -static unsigned int -get_opacity_prop(Display *dpy, win *w, unsigned int def); +opacity_t +get_opacity_prop(Display *dpy, win *w, opacity_t def); static double get_opacity_percent(Display *dpy, win *w); @@ -272,8 +302,9 @@ 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); +void set_opacity(Display *dpy, win *w, opacity_t opacity); + +void calc_opacity(Display *dpy, win *w, Bool refetch_prop); static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect); From 0c077a76d1a8ce50e889c2caa31815c59926bda3 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 11 Sep 2012 23:11:23 +0800 Subject: [PATCH 014/178] Bug fix: Segfault when encountering invalid long option I didn't read the documentation of getopt_long() carefully. --- compton.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compton.c b/compton.c index 9e1eef336..a281b31b3 100644 --- a/compton.c +++ b/compton.c @@ -2594,6 +2594,8 @@ main(int argc, char **argv) { { "shadow-green", required_argument, NULL, 0 }, { "shadow-blue", required_argument, NULL, 0 }, { "inactive-opacity-override", no_argument, NULL, 0 }, + // Must terminate with a NULL entry + { NULL, 0, NULL, 0 }, }; XEvent ev; From 7ab11dfa666ee9341ddd0eb82e3e6d0117e42e8f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 12 Sep 2012 09:08:15 +0800 Subject: [PATCH 015/178] Debug: Enhanced debugging capability - Change all #if DEBUG_XXX directives to #ifdef, thus making it possible to directly enable debugging options with CFLAGS (-DDEBUG_XXX). - Print timestamp before event debugging messages. --- compton.c | 41 ++++++++++++++++++------------- compton.h | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/compton.c b/compton.c index a281b31b3..20fc8f841 100644 --- a/compton.c +++ b/compton.c @@ -10,14 +10,14 @@ #include "compton.h" -#if DEBUG_EVENTS -static int window_get_name(Window w, char **name); -#endif - /** * Shared */ +#ifdef DEBUG_EVENTS +struct timeval time_start = { 0, 0 }; +#endif + win *list; fade *fades; Display *dpy; @@ -955,7 +955,7 @@ paint_all(Display *dpy, XserverRegion region) { region = get_screen_region(dpy); } -#if MONITOR_REPAINT +#ifdef MONITOR_REPAINT root_buffer = root_picture; #else if (!root_buffer) { @@ -973,14 +973,14 @@ paint_all(Display *dpy, XserverRegion region) { XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); -#if MONITOR_REPAINT +#ifdef MONITOR_REPAINT XRenderComposite( dpy, PictOpSrc, black_picture, None, root_picture, 0, 0, 0, 0, 0, 0, root_width, root_height); #endif -#if DEBUG_REPAINT +#ifdef DEBUG_REPAINT printf("paint:"); #endif @@ -1017,7 +1017,7 @@ paint_all(Display *dpy, XserverRegion region) { dpy, draw, format, CPSubwindowMode, &pa); } -#if DEBUG_REPAINT +#ifdef DEBUG_REPAINT printf(" 0x%x", w->id); #endif @@ -1077,7 +1077,7 @@ paint_all(Display *dpy, XserverRegion region) { t = w; } -#if DEBUG_REPAINT +#ifdef DEBUG_REPAINT printf("\n"); fflush(stdout); #endif @@ -1212,7 +1212,7 @@ repair_win(Display *dpy, win *w) { w->damaged = 1; } -#if DEBUG_WINTYPE +#ifdef DEBUG_WINTYPE static const char * wintype_name(wintype type) { const char *t; @@ -1357,7 +1357,7 @@ map_win(Display *dpy, Window id, w->a.map_state = IsViewable; w->window_type = determine_wintype(dpy, w->id, w->id); -#if DEBUG_WINTYPE +#ifdef DEBUG_WINTYPE printf("window 0x%x type %s\n", w->id, wintype_name(w->window_type)); #endif @@ -1729,7 +1729,7 @@ restack_win(Display *dpy, win *w, Window new_above) { w->next = *prev; *prev = w; -#if DEBUG_RESTACK +#ifdef DEBUG_RESTACK { const char *desc; char *window_name; @@ -2087,7 +2087,7 @@ expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) { add_damage(dpy, region); } -#if DEBUG_EVENTS || DEBUG_RESTACK +#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) static int window_get_name(Window w, char **name) { Atom prop = XInternAtom(dpy, "_NET_WM_NAME", False); Atom utf8_type = XInternAtom(dpy, "UTF8_STRING", False); @@ -2104,7 +2104,7 @@ static int window_get_name(Window w, char **name) { &leftover, (unsigned char **) &data))) { if (BadWindow == ret) return 0; - set_ignore(dpy, NextRequest(dpy)); + set_ignore(dpy, NextRequest(dpy)); printf("Window %#010lx: _NET_WM_NAME unset, falling back to WM_NAME.\n", w); if (!XFetchName(dpy, w, &data)) return 0; @@ -2113,7 +2113,9 @@ static int window_get_name(Window w, char **name) { *name = (char *) data; return 1; } +#endif +#ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) { @@ -2232,7 +2234,7 @@ ev_create_notify(XCreateWindowEvent *ev) { inline static void ev_configure_notify(XConfigureEvent *ev) { -#if DEBUG_EVENTS +#ifdef DEBUG_EVENTS printf("{ send_event: %d, above: %#010lx, override_redirect: %d }\n", ev->send_event, ev->above, ev->override_redirect); #endif configure_win(dpy, ev); @@ -2357,7 +2359,7 @@ static void ev_shape_notify(XShapeEvent *ev) { inline static void ev_handle(XEvent *ev) { -#if DEBUG_EVENTS +#ifdef DEBUG_EVENTS Window w; char *window_name; Bool to_free = False; @@ -2367,7 +2369,7 @@ ev_handle(XEvent *ev) { discard_ignore(dpy, ev->xany.serial); } -#if DEBUG_EVENTS +#ifdef DEBUG_EVENTS w = ev_window(ev); window_name = "(Failed to get title)"; if (w) { @@ -2377,6 +2379,7 @@ ev_handle(XEvent *ev) { to_free = (Bool) window_get_name(w, &window_name); } if (ev->type != damage_event + XDamageNotify) { + print_timestamp(); printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", ev_name(ev), ev_serial(ev), w, window_name); } @@ -2616,6 +2619,10 @@ main(int argc, char **argv) { double shadow_green = 0.0; double shadow_blue = 0.0; +#ifdef DEBUG_EVENTS + gettimeofday(&time_start, NULL); +#endif + for (i = 0; i < NUM_WINTYPES; ++i) { win_type_fade[i] = False; win_type_shadow[i] = False; diff --git a/compton.h b/compton.h index 21cccd826..451ea3d53 100644 --- a/compton.h +++ b/compton.h @@ -28,11 +28,19 @@ #endif #define CAN_DO_USABLE 0 -#define DEBUG_REPAINT 0 -#define DEBUG_EVENTS 0 -#define DEBUG_RESTACK 0 -#define DEBUG_WINTYPE 0 -#define MONITOR_REPAINT 0 + +// Debug options, enable them using -D in CFLAGS +// #define DEBUG_REPAINT 1 +// #define DEBUG_EVENTS 1 +// #define DEBUG_RESTACK 1 +// #define DEBUG_WINTYPE 1 +// #define MONITOR_REPAINT 1 + +#ifdef DEBUG_EVENTS +// For printing timestamps +#include +extern struct timeval time_start; +#endif #define OPAQUE 0xffffffff #define REGISTER_PROP "_NET_WM_CM_S" @@ -173,6 +181,55 @@ static inline Bool array_wid_exists(const Window *arr, return False; } +#ifdef DEBUG_EVENTS +/* + * Subtracting two struct timeval values. + * + * Taken from glibc manual. + * + * Subtract the `struct timeval' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. */ +int timeval_subtract (result, x, y) + struct timeval *result, *x, *y; +{ + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_usec < y->tv_usec) { + int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + if (x->tv_usec - y->tv_usec > 1000000) { + int nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_usec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * Print time passed since program starts execution. + * + * Used for debugging. + */ +static inline void print_timestamp(void) { + struct timeval tm, diff; + + if (gettimeofday(&tm, NULL)) + return; + + timeval_subtract(&diff, &tm, &time_start); + printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); +} +#endif + static int get_time_in_milliseconds(); @@ -338,7 +395,11 @@ error(Display *dpy, XErrorEvent *ev); static void expose_root(Display *dpy, Window root, XRectangle *rects, int nrects); -#if DEBUG_EVENTS +#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) +static int window_get_name(Window w, char **name); +#endif + +#ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev); From 1f271c29531850560e1e50eec3dece70d2ee1dfd Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 12 Sep 2012 10:52:52 +0800 Subject: [PATCH 016/178] Feature: Issue #2: Support dim inactive windows - Add a switch --inactive-dim that dims inactive windows. - The window dimming feature is implemented in a pretty ugly way. Improve it if possible. --- compton.c | 52 ++++++++++++++++++++++++++++++++++++++++++++-------- compton.h | 15 +++++++++++++++ 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/compton.c b/compton.c index 20fc8f841..08e7cf8cd 100644 --- a/compton.c +++ b/compton.c @@ -28,6 +28,8 @@ Picture root_picture; Picture root_buffer; Picture black_picture; Picture cshadow_picture; +/// Picture used for dimming inactive windows. +Picture dim_picture = 0; Picture root_tile; XserverRegion all_damage; Bool clip_changed; @@ -113,6 +115,8 @@ opacity_t inactive_opacity = 0; /// attributes. Bool inactive_opacity_override = False; double frame_opacity = 0.0; +/// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. +double inactive_dim = 0.0; Bool synchronize = False; @@ -1066,6 +1070,10 @@ paint_all(Display *dpy, XserverRegion region) { dpy, PictOpSrc, w->picture, None, root_buffer, 0, 0, 0, 0, x, y, wid, hei); + + if (w->dim) + XRenderComposite(dpy, PictOpOver, dim_picture, None, + root_buffer, 0, 0, 0, 0, x, y, wid, hei); } if (!w->border_clip) { @@ -1164,6 +1172,10 @@ paint_all(Display *dpy, XserverRegion region) { dpy, PictOpOver, w->picture, w->alpha_pict, root_buffer, l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); } + + if (w->dim) + XRenderComposite(dpy, PictOpOver, dim_picture, None, + root_buffer, 0, 0, 0, 0, x, y, wid, hei); } XFixesDestroyRegion(dpy, w->border_clip); @@ -1373,6 +1385,7 @@ map_win(Display *dpy, Window id, } calc_opacity(dpy, w, True); + calc_dim(dpy, w); determine_mode(dpy, w); @@ -1533,12 +1546,7 @@ determine_mode(Display *dpy, win *w) { w->mode = mode; - if (w->extents) { - XserverRegion damage; - damage = XFixesCreateRegion(dpy, 0, 0); - XFixesCopyRegion(dpy, damage, w->extents); - add_damage(dpy, damage); - } + add_damage_win(dpy, w); } void set_opacity(Display *dpy, win *w, opacity_t opacity) { @@ -1608,6 +1616,20 @@ void calc_opacity(Display *dpy, win *w, Bool refetch_prop) { set_opacity(dpy, w, opacity); } +void calc_dim(Display *dpy, win *w) { + Bool dim; + + if (inactive_dim && IS_NORMAL_WIN(w) && !(w->focused)) + dim = True; + else + dim = False; + + if (dim != w->dim) { + w->dim = dim; + add_damage_win(dpy, w); + } +} + static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { if (find_win(dpy, id)) { @@ -1666,6 +1688,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_height = 0; new->opacity = OPAQUE; new->opacity_prop = OPAQUE; + new->dim = False; new->focused = False; new->destroyed = False; new->need_configure = False; @@ -2200,17 +2223,18 @@ ev_window(XEvent *ev) { inline static void ev_focus_in(XFocusChangeEvent *ev) { - if (!inactive_opacity) return; + if (!inactive_opacity && !inactive_dim) return; win *w = find_win(dpy, ev->window); w->focused = True; calc_opacity(dpy, w, False); + calc_dim(dpy, w); } inline static void ev_focus_out(XFocusChangeEvent *ev) { - if (!inactive_opacity) return; + if (!inactive_opacity && !inactive_dim) return; if (ev->mode == NotifyGrab || (ev->mode == NotifyNormal @@ -2225,6 +2249,7 @@ ev_focus_out(XFocusChangeEvent *ev) { w->focused = False; calc_opacity(dpy, w, False); + calc_dim(dpy, w); } inline static void @@ -2491,6 +2516,8 @@ usage() { " Blue color value of shadow (0.0 - 1.0, defaults to 0).\n" "--inactive-opacity-override\n" " Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n" + "--inactive-dim value\n" + " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n" ); exit(1); @@ -2597,6 +2624,7 @@ main(int argc, char **argv) { { "shadow-green", required_argument, NULL, 0 }, { "shadow-blue", required_argument, NULL, 0 }, { "inactive-opacity-override", no_argument, NULL, 0 }, + { "inactive-dim", required_argument, NULL, 0 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -2648,6 +2676,9 @@ main(int argc, char **argv) { case 3: inactive_opacity_override = True; break; + case 4: + inactive_dim = normalize_d(atof(optarg)); + break; } break; // Short options @@ -2819,6 +2850,10 @@ main(int argc, char **argv) { cshadow_picture = solid_picture(dpy, True, 1, shadow_red, shadow_green, shadow_blue); + // Generates a picture for inactive_dim + if (inactive_dim) + dim_picture = solid_picture(dpy, True, inactive_dim, 0, 0, 0); + all_damage = None; clip_changed = True; XGrabServer(dpy); @@ -2867,6 +2902,7 @@ main(int argc, char **argv) { if (wid && wid != root && (w = find_win(dpy, wid))) { w->focused = True; calc_opacity(dpy, w, False); + calc_dim(dpy, w); } } diff --git a/compton.h b/compton.h index 451ea3d53..ea906969d 100644 --- a/compton.h +++ b/compton.h @@ -108,6 +108,8 @@ typedef struct _win { opacity_t opacity; /// Cached value of opacity window attribute. opacity_t opacity_prop; + /// Whether the window is to be dimmed. + Bool dim; wintype window_type; /// Whether the window is focused. Bool focused; @@ -363,6 +365,8 @@ void set_opacity(Display *dpy, win *w, opacity_t opacity); void calc_opacity(Display *dpy, win *w, Bool refetch_prop); +void calc_dim(Display *dpy, win *w); + static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect); @@ -488,6 +492,17 @@ inline static XserverRegion copy_region(Display *dpy, return region; } +/** + * Add a window to damaged area. + * + * @param dpy display in use + * @param w struct _win element representing the window + */ +static inline void add_damage_win(Display *dpy, win *w) { + if (w->extents) + add_damage(dpy, copy_region(dpy, w->extents)); +} + inline static void ev_handle(XEvent *ev); From 05b229f2a0405f5eec0af1ba55a71923ed192d55 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 12 Sep 2012 11:06:16 +0800 Subject: [PATCH 017/178] Bug fix: Issue #39: Render windows just mapped && focused incorrectly More info in the issue description. This also fixes the problem for --inactive-dim. --- compton.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/compton.c b/compton.c index 08e7cf8cd..ef157953c 100644 --- a/compton.c +++ b/compton.c @@ -1384,6 +1384,50 @@ map_win(Display *dpy, Window id, } } + /* + * Occasionally compton does not seem able to get a FocusIn event from a + * window just mapped. I suspect it's a timing issue again when the + * XSelectInput() is called too late. If this is the case, I could think + * of two fixes: To monitor the focus events from the root window, and + * to determine if the current window is focused in map_win(). Looks + * like the XFocusChangeEvent sent to the root window contains no + * information about where the WM frame of the focused window is, and + * XGetInputFocus() often returns an application window instead of the + * WM frame, which compton keeps track of, in either way I believe we + * have to travel through the ancestors of the focused window it + * returns. The latter choice looks cheaper, so I'm doing it here. + * But still, this could anyway be costly. + * + * An alternative route might be relying on _NET_ACTIVE_WINDOW. + * Unfortunately as it's set by WM I'm not completely sure if it's + * reliable and will be updated on the very moment a window is mapped. + */ + { + Window wid = id; + int revert_to; + win *w = NULL; + + XGetInputFocus(dpy, &wid, &revert_to); + + // XGetInputFocus seemingly returns the application window focused + // instead of the WM window frame, so we traverse through its + // ancestors to find out the frame + while(wid && wid != root && !find_win(dpy, wid)) { + Window troot; + Window parent; + Window *tchildren; + unsigned tnchildren; + + XQueryTree(dpy, wid, &troot, &parent, &tchildren, &tnchildren); + XFree(tchildren); + wid = parent; + } + + // And we set the focus state + if (wid && wid != root && (w = find_win(dpy, wid))) + w->focused = True; + } + calc_opacity(dpy, w, True); calc_dim(dpy, w); From 17b8a50161ab84a7b1d05d69b14a81d9a98e4432 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 12 Sep 2012 12:14:24 +0800 Subject: [PATCH 018/178] Bug fix: Double free when XQueryTree() fails Take care of failure of XQueryTree() to prevent it from causing a double-free crash. This usually happens when X is initializing and windows are constantly changing. --- compton.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/compton.c b/compton.c index ef157953c..3101771f2 100644 --- a/compton.c +++ b/compton.c @@ -1022,7 +1022,7 @@ paint_all(Display *dpy, XserverRegion region) { } #ifdef DEBUG_REPAINT - printf(" 0x%x", w->id); + printf(" %#010lx", w->id); #endif if (clip_changed) { @@ -1418,8 +1418,17 @@ map_win(Display *dpy, Window id, Window *tchildren; unsigned tnchildren; - XQueryTree(dpy, wid, &troot, &parent, &tchildren, &tnchildren); - XFree(tchildren); + // XQueryTree probably fails if you run compton when X is somehow + // initializing (like add it in .xinitrc). In this case + // just leave it alone. + if(!XQueryTree(dpy, wid, &troot, &parent, &tchildren, + &tnchildren)) { + wid = 0; + break; + } + + if (tchildren) + XFree(tchildren); wid = parent; } @@ -2934,11 +2943,20 @@ main(int argc, char **argv) { && !array_wid_exists(children, nchildren, wid)) { Window troot; Window parent; - Window *tchildren; + Window *tchildren = 0; unsigned tnchildren; - XQueryTree(dpy, wid, &troot, &parent, &tchildren, &tnchildren); - XFree(tchildren); + // XQueryTree probably fails if you run compton when X is somehow + // initializing (like add it in .xinitrc). In this case + // just leave it alone. + if(!XQueryTree(dpy, wid, &troot, &parent, &tchildren, + &tnchildren)) { + wid = 0; + break; + } + + if (tchildren) + XFree(tchildren); wid = parent; } From 73342d1ff233aa74999998e8cf628d8e956d394f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 12 Sep 2012 21:01:06 +0800 Subject: [PATCH 019/178] Bug fix: Issue #40: -z does not work as expected More information in the issue report. - Let window opacity affect the opacity of its shadow and frames even if -z is enabled. - Check for the range of -o to eliminate potential segfault. --- compton.c | 48 +++++++++++++++++++++++++----------------------- compton.h | 17 +++++++++++++++++ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/compton.c b/compton.c index 3101771f2..d54b52ab2 100644 --- a/compton.c +++ b/compton.c @@ -481,18 +481,19 @@ make_shadow(Display *dpy, double opacity, * center (fill the complete data array) */ - if (!clear_shadow) { + // If clear_shadow is enabled and the border & corner shadow (which + // later will be filled) could entirely cover the area of the shadow + // that will be displayed, do not bother filling other pixels. If it + // can't, we must fill the other pixels here. + if (!(clear_shadow && shadow_offset_x <= 0 && shadow_offset_x >= -cgsize + && shadow_offset_y <= 0 && shadow_offset_y >= -cgsize)) { if (cgsize > 0) { d = shadow_top[opacity_int * (cgsize + 1) + cgsize]; } 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); } /* @@ -556,16 +557,19 @@ make_shadow(Display *dpy, double opacity, } } - // 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; - } - } + if (clear_shadow) { + // Clear the region in the shadow that the window would cover based + // on shadow_offset_{x,y} user provides + int xstart = normalize_i_range(- (int) shadow_offset_x, 0, swidth); + int xrange = normalize_i_range(width - (int) shadow_offset_x, + 0, swidth) - xstart; + int ystart = normalize_i_range(- (int) shadow_offset_y, 0, sheight); + int yend = normalize_i_range(height - (int) shadow_offset_y, + 0, sheight); + int y; + + for (y = ystart; y < yend; y++) + memset(&data[y * swidth + xstart], 0, xrange); } return ximage; @@ -802,14 +806,12 @@ win_extents(Display *dpy, win *w) { if (!w->shadow) { double opacity = shadow_opacity; - if (!clear_shadow) { - if (w->mode != WINDOW_SOLID) { - opacity = opacity * ((double)w->opacity) / ((double)OPAQUE); - } + if (w->mode != WINDOW_SOLID) { + opacity = opacity * ((double)w->opacity) / ((double)OPAQUE); + } - if (HAS_FRAME_OPACITY(w)) { - opacity = opacity * frame_opacity; - } + if (HAS_FRAME_OPACITY(w)) { + opacity = opacity * frame_opacity; } w->shadow = shadow_picture( @@ -2784,7 +2786,7 @@ main(int argc, char **argv) { shadow_radius = atoi(optarg); break; case 'o': - shadow_opacity = atof(optarg); + shadow_opacity = normalize_d(atof(optarg)); break; case 'l': shadow_offset_x = atoi(optarg); diff --git a/compton.h b/compton.h index ea906969d..9a8dd8506 100644 --- a/compton.h +++ b/compton.h @@ -152,6 +152,23 @@ extern int root_height, root_width; // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline +/** + * Normalize an int value to a specific range. + * + * @param i int value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline double normalize_i_range(int i, int min, int max) { + if (i > max) + return max; + if (i < min) + return min; + + return i; +} + /** * Normalize a double value to 0.\ 0 - 1.\ 0. * From a447b5d3101398af1b85cd6eed81b0676982032a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 13 Sep 2012 11:47:31 +0800 Subject: [PATCH 020/178] Improvement: Do not track focus changes unless necessary Stop tracking focus changes unless either inactive_opacity or inactive_dim is enabled, small performance boost in certain cases. --- compton.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/compton.c b/compton.c index d54b52ab2..daab20ed4 100644 --- a/compton.c +++ b/compton.c @@ -118,6 +118,9 @@ double frame_opacity = 0.0; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim = 0.0; +/// Whether compton needs to track focus changes. +Bool track_focus = False; + Bool synchronize = False; /** @@ -1379,7 +1382,10 @@ map_win(Display *dpy, Window id, /* select before reading the property so that no property changes are lost */ if (!override_redirect) { - XSelectInput(dpy, id, PropertyChangeMask | FocusChangeMask); + long evmask = PropertyChangeMask; + if (track_focus) + evmask |= FocusChangeMask; + XSelectInput(dpy, id, evmask); // Notify compton when the shape of a window changes if (shape_exists) { XShapeSelectInput(dpy, id, ShapeNotifyMask); @@ -1404,6 +1410,7 @@ map_win(Display *dpy, Window id, * Unfortunately as it's set by WM I'm not completely sure if it's * reliable and will be updated on the very moment a window is mapped. */ + if (track_focus) { Window wid = id; int revert_to; @@ -1664,7 +1671,7 @@ void calc_opacity(Display *dpy, win *w, Bool refetch_prop) { } // Respect inactive_opacity in some cases - if (IS_NORMAL_WIN(w) && False == w->focused && inactive_opacity + if (inactive_opacity && IS_NORMAL_WIN(w) && False == w->focused && (OPAQUE == opacity || inactive_opacity_override)) opacity = inactive_opacity; @@ -2278,8 +2285,6 @@ ev_window(XEvent *ev) { inline static void ev_focus_in(XFocusChangeEvent *ev) { - if (!inactive_opacity && !inactive_dim) return; - win *w = find_win(dpy, ev->window); w->focused = True; @@ -2289,8 +2294,6 @@ ev_focus_in(XFocusChangeEvent *ev) { inline static void ev_focus_out(XFocusChangeEvent *ev) { - if (!inactive_opacity && !inactive_dim) return; - if (ev->mode == NotifyGrab || (ev->mode == NotifyNormal && (ev->detail == NotifyNonlinear @@ -2831,6 +2834,10 @@ main(int argc, char **argv) { win_type_shadow[WINTYPE_DND] = False; } + // Determine whether we need to track focus changes + if (inactive_opacity || inactive_dim) + track_focus = True; + dpy = XOpenDisplay(display); if (!dpy) { fprintf(stderr, "Can't open display\n"); @@ -2929,9 +2936,10 @@ main(int argc, char **argv) { add_win(dpy, children[i], i ? children[i-1] : None, False); } - // Check the currently focused window so we can apply appropriate - // opacity on it + if (track_focus) { + // Determine the currently focused window so we can apply appropriate + // opacity on it Window wid = 0; int revert_to; win *w = NULL; From 3a0ba85d3b5872221d21891be06bf3c8dfabd851 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 13 Sep 2012 21:38:55 +0800 Subject: [PATCH 021/178] Improvement: Use find_toplevel() to find WM frame Use find_toplevel() to find out the WM frame of a client window. I didn't noticed it beforehand. Fallback to the old method as compton does not always get correct client windows. - Clean up find_client_win() a bit. A BFS search algorithm could be more optimal yet it requires a queue implementation. --- compton.c | 220 ++++++++++++++++++++++++------------------------------ compton.h | 51 ++++++++++++- 2 files changed, 147 insertions(+), 124 deletions(-) diff --git a/compton.c b/compton.c index daab20ed4..50a74d0f8 100644 --- a/compton.c +++ b/compton.c @@ -71,6 +71,7 @@ int size_expose = 0; int n_expose = 0; /* atoms */ +Atom atom_client_attr; Atom extents_atom; Atom opacity_atom; Atom win_type_atom; @@ -715,8 +716,14 @@ find_win(Display *dpy, Window id) { return 0; } -static win * -find_toplevel(Display *dpy, Window id) { +/** + * Find out the WM frame of a client window using existing data. + * + * @param dpy display to use + * @param w window ID + * @return struct _win object of the found window, NULL if not found + */ +win *find_toplevel(Display *dpy, Window id) { win *w; for (w = list; w; w = w->next) { @@ -724,7 +731,72 @@ find_toplevel(Display *dpy, Window id) { return w; } - return 0; + return NULL; +} + +/** + * Find out the WM frame of a client window by querying X. + * + * @param dpy display to use + * @param w window ID + * @return struct _win object of the found window, NULL if not found + */ +win *find_toplevel2(Display *dpy, Window wid) { + win *w = NULL; + + // We traverse through its ancestors to find out the frame + while(wid && wid != root && !(w = find_win(dpy, wid))) { + Window troot; + Window parent; + Window *tchildren; + unsigned tnchildren; + + // XQueryTree probably fails if you run compton when X is somehow + // initializing (like add it in .xinitrc). In this case + // just leave it alone. + if(!XQueryTree(dpy, wid, &troot, &parent, &tchildren, + &tnchildren)) { + wid = 0; + break; + } + + if (tchildren) + XFree(tchildren); + wid = parent; + } + + return w; +} + +/** + * Recheck currently focused window and set its w->focused + * to True. + * + * @param dpy display to use + * @return struct _win of currently focused window, NULL if not found + */ +win *recheck_focus(Display *dpy) { + // Determine the currently focused window so we can apply appropriate + // opacity on it + Window wid = 0; + int revert_to; + win *w = NULL; + + XGetInputFocus(dpy, &wid, &revert_to); + + // Fallback to the old method if find_toplevel() fails + if (!(w = find_toplevel(dpy, wid))) + w = find_toplevel2(dpy, wid); + + // And we set the focus state and opacity here + if (w) { + w->focused = True; + calc_opacity(dpy, w, False); + calc_dim(dpy, w); + return w; + } + + return NULL; } static Picture @@ -876,40 +948,25 @@ border_size(Display *dpy, win *w) { return border; } -static Window -find_client_win(Display *dpy, Window win) { - Atom WM_STATE = XInternAtom(dpy, "WM_STATE", False); +Window find_client_win(Display *dpy, Window w) { + if (win_has_attr(dpy, w, atom_client_attr)) + return w; - 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); + Window ret = 0; - if (type) return win; - - if (!XQueryTree(dpy, win, &root, - &parent, &children, &nchildren)) { + if(!win_get_children(dpy, w, &children, &nchildren)) return 0; - } - for (i = 0; i < nchildren; i++) { - client = find_client_win(dpy, children[i]); - if (client) break; - } + for (i = 0; i < nchildren; ++i) + if ((ret = find_client_win(dpy, children[i]))) + break; - if (children) XFree((char *)children); + XFree(children); - return client; + return ret; } static void @@ -1395,56 +1452,11 @@ map_win(Display *dpy, Window id, /* * Occasionally compton does not seem able to get a FocusIn event from a * window just mapped. I suspect it's a timing issue again when the - * XSelectInput() is called too late. If this is the case, I could think - * of two fixes: To monitor the focus events from the root window, and - * to determine if the current window is focused in map_win(). Looks - * like the XFocusChangeEvent sent to the root window contains no - * information about where the WM frame of the focused window is, and - * XGetInputFocus() often returns an application window instead of the - * WM frame, which compton keeps track of, in either way I believe we - * have to travel through the ancestors of the focused window it - * returns. The latter choice looks cheaper, so I'm doing it here. - * But still, this could anyway be costly. - * - * An alternative route might be relying on _NET_ACTIVE_WINDOW. - * Unfortunately as it's set by WM I'm not completely sure if it's - * reliable and will be updated on the very moment a window is mapped. + * XSelectInput() is called too late. We have to recheck the focused + * window here. */ if (track_focus) - { - Window wid = id; - int revert_to; - win *w = NULL; - - XGetInputFocus(dpy, &wid, &revert_to); - - // XGetInputFocus seemingly returns the application window focused - // instead of the WM window frame, so we traverse through its - // ancestors to find out the frame - while(wid && wid != root && !find_win(dpy, wid)) { - Window troot; - Window parent; - Window *tchildren; - unsigned tnchildren; - - // XQueryTree probably fails if you run compton when X is somehow - // initializing (like add it in .xinitrc). In this case - // just leave it alone. - if(!XQueryTree(dpy, wid, &troot, &parent, &tchildren, - &tnchildren)) { - wid = 0; - break; - } - - if (tchildren) - XFree(tchildren); - wid = parent; - } - - // And we set the focus state - if (wid && wid != root && (w = find_win(dpy, wid))) - w->focused = True; - } + recheck_focus(dpy); calc_opacity(dpy, w, True); calc_dim(dpy, w); @@ -1768,11 +1780,13 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { 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); + if (frame_opacity) + get_frame_extents(dpy, cw, + &new->left_width, &new->right_width, + &new->top_width, &new->bottom_width); + if (id != cw) + XSelectInput(dpy, cw, PropertyChangeMask); } } @@ -2851,6 +2865,7 @@ main(int argc, char **argv) { scr = DefaultScreen(dpy); root = RootWindow(dpy, scr); + atom_client_attr = XInternAtom(dpy, "WM_STATE", False); if (!XRenderQueryExtension(dpy, &render_event, &render_error)) { fprintf(stderr, "No render extension\n"); @@ -2936,50 +2951,11 @@ main(int argc, char **argv) { add_win(dpy, children[i], i ? children[i-1] : None, False); } - if (track_focus) - { - // Determine the currently focused window so we can apply appropriate - // opacity on it - Window wid = 0; - int revert_to; - win *w = NULL; - - XGetInputFocus(dpy, &wid, &revert_to); - - // XGetInputFocus seemingly returns the application window focused - // instead of the WM window frame, so we traverse through its - // ancestors to find out the frame - while(wid && wid != root - && !array_wid_exists(children, nchildren, wid)) { - Window troot; - Window parent; - Window *tchildren = 0; - unsigned tnchildren; - - // XQueryTree probably fails if you run compton when X is somehow - // initializing (like add it in .xinitrc). In this case - // just leave it alone. - if(!XQueryTree(dpy, wid, &troot, &parent, &tchildren, - &tnchildren)) { - wid = 0; - break; - } - - if (tchildren) - XFree(tchildren); - wid = parent; - } - - // And we set the focus state and opacity here - if (wid && wid != root && (w = find_win(dpy, wid))) { - w->focused = True; - calc_opacity(dpy, w, False); - calc_dim(dpy, w); - } - } - XFree(children); + if (track_focus) + recheck_focus(dpy); + XUngrabServer(dpy); ufd.fd = ConnectionNumber(dpy); diff --git a/compton.h b/compton.h index 9a8dd8506..c85992bb5 100644 --- a/compton.h +++ b/compton.h @@ -144,6 +144,7 @@ typedef struct _fade { } fade; extern int root_height, root_width; +extern Atom atom_client_attr; /** * Functions @@ -249,6 +250,51 @@ static inline void print_timestamp(void) { } #endif +/** + * Determine if a window has a specific attribute. + * + * @param dpy Display to use + * @param w window to check + * @param atom atom of attribute to check + * @return 1 if it has the attribute, 0 otherwise + */ +static inline Bool win_has_attr(Display *dpy, Window w, Atom atom) { + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + + if (Success == XGetWindowProperty(dpy, w, atom, 0, 0, False, + AnyPropertyType, &type, &format, &nitems, &after, &data)) { + XFree(data); + if (type) + return True; + } + + return False; +} + +/** + * Get the children of a window. + * + * @param dpy Display to use + * @param w window to check + * @param children [out] an array of child window IDs + * @param nchildren [out] number of children + * @return 1 if successful, 0 otherwise + */ +static inline Bool win_get_children(Display *dpy, Window w, + Window **children, unsigned *nchildren) { + Window troot, tparent; + + if (!XQueryTree(dpy, w, &troot, &tparent, children, nchildren)) { + *nchildren = 0; + return False; + } + + return True; +} + static int get_time_in_milliseconds(); @@ -328,8 +374,9 @@ win_extents(Display *dpy, win *w); static XserverRegion border_size(Display *dpy, win *w); -static Window -find_client_win(Display *dpy, Window win); +Window find_client_win(Display *dpy, Window w); + +Window find_client_win2(Display *dpy, Window w); static void get_frame_extents(Display *dpy, Window w, From 27197e24dbcb3177642d55f56571205fead2b16e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 13 Sep 2012 22:30:18 +0800 Subject: [PATCH 022/178] Bug fix: Segfault when meeting FocusIn/Out from destoryed windows I found compton segfaults sometimes when starting from .xinitrc. Debugging reveals my conky window was just reparented to a fvwm's frame window before compton picked up a FocusOut event on this conky window that has just been destroyed in the event queue. find_win() call in ev_focus_in/out() returned a NULL pointer. When it tried to use the pointer segfault happens. - Add extra check to ev_focus_in/out() to stop the segfault. - Reset window event mask on window reparenting to a non-root window to minimize wrong events. - More abstraction for determining window event mask. --- compton.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/compton.c b/compton.c index 50a74d0f8..c2d4a221a 100644 --- a/compton.c +++ b/compton.c @@ -704,6 +704,21 @@ should_ignore(Display *dpy, unsigned long sequence) { * Windows */ +long determine_evmask(Display *dpy, Window wid, enum win_evmode_t mode) { + long evmask = NoEventMask; + + if (WIN_EVMODE_FRAME == mode || find_win(dpy, wid)) { + evmask |= PropertyChangeMask; + if (track_focus) + evmask |= FocusChangeMask; + } + if (WIN_EVMODE_CLIENT == mode || find_client_win(dpy, wid)) { + evmask |= PropertyChangeMask; + } + + return evmask; +} + static win * find_win(Display *dpy, Window id) { win *w; @@ -1439,10 +1454,7 @@ map_win(Display *dpy, Window id, /* select before reading the property so that no property changes are lost */ if (!override_redirect) { - long evmask = PropertyChangeMask; - if (track_focus) - evmask |= FocusChangeMask; - XSelectInput(dpy, id, evmask); + XSelectInput(dpy, id, determine_evmask(dpy, id, WIN_EVMODE_FRAME)); // Notify compton when the shape of a window changes if (shape_exists) { XShapeSelectInput(dpy, id, ShapeNotifyMask); @@ -1785,8 +1797,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { get_frame_extents(dpy, cw, &new->left_width, &new->right_width, &new->top_width, &new->bottom_width); - if (id != cw) - XSelectInput(dpy, cw, PropertyChangeMask); + XSelectInput(dpy, cw, determine_evmask(dpy, id, WIN_EVMODE_CLIENT)); } } @@ -2301,6 +2312,10 @@ inline static void ev_focus_in(XFocusChangeEvent *ev) { win *w = find_win(dpy, ev->window); + // To deal with events sent from windows just destroyed + if (!w) + return; + w->focused = True; calc_opacity(dpy, w, False); calc_dim(dpy, w); @@ -2319,7 +2334,11 @@ ev_focus_out(XFocusChangeEvent *ev) { win *w = find_win(dpy, ev->window); + // To deal with events sent from windows just destroyed + if (!w) + return; w->focused = False; + calc_opacity(dpy, w, False); calc_dim(dpy, w); } @@ -2358,6 +2377,9 @@ ev_reparent_notify(XReparentEvent *ev) { add_win(dpy, ev->window, 0, ev->override_redirect); } else { destroy_win(dpy, ev->window, True); + // Reset event mask in case something wrong happens + XSelectInput(dpy, ev->window, + determine_evmask(dpy, ev->window, WIN_EVMODE_UNKNOWN)); } } From 2f63377d4edae923c0ede939fd2915f4e063a0be Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 13 Sep 2012 00:39:43 -0500 Subject: [PATCH 023/178] whitespace --- compton.c | 4 ++-- compton.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compton.c b/compton.c index c2d4a221a..6ec0e1744 100644 --- a/compton.c +++ b/compton.c @@ -1693,7 +1693,7 @@ void calc_opacity(Display *dpy, win *w, Bool refetch_prop) { if (OPAQUE != win_type_opacity[w->window_type]) opacity = win_type_opacity[w->window_type] * OPAQUE; } - + // Respect inactive_opacity in some cases if (inactive_opacity && IS_NORMAL_WIN(w) && False == w->focused && (OPAQUE == opacity || inactive_opacity_override)) @@ -1845,7 +1845,7 @@ restack_win(Display *dpy, win *w, Window new_above) { char *window_name; Bool to_free; win* c = list; - + printf("restack_win(%#010lx, %#010lx): Window stack modified. Current stack:\n", w->id, new_above); for (; c; c = c->next) { window_name = "(Failed to get title)"; diff --git a/compton.h b/compton.h index c85992bb5..f4bccd4b5 100644 --- a/compton.h +++ b/compton.h @@ -197,12 +197,12 @@ static inline Bool array_wid_exists(const Window *arr, if (arr[count] == wid) return True; } - + return False; } #ifdef DEBUG_EVENTS -/* +/* * Subtracting two struct timeval values. * * Taken from glibc manual. From f092885f476fb740adc9a539baac48df11bace8e Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 13 Sep 2012 00:58:05 -0500 Subject: [PATCH 024/178] stay consistent with code style --- compton.c | 130 ++++++++++++++++++++++++++++++++++++------------------ compton.h | 74 +++++++++++++++++-------------- 2 files changed, 129 insertions(+), 75 deletions(-) diff --git a/compton.c b/compton.c index 6ec0e1744..cfddd431a 100644 --- a/compton.c +++ b/compton.c @@ -572,8 +572,9 @@ make_shadow(Display *dpy, double opacity, 0, sheight); int y; - for (y = ystart; y < yend; y++) + for (y = ystart; y < yend; y++) { memset(&data[y * swidth + xstart], 0, xrange); + } } return ximage; @@ -1148,9 +1149,10 @@ paint_all(Display *dpy, XserverRegion region) { None, root_buffer, 0, 0, 0, 0, x, y, wid, hei); - if (w->dim) + if (w->dim) { XRenderComposite(dpy, PictOpOver, dim_picture, None, root_buffer, 0, 0, 0, 0, x, y, wid, hei); + } } if (!w->border_clip) { @@ -1169,6 +1171,7 @@ paint_all(Display *dpy, XserverRegion region) { XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); + paint_root(dpy); for (w = t; w; w = w->prev_trans) { @@ -1250,9 +1253,10 @@ paint_all(Display *dpy, XserverRegion region) { l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); } - if (w->dim) + if (w->dim) { XRenderComposite(dpy, PictOpOver, dim_picture, None, root_buffer, 0, 0, 0, 0, x, y, wid, hei); + } } XFixesDestroyRegion(dpy, w->border_clip); @@ -1569,7 +1573,8 @@ unmap_win(Display *dpy, Window id, Bool fade) { finish_unmap_win(dpy, w); } -opacity_t get_opacity_prop(Display *dpy, win *w, opacity_t def) { +static opacity_t +get_opacity_prop(Display *dpy, win *w, opacity_t def) { Atom actual; int format; unsigned long n, left; @@ -1635,13 +1640,14 @@ determine_mode(Display *dpy, win *w) { add_damage_win(dpy, w); } -void set_opacity(Display *dpy, win *w, opacity_t opacity) { +static void +set_opacity(Display *dpy, win *w, opacity_t opacity) { // Do nothing if the opacity does not change - if (w->opacity == opacity) - return; + if (w->opacity == opacity) return; w->opacity = opacity; determine_mode(dpy, w); + if (w->shadow) { XRenderFreePicture(dpy, w->shadow); w->shadow = None; @@ -1675,40 +1681,45 @@ void set_opacity(Display *dpy, win *w, opacity_t opacity) { * @param refetch_prop whether _NET_WM_OPACITY of the window needs to be * refetched */ -void calc_opacity(Display *dpy, win *w, Bool refetch_prop) { +static void +calc_opacity(Display *dpy, win *w, Bool refetch_prop) { opacity_t opacity; // Do nothing for unmapped window, calc_opacity() will be called // when it's mapped // I suppose I need not to check for IsUnviewable here? - if (IsViewable != w->a.map_state) - return; + if (IsViewable != w->a.map_state) return; // Do not refetch the opacity window attribute unless necessary, this // is probably an expensive operation in some cases - if (refetch_prop) + if (refetch_prop) { w->opacity_prop = get_opacity_prop(dpy, w, OPAQUE); + } if (OPAQUE == (opacity = w->opacity_prop)) { - if (OPAQUE != win_type_opacity[w->window_type]) + if (OPAQUE != win_type_opacity[w->window_type]) { opacity = win_type_opacity[w->window_type] * OPAQUE; + } } // Respect inactive_opacity in some cases if (inactive_opacity && IS_NORMAL_WIN(w) && False == w->focused - && (OPAQUE == opacity || inactive_opacity_override)) + && (OPAQUE == opacity || inactive_opacity_override)) { opacity = inactive_opacity; + } set_opacity(dpy, w, opacity); } -void calc_dim(Display *dpy, win *w) { +static void +calc_dim(Display *dpy, win *w) { Bool dim; - if (inactive_dim && IS_NORMAL_WIN(w) && !(w->focused)) + if (inactive_dim && IS_NORMAL_WIN(w) && !(w->focused)) { dim = True; - else + } else { dim = False; + } if (dim != w->dim) { w->dim = dim; @@ -1846,17 +1857,22 @@ restack_win(Display *dpy, win *w, Window new_above) { Bool to_free; win* c = list; - printf("restack_win(%#010lx, %#010lx): Window stack modified. Current stack:\n", w->id, new_above); + printf("restack_win(%#010lx, %#010lx): " + "Window stack modified. Current stack:\n", w->id, new_above); + for (; c; c = c->next) { window_name = "(Failed to get title)"; - if (root == c->id) + + if (root == c->id) { window_name = "(Root window)"; - else + } else { to_free = window_get_name(c->id, &window_name); + } + desc = ""; - if (c->destroyed) - desc = "(D) "; + if (c->destroyed) desc = "(D) "; printf("%#010lx \"%s\" %s-> ", c->id, window_name, desc); + if (to_free) { XFree(window_name); window_name = NULL; @@ -1891,8 +1907,10 @@ configure_win(Display *dpy, XConfigureEvent *ce) { w->queue_configure = *ce; restack_win(dpy, w, ce->above); } else { - if (!(w->need_configure)) + if (!(w->need_configure)) { restack_win(dpy, w, ce->above); + } + w->need_configure = False; #if CAN_DO_USABLE @@ -1900,8 +1918,9 @@ configure_win(Display *dpy, XConfigureEvent *ce) { #endif { damage = XFixesCreateRegion(dpy, 0, 0); - if (w->extents != None) + if (w->extents != None) { XFixesCopyRegion(dpy, damage, w->extents); + } } w->a.x = ce->x; @@ -2198,7 +2217,8 @@ expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) { } #if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) -static int window_get_name(Window w, char **name) { +static int +window_get_name(Window w, char **name) { Atom prop = XInternAtom(dpy, "_NET_WM_NAME", False); Atom utf8_type = XInternAtom(dpy, "UTF8_STRING", False); Atom actual_type; @@ -2209,16 +2229,20 @@ static int window_get_name(Window w, char **name) { Status ret; set_ignore(dpy, NextRequest(dpy)); + if (Success != (ret = XGetWindowProperty(dpy, w, prop, 0L, (long) BUFSIZ, False, utf8_type, &actual_type, &actual_format, &nitems, &leftover, (unsigned char **) &data))) { - if (BadWindow == ret) - return 0; + if (BadWindow == ret) return 0; + set_ignore(dpy, NextRequest(dpy)); printf("Window %#010lx: _NET_WM_NAME unset, falling back to WM_NAME.\n", w); - if (!XFetchName(dpy, w, &data)) + + if (!XFetchName(dpy, w, &data)) { return 0; + } } + // if (actual_type == utf8_type && actual_format == 8) *name = (char *) data; return 1; @@ -2264,9 +2288,13 @@ ev_name(XEvent *ev) { if (ev->type == damage_event + XDamageNotify) { return "Damage"; } - if (shape_exists && ev->type == shape_event) + + if (shape_exists && ev->type == shape_event) { return "ShapeNotify"; + } + sprintf(buf, "Event %d", ev->type); + return buf; } } @@ -2297,8 +2325,11 @@ ev_window(XEvent *ev) { if (ev->type == damage_event + XDamageNotify) { return ((XDamageNotifyEvent *)ev)->drawable; } - if (shape_exists && ev->type == shape_event) + + if (shape_exists && ev->type == shape_event) { return ((XShapeEvent *) ev)->window; + } + return 0; } } @@ -2311,6 +2342,7 @@ ev_window(XEvent *ev) { inline static void ev_focus_in(XFocusChangeEvent *ev) { win *w = find_win(dpy, ev->window); + if (!w) return; // To deal with events sent from windows just destroyed if (!w) @@ -2333,6 +2365,7 @@ ev_focus_out(XFocusChangeEvent *ev) { } win *w = find_win(dpy, ev->window); + if (!w) return; // To deal with events sent from windows just destroyed if (!w) @@ -2351,7 +2384,10 @@ ev_create_notify(XCreateWindowEvent *ev) { inline static void ev_configure_notify(XConfigureEvent *ev) { #ifdef DEBUG_EVENTS - printf("{ send_event: %d, above: %#010lx, override_redirect: %d }\n", ev->send_event, ev->above, ev->override_redirect); + printf("{ send_event: %d, " + " above: %#010lx, " + " override_redirect: %d }\n", + ev->send_event, ev->above, ev->override_redirect); #endif configure_win(dpy, ev); } @@ -2454,8 +2490,10 @@ ev_damage_notify(XDamageNotifyEvent *ev) { damage_win(dpy, ev); } -static void ev_shape_notify(XShapeEvent *ev) { +inline static void +ev_shape_notify(XShapeEvent *ev) { win *w = find_win(dpy, ev->window); + if (!w) return; /* * Empty border_size may indicated an @@ -2472,12 +2510,10 @@ static void ev_shape_notify(XShapeEvent *ev) { // Mark the new border_size as damaged add_damage(dpy, copy_region(dpy, w->border_size)); } - } inline static void ev_handle(XEvent *ev) { - #ifdef DEBUG_EVENTS Window w; char *window_name; @@ -2491,17 +2527,21 @@ ev_handle(XEvent *ev) { #ifdef DEBUG_EVENTS w = ev_window(ev); window_name = "(Failed to get title)"; + if (w) { - if (root == w) + if (root == w) { window_name = "(Root window)"; - else + } else { to_free = (Bool) window_get_name(w, &window_name); + } } + if (ev->type != damage_event + XDamageNotify) { print_timestamp(); printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", ev_name(ev), ev_serial(ev), w, window_name); } + if (to_free) { XFree(window_name); window_name = NULL; @@ -2611,8 +2651,7 @@ usage() { "--inactive-opacity-override\n" " Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n" "--inactive-dim value\n" - " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n" - ); + " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n"); exit(1); } @@ -2835,8 +2874,9 @@ main(int argc, char **argv) { break; case 'i': inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); - if (OPAQUE == inactive_opacity) + if (OPAQUE == inactive_opacity) { inactive_opacity = 0; + } break; case 'e': frame_opacity = normalize_d(atof(optarg)); @@ -2871,8 +2911,9 @@ main(int argc, char **argv) { } // Determine whether we need to track focus changes - if (inactive_opacity || inactive_dim) + if (inactive_opacity || inactive_dim) { track_focus = True; + } dpy = XOpenDisplay(display); if (!dpy) { @@ -2918,8 +2959,9 @@ main(int argc, char **argv) { exit(1); } - if (!XShapeQueryExtension(dpy, &shape_event, &shape_error)) + if (!XShapeQueryExtension(dpy, &shape_event, &shape_error)) { shape_exists = False; + } register_cm(scr); @@ -2943,15 +2985,17 @@ main(int argc, char **argv) { // Generates another Picture for shadows if the color is modified by // user - if (!shadow_red && !shadow_green && !shadow_blue) + if (!shadow_red && !shadow_green && !shadow_blue) { cshadow_picture = black_picture; - else + } else { cshadow_picture = solid_picture(dpy, True, 1, shadow_red, shadow_green, shadow_blue); + } // Generates a picture for inactive_dim - if (inactive_dim) + if (inactive_dim) { dim_picture = solid_picture(dpy, True, inactive_dim, 0, 0, 0); + } all_damage = None; clip_changed = True; diff --git a/compton.h b/compton.h index f4bccd4b5..83671a682 100644 --- a/compton.h +++ b/compton.h @@ -161,12 +161,10 @@ extern Atom atom_client_attr; * @param max maximum value * @return normalized value */ -static inline double normalize_i_range(int i, int min, int max) { - if (i > max) - return max; - if (i < min) - return min; - +static inline double +normalize_i_range(int i, int min, int max) { + if (i > max) return max; + if (i < min) return min; return i; } @@ -175,12 +173,10 @@ static inline double normalize_i_range(int i, int min, int max) { * * @param d double value to normalize */ -static inline double normalize_d(double d) { - if (d > 1.0) - return 1.0; - if (d < 0.0) - return 0.0; - +static inline double +normalize_d(double d) { + if (d > 1.0) return 1.0; + if (d < 0.0) return 0.0; return d; } @@ -191,11 +187,12 @@ static inline double normalize_d(double d) { * @param count amount of elements in the array * @param wid window ID to search for */ -static inline Bool array_wid_exists(const Window *arr, - int count, Window wid) { +static inline Bool +array_wid_exists(const Window *arr, int count, Window wid) { while (count--) { - if (arr[count] == wid) + if (arr[count] == wid) { return True; + } } return False; @@ -210,15 +207,17 @@ static inline Bool array_wid_exists(const Window *arr, * Subtract the `struct timeval' values X and Y, * storing the result in RESULT. * Return 1 if the difference is negative, otherwise 0. */ -int timeval_subtract (result, x, y) - struct timeval *result, *x, *y; -{ +static int +timeval_subtract(struct timeval *result, + struct timeval *x, + struct timeval *y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } + if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; @@ -239,11 +238,11 @@ int timeval_subtract (result, x, y) * * Used for debugging. */ -static inline void print_timestamp(void) { +static inline void +print_timestamp(void) { struct timeval tm, diff; - if (gettimeofday(&tm, NULL)) - return; + if (gettimeofday(&tm, NULL)) return; timeval_subtract(&diff, &tm, &time_start); printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); @@ -416,7 +415,7 @@ unmap_callback(Display *dpy, win *w); static void unmap_win(Display *dpy, Window id, Bool fade); -opacity_t +static opacity_t get_opacity_prop(Display *dpy, win *w, opacity_t def); static double @@ -425,11 +424,14 @@ get_opacity_percent(Display *dpy, win *w); static void determine_mode(Display *dpy, win *w); -void set_opacity(Display *dpy, win *w, opacity_t opacity); +static void +set_opacity(Display *dpy, win *w, opacity_t opacity); -void calc_opacity(Display *dpy, win *w, Bool refetch_prop); +static void +calc_opacity(Display *dpy, win *w, Bool refetch_prop); -void calc_dim(Display *dpy, win *w); +static void +calc_dim(Display *dpy, win *w); static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect); @@ -464,7 +466,8 @@ static void expose_root(Display *dpy, Window root, XRectangle *rects, int nrects); #if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) -static int window_get_name(Window w, char **name); +static int +window_get_name(Window w, char **name); #endif #ifdef DEBUG_EVENTS @@ -520,10 +523,14 @@ ev_property_notify(XPropertyEvent *ev); inline static void ev_damage_notify(XDamageNotifyEvent *ev); +inline static void +ev_shape_notify(XShapeEvent *ev); + /** * Destory the cached border_size of a window. */ -inline static void win_free_border_size(Display *dpy, win *w) { +inline static void +win_free_border_size(Display *dpy, win *w) { if (w->border_size) { set_ignore(dpy, NextRequest(dpy)); XFixesDestroyRegion(dpy, w->border_size); @@ -534,7 +541,8 @@ inline static void win_free_border_size(Display *dpy, win *w) { /** * Get a region of the screen size. */ -inline static XserverRegion get_screen_region(Display *dpy) { +inline static XserverRegion +get_screen_region(Display *dpy) { XRectangle r; r.x = 0; @@ -547,8 +555,8 @@ inline static XserverRegion get_screen_region(Display *dpy) { /** * Copies a region */ -inline static XserverRegion copy_region(Display *dpy, - XserverRegion oldregion) { +inline static XserverRegion +copy_region(Display *dpy, XserverRegion oldregion) { XserverRegion region = XFixesCreateRegion(dpy, NULL, 0); XFixesCopyRegion(dpy, region, oldregion); @@ -562,9 +570,11 @@ inline static XserverRegion copy_region(Display *dpy, * @param dpy display in use * @param w struct _win element representing the window */ -static inline void add_damage_win(Display *dpy, win *w) { - if (w->extents) +static inline void +add_damage_win(Display *dpy, win *w) { + if (w->extents) { add_damage(dpy, copy_region(dpy, w->extents)); + } } inline static void From 212582469a1f7ac8a6fd81bc8619ca401e247b77 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 13 Sep 2012 10:12:54 -0500 Subject: [PATCH 025/178] more style changes --- compton.c | 45 ++++++++++++++++++++++++++++----------------- compton.h | 21 +++++++++++++++------ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/compton.c b/compton.c index cfddd431a..f54e95089 100644 --- a/compton.c +++ b/compton.c @@ -710,9 +710,9 @@ long determine_evmask(Display *dpy, Window wid, enum win_evmode_t mode) { if (WIN_EVMODE_FRAME == mode || find_win(dpy, wid)) { evmask |= PropertyChangeMask; - if (track_focus) - evmask |= FocusChangeMask; + if (track_focus) evmask |= FocusChangeMask; } + if (WIN_EVMODE_CLIENT == mode || find_client_win(dpy, wid)) { evmask |= PropertyChangeMask; } @@ -739,7 +739,8 @@ find_win(Display *dpy, Window id) { * @param w window ID * @return struct _win object of the found window, NULL if not found */ -win *find_toplevel(Display *dpy, Window id) { +static win * +find_toplevel(Display *dpy, Window id) { win *w; for (w = list; w; w = w->next) { @@ -757,11 +758,12 @@ win *find_toplevel(Display *dpy, Window id) { * @param w window ID * @return struct _win object of the found window, NULL if not found */ -win *find_toplevel2(Display *dpy, Window wid) { +static win * +find_toplevel2(Display *dpy, Window wid) { win *w = NULL; // We traverse through its ancestors to find out the frame - while(wid && wid != root && !(w = find_win(dpy, wid))) { + while (wid && wid != root && !(w = find_win(dpy, wid))) { Window troot; Window parent; Window *tchildren; @@ -770,14 +772,14 @@ win *find_toplevel2(Display *dpy, Window wid) { // XQueryTree probably fails if you run compton when X is somehow // initializing (like add it in .xinitrc). In this case // just leave it alone. - if(!XQueryTree(dpy, wid, &troot, &parent, &tchildren, + if (!XQueryTree(dpy, wid, &troot, &parent, &tchildren, &tnchildren)) { wid = 0; break; } - if (tchildren) - XFree(tchildren); + if (tchildren) XFree(tchildren); + wid = parent; } @@ -791,7 +793,8 @@ win *find_toplevel2(Display *dpy, Window wid) { * @param dpy display to use * @return struct _win of currently focused window, NULL if not found */ -win *recheck_focus(Display *dpy) { +static win * +recheck_focus(Display *dpy) { // Determine the currently focused window so we can apply appropriate // opacity on it Window wid = 0; @@ -801,8 +804,9 @@ win *recheck_focus(Display *dpy) { XGetInputFocus(dpy, &wid, &revert_to); // Fallback to the old method if find_toplevel() fails - if (!(w = find_toplevel(dpy, wid))) + if (!(w = find_toplevel(dpy, wid))) { w = find_toplevel2(dpy, wid); + } // And we set the focus state and opacity here if (w) { @@ -964,21 +968,25 @@ border_size(Display *dpy, win *w) { return border; } -Window find_client_win(Display *dpy, Window w) { - if (win_has_attr(dpy, w, atom_client_attr)) +static Window +find_client_win(Display *dpy, Window w) { + if (win_has_attr(dpy, w, atom_client_attr)) { return w; + } Window *children; unsigned int nchildren; unsigned int i; Window ret = 0; - if(!win_get_children(dpy, w, &children, &nchildren)) + if (!win_get_children(dpy, w, &children, &nchildren)) { return 0; + } - for (i = 0; i < nchildren; ++i) + for (i = 0; i < nchildren; ++i) { if ((ret = find_client_win(dpy, children[i]))) break; + } XFree(children); @@ -1471,8 +1479,9 @@ map_win(Display *dpy, Window id, * XSelectInput() is called too late. We have to recheck the focused * window here. */ - if (track_focus) + if (track_focus) { recheck_focus(dpy); + } calc_opacity(dpy, w, True); calc_dim(dpy, w); @@ -1804,10 +1813,11 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { Window cw = find_client_win(dpy, new->id); if (cw) { new->client_win = cw; - if (frame_opacity) + if (frame_opacity) { get_frame_extents(dpy, cw, &new->left_width, &new->right_width, &new->top_width, &new->bottom_width); + } XSelectInput(dpy, cw, determine_evmask(dpy, id, WIN_EVMODE_CLIENT)); } } @@ -3019,8 +3029,9 @@ main(int argc, char **argv) { XFree(children); - if (track_focus) + if (track_focus) { recheck_focus(dpy); + } XUngrabServer(dpy); diff --git a/compton.h b/compton.h index 83671a682..e89728a6a 100644 --- a/compton.h +++ b/compton.h @@ -257,7 +257,8 @@ print_timestamp(void) { * @param atom atom of attribute to check * @return 1 if it has the attribute, 0 otherwise */ -static inline Bool win_has_attr(Display *dpy, Window w, Atom atom) { +static inline Bool +win_has_attr(Display *dpy, Window w, Atom atom) { Atom type = None; int format; unsigned long nitems, after; @@ -266,8 +267,7 @@ static inline Bool win_has_attr(Display *dpy, Window w, Atom atom) { if (Success == XGetWindowProperty(dpy, w, atom, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data)) { XFree(data); - if (type) - return True; + if (type) return True; } return False; @@ -282,7 +282,8 @@ static inline Bool win_has_attr(Display *dpy, Window w, Atom atom) { * @param nchildren [out] number of children * @return 1 if successful, 0 otherwise */ -static inline Bool win_get_children(Display *dpy, Window w, +static inline Bool +win_get_children(Display *dpy, Window w, Window **children, unsigned *nchildren) { Window troot, tparent; @@ -361,6 +362,12 @@ find_win(Display *dpy, Window id); static win * find_toplevel(Display *dpy, Window id); +static win * +find_toplevel2(Display *dpy, Window wid); + +static win * +recheck_focus(Display *dpy); + static Picture root_tile_f(Display *dpy); @@ -373,9 +380,11 @@ win_extents(Display *dpy, win *w); static XserverRegion border_size(Display *dpy, win *w); -Window find_client_win(Display *dpy, Window w); +static Window +find_client_win(Display *dpy, Window w); -Window find_client_win2(Display *dpy, Window w); +static Window +find_client_win2(Display *dpy, Window w); static void get_frame_extents(Display *dpy, Window w, From 6278604753e1f9a08a75119f9b661178dd450bd0 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 13 Sep 2012 10:24:37 -0500 Subject: [PATCH 026/178] fix determine_evmask warnings --- compton.c | 3 ++- compton.h | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index f54e95089..fc2f536e5 100644 --- a/compton.c +++ b/compton.c @@ -705,7 +705,8 @@ should_ignore(Display *dpy, unsigned long sequence) { * Windows */ -long determine_evmask(Display *dpy, Window wid, enum win_evmode_t mode) { +static long +determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { long evmask = NoEventMask; if (WIN_EVMODE_FRAME == mode || find_win(dpy, wid)) { diff --git a/compton.h b/compton.h index e89728a6a..2633a98ed 100644 --- a/compton.h +++ b/compton.h @@ -143,6 +143,12 @@ typedef struct _fade { Display *dpy; } fade; +typedef enum { + WIN_EVMODE_UNKNOWN, + WIN_EVMODE_FRAME, + WIN_EVMODE_CLIENT +} win_evmode_t; + extern int root_height, root_width; extern Atom atom_client_attr; @@ -356,6 +362,9 @@ set_ignore(Display *dpy, unsigned long sequence); static int should_ignore(Display *dpy, unsigned long sequence); +static long +determine_evmask(Display *dpy, Window wid, win_evmode_t mode); + static win * find_win(Display *dpy, Window id); From 72a11771151f93b8b905e28601bdb79b4d181a04 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 13 Sep 2012 10:28:27 -0500 Subject: [PATCH 027/178] fix code duplication resulting from rebase --- compton.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/compton.c b/compton.c index fc2f536e5..bc13f53dd 100644 --- a/compton.c +++ b/compton.c @@ -2353,11 +2353,9 @@ ev_window(XEvent *ev) { inline static void ev_focus_in(XFocusChangeEvent *ev) { win *w = find_win(dpy, ev->window); - if (!w) return; // To deal with events sent from windows just destroyed - if (!w) - return; + if (!w) return; w->focused = True; calc_opacity(dpy, w, False); @@ -2376,11 +2374,10 @@ ev_focus_out(XFocusChangeEvent *ev) { } win *w = find_win(dpy, ev->window); - if (!w) return; // To deal with events sent from windows just destroyed - if (!w) - return; + if (!w) return; + w->focused = False; calc_opacity(dpy, w, False); From e370ec9b1ca1768ad9160703477d6ac5ec20fa80 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 14 Sep 2012 11:51:46 +0800 Subject: [PATCH 028/178] Bug fix: Issue #43, better client window lookup More details on the issue report. - Look up the client window of a WM frame when it's mapped instead of when it's created, for better reliability. - Fix a warning when building. --- compton.c | 52 ++++++++++++++++++++++++++++++++++++++-------------- compton.h | 6 +++--- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/compton.c b/compton.c index bc13f53dd..547d8a623 100644 --- a/compton.c +++ b/compton.c @@ -715,7 +715,8 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { } if (WIN_EVMODE_CLIENT == mode || find_client_win(dpy, wid)) { - evmask |= PropertyChangeMask; + if (frame_opacity) + evmask |= PropertyChangeMask; } return evmask; @@ -1460,13 +1461,22 @@ map_win(Display *dpy, Window id, w->window_type = determine_wintype(dpy, w->id, w->id); #ifdef DEBUG_WINTYPE - printf("window 0x%x type %s\n", + printf("map_win(): window %#010lx 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) { + // Detect client window here instead of in add_win() as the client + // window should have been prepared at this point + if (!(w->client_win)) { + Window cw = find_client_win(dpy, w->id); + if (cw) { + mark_client_win(dpy, w, cw); + } + } + XSelectInput(dpy, id, determine_evmask(dpy, id, WIN_EVMODE_FRAME)); // Notify compton when the shape of a window changes if (shape_exists) { @@ -1737,6 +1747,26 @@ calc_dim(Display *dpy, win *w) { } } +/** + * Mark a window as the client window of another. + * + * @param dpy display to use + * @param w struct _win of the parent window + * @param client window ID of the client window + */ +static void +mark_client_win(Display *dpy, win *w, Window client) { + w->client_win = client; + + // Get the frame width and monitor further frame width changes on client + // window if necessary + if (frame_opacity) { + get_frame_extents(dpy, client, + &w->left_width, &w->right_width, &w->top_width, &w->bottom_width); + } + XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); +} + static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { if (find_win(dpy, id)) { @@ -1810,18 +1840,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->bottom_width = 0; new->client_win = 0; - if (!override_redirect) { - Window cw = find_client_win(dpy, new->id); - if (cw) { - new->client_win = cw; - if (frame_opacity) { - get_frame_extents(dpy, cw, - &new->left_width, &new->right_width, - &new->top_width, &new->bottom_width); - } - XSelectInput(dpy, cw, determine_evmask(dpy, id, WIN_EVMODE_CLIENT)); - } - } new->next = *p; *p = new; @@ -2424,6 +2442,12 @@ ev_reparent_notify(XReparentEvent *ev) { // Reset event mask in case something wrong happens XSelectInput(dpy, ev->window, determine_evmask(dpy, ev->window, WIN_EVMODE_UNKNOWN)); + /* + // Check if the window is a client window of another + win *w_top = find_toplevel2(dpy, ev->window); + if (w_top && !(w_top->client_win)) { + mark_client_win(dpy, w_top, ev->window); + } */ } } diff --git a/compton.h b/compton.h index 2633a98ed..1cca657a5 100644 --- a/compton.h +++ b/compton.h @@ -392,9 +392,6 @@ border_size(Display *dpy, win *w); static Window find_client_win(Display *dpy, Window w); -static Window -find_client_win2(Display *dpy, Window w); - static void get_frame_extents(Display *dpy, Window w, unsigned int *left, @@ -451,6 +448,9 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop); static void calc_dim(Display *dpy, win *w); +static void +mark_client_win(Display *dpy, win *w, Window client); + static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect); From 32d9807104ab6a288a279beb98679dd2e02603c1 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 16 Sep 2012 23:12:02 +0800 Subject: [PATCH 029/178] Misc: Clean up - Add 4 helper free functions that free XserverRegion, Damage, Picture, and Pixmap. - Rename w->shadow to w->shadow_pict. Add a bool member w->shadow to prepare for a future change. --- compton.c | 139 +++++++++++++----------------------------------------- compton.h | 62 ++++++++++++++++++------ 2 files changed, 82 insertions(+), 119 deletions(-) diff --git a/compton.c b/compton.c index 547d8a623..e25351ccd 100644 --- a/compton.c +++ b/compton.c @@ -278,13 +278,11 @@ run_fades(Display *dpy) { determine_mode(dpy, w); - if (w->shadow) { - XRenderFreePicture(dpy, w->shadow); - w->shadow = None; + if (w->shadow_pict) { + XRenderFreePicture(dpy, w->shadow_pict); + w->shadow_pict = None; - if (w->extents != None) { - XFixesDestroyRegion(dpy, w->extents); - } + free_region(dpy, &w->extents); /* rebuild the shadow */ w->extents = win_extents(dpy, w); @@ -900,18 +898,18 @@ win_extents(Display *dpy, win *w) { w->shadow_dx = shadow_offset_x; w->shadow_dy = shadow_offset_y; - if (!w->shadow) { + if (!w->shadow_pict) { double opacity = shadow_opacity; if (w->mode != WINDOW_SOLID) { - opacity = opacity * ((double)w->opacity) / ((double)OPAQUE); + opacity = opacity * get_opacity_percent(dpy, w); } if (HAS_FRAME_OPACITY(w)) { opacity = opacity * frame_opacity; } - w->shadow = shadow_picture( + w->shadow_pict = shadow_picture( dpy, opacity, w->alpha_pict, w->a.width + w->a.border_width * 2, w->a.height + w->a.border_width * 2, @@ -1114,15 +1112,9 @@ paint_all(Display *dpy, XserverRegion region) { #endif if (clip_changed) { - win_free_border_size(dpy, w); - if (w->extents) { - XFixesDestroyRegion(dpy, w->extents); - w->extents = None; - } - if (w->border_clip) { - XFixesDestroyRegion(dpy, w->border_clip); - w->border_clip = None; - } + free_region(dpy, &w->border_size); + free_region(dpy, &w->extents); + free_region(dpy, &w->border_clip); } if (!w->border_size) { @@ -1190,7 +1182,7 @@ paint_all(Display *dpy, XserverRegion region) { if (win_type_shadow[w->window_type]) { XRenderComposite( - dpy, PictOpOver, cshadow_picture, w->shadow, + dpy, PictOpOver, cshadow_picture, w->shadow_pict, root_buffer, 0, 0, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height); @@ -1198,7 +1190,7 @@ paint_all(Display *dpy, XserverRegion region) { if (w->opacity != OPAQUE && !w->alpha_pict) { w->alpha_pict = solid_picture( - dpy, False, (double)w->opacity / OPAQUE, 0, 0, 0); + dpy, False, get_opacity_percent(dpy, w), 0, 0, 0); } if (HAS_FRAME_OPACITY(w) && !w->alpha_border_pict) { @@ -1269,8 +1261,7 @@ paint_all(Display *dpy, XserverRegion region) { } } - XFixesDestroyRegion(dpy, w->border_clip); - w->border_clip = None; + free_region(dpy, &w->border_clip); } XFixesDestroyRegion(dpy, region); @@ -1533,29 +1524,13 @@ finish_unmap_win(Display *dpy, win *w) { } #if HAS_NAME_WINDOW_PIXMAP - if (w->pixmap) { - XFreePixmap(dpy, w->pixmap); - w->pixmap = None; - } + free_pixmap(dpy, &w->pixmap); #endif - if (w->picture) { - set_ignore(dpy, NextRequest(dpy)); - XRenderFreePicture(dpy, w->picture); - w->picture = None; - } - - win_free_border_size(dpy, w); - - if (w->shadow) { - XRenderFreePicture(dpy, w->shadow); - w->shadow = None; - } - - if (w->border_clip) { - XFixesDestroyRegion(dpy, w->border_clip); - w->border_clip = None; - } + free_picture(dpy, &w->picture); + free_region(dpy, &w->border_size); + free_picture(dpy, &w->shadow_pict); + free_region(dpy, &w->border_clip); clip_changed = True; } @@ -1586,7 +1561,7 @@ unmap_win(Display *dpy, Window id, Bool fade) { #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, + set_fade(dpy, w, get_opacity_percent(dpy, w), 0.0, fade_out_step, unmap_callback, False, True); } else #endif @@ -1615,7 +1590,7 @@ get_opacity_prop(Display *dpy, win *w, opacity_t def) { static double get_opacity_percent(Display *dpy, win *w) { - return w->opacity * 1.0 / OPAQUE; + return ((double) w->opacity) / OPAQUE; } static void @@ -1625,20 +1600,8 @@ determine_mode(Display *dpy, win *w) { /* 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; - } + free_picture(dpy, &w->alpha_pict); + free_picture(dpy, &w->alpha_border_pict); if (w->a.class == InputOnly) { format = 0; @@ -1668,13 +1631,11 @@ set_opacity(Display *dpy, win *w, opacity_t opacity) { w->opacity = opacity; determine_mode(dpy, w); - if (w->shadow) { - XRenderFreePicture(dpy, w->shadow); - w->shadow = None; + if (w->shadow_pict) { + XRenderFreePicture(dpy, w->shadow_pict); + w->shadow_pict = None; - if (w->extents != None) { - XFixesDestroyRegion(dpy, w->extents); - } + free_region(dpy, &w->extents); /* rebuild the shadow */ w->extents = win_extents(dpy, w); @@ -1815,10 +1776,10 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->alpha_pict = None; new->alpha_border_pict = None; + new->shadow = False; 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; @@ -1957,20 +1918,10 @@ configure_win(Display *dpy, XConfigureEvent *ce) { 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; - } - } + free_pixmap(dpy, &w->pixmap); + free_picture(dpy, &w->picture); #endif - - if (w->shadow) { - XRenderFreePicture(dpy, w->shadow); - w->shadow = None; - } + free_picture(dpy, &w->shadow_pict); } w->a.width = ce->width; @@ -2016,32 +1967,10 @@ finish_destroy_win(Display *dpy, Window id) { 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; - } + free_picture(dpy, &w->alpha_pict); + free_picture(dpy, &w->alpha_border_pict); + free_picture(dpy, &w->shadow_pict); + free_damage(dpy, &w->damage); cleanup_fade(dpy, w); free(w); diff --git a/compton.h b/compton.h index 1cca657a5..ce8e4d298 100644 --- a/compton.h +++ b/compton.h @@ -97,10 +97,10 @@ typedef struct _win { Picture picture; Picture alpha_pict; Picture alpha_border_pict; - Picture shadow_pict; XserverRegion border_size; XserverRegion extents; - Picture shadow; + Bool shadow; + Picture shadow_pict; int shadow_dx; int shadow_dy; int shadow_width; @@ -159,6 +159,8 @@ extern Atom atom_client_attr; // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline +// Helper functions + /** * Normalize an int value to a specific range. * @@ -255,6 +257,50 @@ print_timestamp(void) { } #endif +/** + * Destroy a XserverRegion. + */ +inline static void +free_region(Display *dpy, XserverRegion *p) { + if (*p) { + XFixesDestroyRegion(dpy, *p); + *p = None; + } +} + +/** + * Destroy a Picture. + */ +inline static void +free_picture(Display *dpy, Picture *p) { + if (*p) { + XRenderFreePicture(dpy, *p); + *p = None; + } +} + +/** + * Destroy a Pixmap. + */ +inline static void +free_pixmap(Display *dpy, Pixmap *p) { + if (*p) { + XFreePixmap(dpy, *p); + *p = None; + } +} + +/** + * Destroy a Damage. + */ +inline static void +free_damage(Display *dpy, Damage *p) { + if (*p) { + XDamageDestroy(dpy, *p); + *p = None; + } +} + /** * Determine if a window has a specific attribute. * @@ -544,18 +590,6 @@ ev_damage_notify(XDamageNotifyEvent *ev); inline static void ev_shape_notify(XShapeEvent *ev); -/** - * Destory the cached border_size of a window. - */ -inline static void -win_free_border_size(Display *dpy, win *w) { - if (w->border_size) { - set_ignore(dpy, NextRequest(dpy)); - XFixesDestroyRegion(dpy, w->border_size); - w->border_size = None; - } -} - /** * Get a region of the screen size. */ From 65e8e56c1ef0e1d02f3008e2af243e1b170414f3 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 17 Sep 2012 12:31:01 +0800 Subject: [PATCH 030/178] Improvement: Change generating process of alpha_pict, etc. - Change how w->alpha_pict is generated, in hope to boost performance, slightly, and as a step to eventually move handling code of most resources used when painting to paint_preprocess(). - Remove alpha_pict parameter of shadow_picture() as it's not necessary. - Let window opacity affect frame opacity. - Rename some members of struct _win. --- compton.c | 71 ++++++++++++++++++++++++++++++++++--------------------- compton.h | 18 +++++++++++--- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/compton.c b/compton.c index e25351ccd..a6a99a887 100644 --- a/compton.c +++ b/compton.c @@ -579,7 +579,7 @@ make_shadow(Display *dpy, double opacity, } static Picture -shadow_picture(Display *dpy, double opacity, Picture alpha_pict, +shadow_picture(Display *dpy, double opacity, int width, int height, int *wp, int *hp) { XImage *shadow_image; Pixmap shadow_pixmap; @@ -910,7 +910,7 @@ win_extents(Display *dpy, win *w) { } w->shadow_pict = shadow_picture( - dpy, opacity, w->alpha_pict, + dpy, opacity, w->a.width + w->a.border_width * 2, w->a.height + w->a.border_width * 2, &w->shadow_width, &w->shadow_height); @@ -1125,7 +1125,32 @@ paint_all(Display *dpy, XserverRegion region) { w->extents = win_extents(dpy, w); } - if (w->mode == WINDOW_SOLID && !HAS_FRAME_OPACITY(w)) { + // Rebuild alpha_pict only if necessary + if (OPAQUE != w->opacity + && (!w->alpha_pict || w->opacity != w->opacity_cur)) { + free_picture(dpy, &w->alpha_pict); + w->alpha_pict = solid_picture( + dpy, False, get_opacity_percent(dpy, w), 0, 0, 0); + w->opacity_cur = w->opacity; + } + + // Calculate frame_opacity + if (frame_opacity && 1.0 != frame_opacity && w->top_width) + w->frame_opacity = get_opacity_percent(dpy, w) * frame_opacity; + else + w->frame_opacity = 0.0; + + // Rebuild frame_alpha_pict only if necessary + if (w->frame_opacity + && (!w->frame_alpha_pict + || w->frame_opacity != w->frame_opacity_cur)) { + free_picture(dpy, &w->frame_alpha_pict); + w->frame_alpha_pict = solid_picture( + dpy, False, w->frame_opacity, 0, 0, 0); + w->frame_opacity_cur = w->frame_opacity; + } + + if (w->mode == WINDOW_SOLID && !w->frame_opacity) { int x, y, wid, hei; #if HAS_NAME_WINDOW_PIXMAP @@ -1188,17 +1213,7 @@ paint_all(Display *dpy, XserverRegion region) { w->shadow_width, w->shadow_height); } - if (w->opacity != OPAQUE && !w->alpha_pict) { - w->alpha_pict = solid_picture( - dpy, False, get_opacity_percent(dpy, w), 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)) { + if (w->mode != WINDOW_SOLID || w->frame_opacity) { int x, y, wid, hei; // Necessary to make sure specially-shaped windows are @@ -1219,9 +1234,11 @@ paint_all(Display *dpy, XserverRegion region) { set_ignore(dpy, NextRequest(dpy)); - if (!HAS_FRAME_OPACITY(w)) { + Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); + + if (!w->frame_opacity) { XRenderComposite( - dpy, PictOpOver, w->picture, w->alpha_pict, + dpy, PictOpOver, w->picture, alpha_mask, root_buffer, 0, 0, 0, 0, x, y, wid, hei); } else { unsigned int t = w->top_width; @@ -1231,27 +1248,27 @@ paint_all(Display *dpy, XserverRegion region) { /* top */ XRenderComposite( - dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, + dpy, PictOpOver, w->picture, w->frame_alpha_pict, root_buffer, 0, 0, 0, 0, x, y, wid, t); /* left */ XRenderComposite( - dpy, PictOpOver, w->picture, w->alpha_border_pict, root_buffer, + dpy, PictOpOver, w->picture, w->frame_alpha_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, + dpy, PictOpOver, w->picture, w->frame_alpha_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, + dpy, PictOpOver, w->picture, w->frame_alpha_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, + dpy, PictOpOver, w->picture, alpha_mask, root_buffer, l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); } @@ -1600,9 +1617,6 @@ determine_mode(Display *dpy, win *w) { /* if trans prop == -1 fall back on previous tests */ - free_picture(dpy, &w->alpha_pict); - free_picture(dpy, &w->alpha_border_pict); - if (w->a.class == InputOnly) { format = 0; } else { @@ -1774,8 +1788,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->damage = XDamageCreate(dpy, id, XDamageReportNonEmpty); } - new->alpha_pict = None; - new->alpha_border_pict = None; new->shadow = False; new->shadow_pict = None; new->border_size = None; @@ -1785,7 +1797,12 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_width = 0; new->shadow_height = 0; new->opacity = OPAQUE; + new->opacity_cur = OPAQUE; new->opacity_prop = OPAQUE; + new->alpha_pict = None; + new->frame_opacity = 1.0; + new->frame_opacity_cur = 1.0; + new->frame_alpha_pict = None; new->dim = False; new->focused = False; new->destroyed = False; @@ -1968,7 +1985,7 @@ finish_destroy_win(Display *dpy, Window id) { *prev = w->next; free_picture(dpy, &w->alpha_pict); - free_picture(dpy, &w->alpha_border_pict); + free_picture(dpy, &w->frame_alpha_pict); free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); diff --git a/compton.h b/compton.h index ce8e4d298..ef49acb50 100644 --- a/compton.h +++ b/compton.h @@ -95,8 +95,6 @@ typedef struct _win { int damaged; Damage damage; Picture picture; - Picture alpha_pict; - Picture alpha_border_pict; XserverRegion border_size; XserverRegion extents; Bool shadow; @@ -105,9 +103,23 @@ typedef struct _win { int shadow_dy; int shadow_width; int shadow_height; + + /// Current window opacity. opacity_t opacity; + /// Opacity of current alpha_pict. + opacity_t opacity_cur; /// Cached value of opacity window attribute. opacity_t opacity_prop; + /// Alpha mask Picture to render window with opacity. + Picture alpha_pict; + + /// Current window frame opacity. + double frame_opacity; + /// Opacity of current frame_alpha_pict. + opacity_t frame_opacity_cur; + /// Alpha mask Picture to render window frame with opacity. + Picture frame_alpha_pict; + /// Whether the window is to be dimmed. Bool dim; wintype window_type; @@ -392,7 +404,7 @@ make_shadow(Display *dpy, double opacity, int width, int height); static Picture -shadow_picture(Display *dpy, double opacity, Picture alpha_pict, +shadow_picture(Display *dpy, double opacity, int width, int height, int *wp, int *hp); static Picture From e7ba091cccc4eba365302f79eee9ab0abe1ec253 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 17 Sep 2012 16:04:04 +0800 Subject: [PATCH 031/178] Improvement: Change painting sequence - Now compton paints windows from the lowest to the highest. Warning: I'm not completely certain that the change won't introduce unexpected glitches. This commit may be revoked in the future. - Remove w->border_clip since it's no longer needed. - Correct a mistake in find_toplevel2(). (clang --analyze found it out.) - Change "func_name()" prototypes to "func_name(void)". If I remember correctly, "func_name()" means you are remaining silent about this function's parameters instead of stating it has no parameter in ANSI C. - Add timestamps to error messages. - Suppress error messages caused by free_damage(). --- compton.c | 183 +++++++++++++++++++++--------------------------------- compton.h | 34 +++++----- 2 files changed, 86 insertions(+), 131 deletions(-) diff --git a/compton.c b/compton.c index a6a99a887..544121ea9 100644 --- a/compton.c +++ b/compton.c @@ -14,9 +14,7 @@ * Shared */ -#ifdef DEBUG_EVENTS struct timeval time_start = { 0, 0 }; -#endif win *list; fade *fades; @@ -774,7 +772,7 @@ find_toplevel2(Display *dpy, Window wid) { // just leave it alone. if (!XQueryTree(dpy, wid, &troot, &parent, &tchildren, &tnchildren)) { - wid = 0; + parent = 0; break; } @@ -1114,7 +1112,6 @@ paint_all(Display *dpy, XserverRegion region) { if (clip_changed) { free_region(dpy, &w->border_size); free_region(dpy, &w->extents); - free_region(dpy, &w->border_clip); } if (!w->border_size) { @@ -1150,43 +1147,6 @@ paint_all(Display *dpy, XserverRegion region) { w->frame_opacity_cur = w->frame_opacity; } - if (w->mode == WINDOW_SOLID && !w->frame_opacity) { - 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->dim) { - XRenderComposite(dpy, PictOpOver, dim_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; } @@ -1196,15 +1156,29 @@ paint_all(Display *dpy, XserverRegion region) { fflush(stdout); #endif - XFixesSetPictureClipRegion(dpy, - root_buffer, 0, 0, region); + 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); + 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 + + // Allow shadow to be painted anywhere in the damaged region + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); + + // Painting shadow if (win_type_shadow[w->window_type]) { XRenderComposite( dpy, PictOpOver, cshadow_picture, w->shadow_pict, @@ -1213,72 +1187,59 @@ paint_all(Display *dpy, XserverRegion region) { w->shadow_width, w->shadow_height); } - if (w->mode != WINDOW_SOLID || w->frame_opacity) { - int x, y, wid, hei; + // The window only could be painted in its bounding region + XserverRegion paint_reg = XFixesCreateRegion(dpy, NULL, 0); + XFixesIntersectRegion(dpy, paint_reg, region, w->border_size); + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, paint_reg); - // Necessary to make sure specially-shaped windows are - // painted correctly - XFixesIntersectRegion(dpy, w->border_clip, w->border_clip, w->border_size); - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, w->border_clip); -#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 + Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); + int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); - set_ignore(dpy, NextRequest(dpy)); + // Painting the window + if (!w->frame_opacity) { + XRenderComposite(dpy, op, w->picture, alpha_mask, + 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; - Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); + /* top */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->frame_alpha_pict, root_buffer, + 0, 0, 0, 0, x, y, wid, t); - if (!w->frame_opacity) { - XRenderComposite( - dpy, PictOpOver, w->picture, alpha_mask, - 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->frame_alpha_pict, root_buffer, - 0, 0, 0, 0, x, y, wid, t); - - /* left */ - XRenderComposite( - dpy, PictOpOver, w->picture, w->frame_alpha_pict, root_buffer, - 0, t, 0, t, x, y + t, l, hei - t); - - /* bottom */ - XRenderComposite( - dpy, PictOpOver, w->picture, w->frame_alpha_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->frame_alpha_pict, root_buffer, - wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); - - /* body */ - XRenderComposite( - dpy, PictOpOver, w->picture, alpha_mask, root_buffer, - l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); - } + /* left */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->frame_alpha_pict, root_buffer, + 0, t, 0, t, x, y + t, l, hei - t); + + /* bottom */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->frame_alpha_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->frame_alpha_pict, root_buffer, + wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); + + /* body */ + XRenderComposite( + dpy, op, w->picture, alpha_mask, root_buffer, + l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); - if (w->dim) { - XRenderComposite(dpy, PictOpOver, dim_picture, None, - root_buffer, 0, 0, 0, 0, x, y, wid, hei); - } } - free_region(dpy, &w->border_clip); + // Dimming the window if needed + if (w->dim) { + XRenderComposite(dpy, PictOpOver, dim_picture, None, + root_buffer, 0, 0, 0, 0, x, y, wid, hei); + } + + XFixesDestroyRegion(dpy, paint_reg); } XFixesDestroyRegion(dpy, region); @@ -1547,7 +1508,6 @@ finish_unmap_win(Display *dpy, win *w) { free_picture(dpy, &w->picture); free_region(dpy, &w->border_size); free_picture(dpy, &w->shadow_pict); - free_region(dpy, &w->border_clip); clip_changed = True; } @@ -1567,7 +1527,8 @@ unmap_win(Display *dpy, Window id, Bool fade) { w->a.map_state = IsUnmapped; - /* don't care about properties anymore */ + // don't care about properties anymore + // Will get BadWindow if the window is destroyed set_ignore(dpy, NextRequest(dpy)); XSelectInput(dpy, w->id, 0); @@ -1809,7 +1770,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->need_configure = False; new->window_type = WINTYPE_UNKNOWN; - new->border_clip = None; new->prev_trans = 0; new->left_width = 0; @@ -2178,6 +2138,7 @@ error(Display *dpy, XErrorEvent *ev) { break; } + print_timestamp(); printf("error %d (%s) request %d minor %d serial %lu\n", ev->error_code, name, ev->request_code, ev->minor_code, ev->serial); @@ -2577,7 +2538,7 @@ ev_handle(XEvent *ev) { */ static void -usage() { +usage(void) { fprintf(stderr, "compton v0.0.1\n"); fprintf(stderr, "usage: compton [options]\n"); fprintf(stderr, @@ -2669,7 +2630,7 @@ register_cm(int scr) { } static void -fork_after() { +fork_after(void) { if (getppid() == 1) return; int pid = fork(); @@ -2689,7 +2650,7 @@ fork_after() { } static void -get_atoms() { +get_atoms(void) { extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); opacity_atom = XInternAtom(dpy, @@ -2758,9 +2719,7 @@ main(int argc, char **argv) { double shadow_green = 0.0; double shadow_blue = 0.0; -#ifdef DEBUG_EVENTS gettimeofday(&time_start, NULL); -#endif for (i = 0; i < NUM_WINTYPES; ++i) { win_type_fade[i] = False; diff --git a/compton.h b/compton.h index ef49acb50..3ca318fe8 100644 --- a/compton.h +++ b/compton.h @@ -36,11 +36,9 @@ // #define DEBUG_WINTYPE 1 // #define MONITOR_REPAINT 1 -#ifdef DEBUG_EVENTS // For printing timestamps #include extern struct timeval time_start; -#endif #define OPAQUE 0xffffffff #define REGISTER_PROP "_NET_WM_CM_S" @@ -135,8 +133,6 @@ typedef struct _win { Bool need_configure; XConfigureEvent queue_configure; - /* for drawing translucent windows */ - XserverRegion border_clip; struct _win *prev_trans; } win; @@ -173,6 +169,15 @@ extern Atom atom_client_attr; // Helper functions +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); + /** * Normalize an int value to a specific range. * @@ -218,7 +223,6 @@ array_wid_exists(const Window *arr, int count, Window wid) { return False; } -#ifdef DEBUG_EVENTS /* * Subtracting two struct timeval values. * @@ -267,7 +271,6 @@ print_timestamp(void) { timeval_subtract(&diff, &tm, &time_start); printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); } -#endif /** * Destroy a XserverRegion. @@ -308,6 +311,8 @@ free_pixmap(Display *dpy, Pixmap *p) { inline static void free_damage(Display *dpy, Damage *p) { if (*p) { + // BadDamage will be thrown if the window is destroyed + set_ignore(dpy, NextRequest(dpy)); XDamageDestroy(dpy, *p); *p = None; } @@ -360,7 +365,7 @@ win_get_children(Display *dpy, Window w, } static int -get_time_in_milliseconds(); +get_time_in_milliseconds(void); static fade * find_fade(win *w); @@ -411,15 +416,6 @@ 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 long determine_evmask(Display *dpy, Window wid, win_evmode_t mode); @@ -558,7 +554,7 @@ ev_window(XEvent *ev); #endif static void -usage(); +usage(void); static void register_cm(int scr); @@ -645,7 +641,7 @@ inline static void ev_handle(XEvent *ev); static void -fork_after(); +fork_after(void); static void -get_atoms(); +get_atoms(void); From be3451e09798b1ec72c69e1372e94257219f4abc Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 17 Sep 2012 22:15:04 +0800 Subject: [PATCH 032/178] Improvement: Defer shadow picture generation - Defer shadow picture generation to minimize interactions with X, hoping to boost performance. - Fix a rendering issue caused by clip_changed in configure_win(). Remove clip_changed altogether. - Split generation of shadow picture from calculating its geometry. - Cache width/height including borders in struct _win as it's frequently used. --- compton.c | 167 +++++++++++++++++++++++++++++++----------------------- compton.h | 66 +++++++++++++++------ 2 files changed, 145 insertions(+), 88 deletions(-) diff --git a/compton.c b/compton.c index 544121ea9..389b857b3 100644 --- a/compton.c +++ b/compton.c @@ -30,7 +30,6 @@ Picture cshadow_picture; Picture dim_picture = 0; Picture root_tile; XserverRegion all_damage; -Bool clip_changed; #if HAS_NAME_WINDOW_PIXMAP Bool has_name_pixmap; #endif @@ -276,16 +275,6 @@ run_fades(Display *dpy) { determine_mode(dpy, w); - if (w->shadow_pict) { - XRenderFreePicture(dpy, w->shadow_pict); - w->shadow_pict = None; - - free_region(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); @@ -577,8 +566,7 @@ make_shadow(Display *dpy, double opacity, } static Picture -shadow_picture(Display *dpy, double opacity, - int width, int height, int *wp, int *hp) { +shadow_picture(Display *dpy, double opacity, int width, int height) { XImage *shadow_image; Pixmap shadow_pixmap; Picture shadow_picture; @@ -616,8 +604,6 @@ shadow_picture(Display *dpy, double opacity, dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, shadow_image->width, shadow_image->height); - *wp = shadow_image->width; - *hp = shadow_image->height; XFreeGC(dpy, gc); XDestroyImage(shadow_image); XFreePixmap(dpy, shadow_pixmap); @@ -881,39 +867,24 @@ paint_root(Display *dpy) { root_width, root_height); } +/** + * Get a rectangular region a window (and possibly its shadow) occupies. + * + * Note w->shadow and shadow geometry must be correct before calling this + * function. + */ 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; + r.width = w->widthb; + r.height = w->heightb; - if (win_type_shadow[w->window_type]) { + if (w->shadow) { XRectangle sr; - w->shadow_dx = shadow_offset_x; - w->shadow_dy = shadow_offset_y; - - if (!w->shadow_pict) { - double opacity = shadow_opacity; - - if (w->mode != WINDOW_SOLID) { - opacity = opacity * get_opacity_percent(dpy, w); - } - - if (HAS_FRAME_OPACITY(w)) { - opacity = opacity * frame_opacity; - } - - w->shadow_pict = shadow_picture( - dpy, opacity, - 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; @@ -953,12 +924,10 @@ border_size(Display *dpy, win *w) { * 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); @@ -1109,11 +1078,6 @@ paint_all(Display *dpy, XserverRegion region) { printf(" %#010lx", w->id); #endif - if (clip_changed) { - free_region(dpy, &w->border_size); - free_region(dpy, &w->extents); - } - if (!w->border_size) { w->border_size = border_size(dpy, w); } @@ -1147,6 +1111,28 @@ paint_all(Display *dpy, XserverRegion region) { w->frame_opacity_cur = w->frame_opacity; } + // Calculate shadow opacity + if (w->frame_opacity) + w->shadow_opacity = shadow_opacity * w->frame_opacity; + else + w->shadow_opacity = shadow_opacity * get_opacity_percent(dpy, w); + + // Rebuild shadow_pict if necessary + if (w->flags & WFLAG_SIZE_CHANGE) + free_picture(dpy, &w->shadow_pict); + + if (w->shadow + && (!w->shadow_pict + || w->shadow_opacity != w->shadow_opacity_cur)) { + free_picture(dpy, &w->shadow_pict); + w->shadow_pict = shadow_picture(dpy, w->shadow_opacity, + w->widthb, w->heightb); + w->shadow_opacity_cur = w->shadow_opacity; + } + + // Reset flags + w->flags = 0; + w->prev_trans = t; t = w; } @@ -1166,8 +1152,8 @@ paint_all(Display *dpy, XserverRegion region) { #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; + wid = w->widthb; + hei = w->heightb; #else x = w->a.x + w->a.border_width; y = w->a.y + w->a.border_width; @@ -1429,6 +1415,19 @@ map_win(Display *dpy, Window id, w->a.map_state = IsViewable; w->window_type = determine_wintype(dpy, w->id, w->id); + // Window type change could affect shadow + { + Bool shadow_old = w->shadow; + determine_shadow(dpy, w); + if (w->shadow != shadow_old) { + calc_shadow_geometry(dpy, w); + if (w->extents) { + free_region(dpy, &w->extents); + w->extents = win_extents(dpy, w); + } + } + } + #ifdef DEBUG_WINTYPE printf("map_win(): window %#010lx type %s\n", w->id, wintype_name(w->window_type)); @@ -1508,8 +1507,6 @@ finish_unmap_win(Display *dpy, win *w) { free_picture(dpy, &w->picture); free_region(dpy, &w->border_size); free_picture(dpy, &w->shadow_pict); - - clip_changed = True; } #if HAS_NAME_WINDOW_PIXMAP @@ -1605,16 +1602,6 @@ set_opacity(Display *dpy, win *w, opacity_t opacity) { w->opacity = opacity; determine_mode(dpy, w); - - if (w->shadow_pict) { - XRenderFreePicture(dpy, w->shadow_pict); - w->shadow_pict = None; - - free_region(dpy, &w->extents); - - /* rebuild the shadow */ - w->extents = win_extents(dpy, w); - } } /** @@ -1683,6 +1670,37 @@ calc_dim(Display *dpy, win *w) { } } +/** + * Determine if a window should have shadow. + */ +static void +determine_shadow(Display *dpy, win *w) { + w->shadow = win_type_shadow[w->window_type]; +} + +/** + * Update cache data in struct _win that depends on window size. + */ + +static void +calc_win_size(Display *dpy, win *w) { + w->widthb = w->a.width + w->a.border_width * 2; + w->heightb = w->a.height + w->a.border_width * 2; + calc_shadow_geometry(dpy, w); + w->flags |= WFLAG_SIZE_CHANGE; +} + +/** + * Calculate and update geometry of the shadow of a window. + */ +static void +calc_shadow_geometry(Display *dpy, win *w) { + w->shadow_dx = shadow_offset_x; + w->shadow_dy = shadow_offset_y; + w->shadow_width = w->widthb + gaussian_map->size; + w->shadow_height = w->heightb + gaussian_map->size; +} + /** * Mark a window as the client window of another. * @@ -1749,10 +1767,12 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->damage = XDamageCreate(dpy, id, XDamageReportNonEmpty); } - new->shadow = False; - new->shadow_pict = None; new->border_size = None; new->extents = None; + new->shadow = False; + new->shadow_opacity = 0.0; + new->shadow_opacity_cur = 0.0; + new->shadow_pict = None; new->shadow_dx = 0; new->shadow_dy = 0; new->shadow_width = 0; @@ -1779,6 +1799,10 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->client_win = 0; + new->flags = 0; + + calc_win_size(dpy, new); + new->next = *p; *p = new; @@ -1898,12 +1922,15 @@ configure_win(Display *dpy, XConfigureEvent *ce) { free_pixmap(dpy, &w->pixmap); free_picture(dpy, &w->picture); #endif - free_picture(dpy, &w->shadow_pict); } - w->a.width = ce->width; - w->a.height = ce->height; - w->a.border_width = ce->border_width; + if (w->a.width != ce->width || w->a.height != ce->height + || w->a.border_width != ce->border_width) { + w->a.width = ce->width; + w->a.height = ce->height; + w->a.border_width = ce->border_width; + calc_win_size(dpy, w); + } if (w->a.map_state != IsUnmapped && damage) { XserverRegion extents = win_extents(dpy, w); @@ -1912,7 +1939,9 @@ configure_win(Display *dpy, XConfigureEvent *ce) { add_damage(dpy, damage); } - clip_changed = True; + // Window extents and border_size may have changed + free_region(dpy, &w->extents); + free_region(dpy, &w->border_size); } w->a.override_redirect = ce->override_redirect; @@ -1932,7 +1961,6 @@ circulate_win(Display *dpy, XCirculateEvent *ce) { } restack_win(dpy, w, new_above); - clip_changed = True; } static void @@ -2016,7 +2044,6 @@ damage_win(Display *dpy, XDamageNotifyEvent *de) { && 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); @@ -2935,7 +2962,6 @@ main(int argc, char **argv) { } all_damage = None; - clip_changed = True; XGrabServer(dpy); XCompositeRedirectSubwindows( @@ -2986,7 +3012,6 @@ main(int argc, char **argv) { paint++; XSync(dpy, False); all_damage = None; - clip_changed = False; } } } diff --git a/compton.h b/compton.h index 3ca318fe8..19f5f2aac 100644 --- a/compton.h +++ b/compton.h @@ -47,6 +47,11 @@ extern struct timeval time_start; #define WINDOW_TRANS 1 #define WINDOW_ARGB 2 +// Window flags + +// Window size is changed +#define WFLAG_SIZE_CHANGE 0x0001 + /** * Types */ @@ -95,12 +100,17 @@ typedef struct _win { Picture picture; XserverRegion border_size; XserverRegion extents; - Bool shadow; - Picture shadow_pict; - int shadow_dx; - int shadow_dy; - int shadow_width; - int shadow_height; + // Type of the window. + wintype window_type; + /// Whether the window is focused. + Bool focused; + Bool destroyed; + /// Cached width/height of the window including border. + int widthb, heightb; + unsigned int left_width; + unsigned int right_width; + unsigned int top_width; + unsigned int bottom_width; /// Current window opacity. opacity_t opacity; @@ -111,24 +121,38 @@ typedef struct _win { /// Alpha mask Picture to render window with opacity. Picture alpha_pict; - /// Current window frame opacity. + /// Current window frame opacity. Affected by window opacity. double frame_opacity; /// Opacity of current frame_alpha_pict. opacity_t frame_opacity_cur; /// Alpha mask Picture to render window frame with opacity. Picture frame_alpha_pict; + /// Whether a window has shadow. Affected by window type. + Bool shadow; + /// Opacity of the shadow. Affected by window opacity and frame opacity. + double shadow_opacity; + /// Opacity of current shadow_pict. + double shadow_opacity_cur; + /// X offset of shadow. Affected by commandline argument. + int shadow_dx; + /// Y offset of shadow. Affected by commandline argument. + int shadow_dy; + /// Width of shadow. Affected by window size and commandline argument. + int shadow_width; + /// Height of shadow. Affected by window size and commandline argument. + int shadow_height; + /// Alpha mask Picture to render shadow. Affected by window size and + /// shadow opacity. + Picture shadow_pict; + /// Whether the window is to be dimmed. Bool dim; - wintype window_type; - /// Whether the window is focused. - Bool focused; + + /// Window flags. Definitions above. + int_fast16_t flags; + 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; @@ -409,8 +433,7 @@ make_shadow(Display *dpy, double opacity, int width, int height); static Picture -shadow_picture(Display *dpy, double opacity, - int width, int height, int *wp, int *hp); +shadow_picture(Display *dpy, double opacity, int width, int height); static Picture solid_picture(Display *dpy, Bool argb, double a, @@ -502,6 +525,15 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop); static void calc_dim(Display *dpy, win *w); +static void +determine_shadow(Display *dpy, win *w); + +static void +calc_win_size(Display *dpy, win *w); + +static void +calc_shadow_geometry(Display *dpy, win *w); + static void mark_client_win(Display *dpy, win *w, Window client); From fb18759b02d274602fdc75ee7f00a2f8bc42676b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 18 Sep 2012 11:28:09 +0800 Subject: [PATCH 033/178] Bug fix: Issue #23, notify-osd not rendered More details in the issue report. - Add ClientMessage detection to ev_name() and ev_window(), although we don't actually handle the event. --- compton.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/compton.c b/compton.c index 389b857b3..d620a7e66 100644 --- a/compton.c +++ b/compton.c @@ -1055,6 +1055,10 @@ paint_all(Display *dpy, XserverRegion region) { continue; } +#ifdef DEBUG_REPAINT + printf(" %#010lx", w->id); +#endif + if (!w->picture) { XRenderPictureAttributes pa; XRenderPictFormat *format; @@ -1074,10 +1078,6 @@ paint_all(Display *dpy, XserverRegion region) { dpy, draw, format, CPSubwindowMode, &pa); } -#ifdef DEBUG_REPAINT - printf(" %#010lx", w->id); -#endif - if (!w->border_size) { w->border_size = border_size(dpy, w); } @@ -1433,22 +1433,21 @@ map_win(Display *dpy, Window id, w->id, wintype_name(w->window_type)); #endif - /* select before reading the property - so that no property changes are lost */ - if (!override_redirect) { - // Detect client window here instead of in add_win() as the client - // window should have been prepared at this point - if (!(w->client_win)) { - Window cw = find_client_win(dpy, w->id); - if (cw) { - mark_client_win(dpy, w, cw); - } - } + // Call XSelectInput() before reading properties so that no property + // changes are lost + XSelectInput(dpy, id, determine_evmask(dpy, id, WIN_EVMODE_FRAME)); + + // Notify compton when the shape of a window changes + if (shape_exists) { + XShapeSelectInput(dpy, id, ShapeNotifyMask); + } - XSelectInput(dpy, id, determine_evmask(dpy, id, WIN_EVMODE_FRAME)); - // Notify compton when the shape of a window changes - if (shape_exists) { - XShapeSelectInput(dpy, id, ShapeNotifyMask); + // Detect client window here instead of in add_win() as the client + // window should have been prepared at this point + if (!(w->client_win)) { + Window cw = find_client_win(dpy, w->id); + if (cw) { + mark_client_win(dpy, w, cw); } } @@ -2247,6 +2246,8 @@ ev_name(XEvent *ev) { return "Expose"; case PropertyNotify: return "PropertyNotify"; + case ClientMessage: + return "ClientMessage"; default: if (ev->type == damage_event + XDamageNotify) { return "Damage"; @@ -2284,6 +2285,8 @@ ev_window(XEvent *ev) { return ev->xexpose.window; case PropertyNotify: return ev->xproperty.window; + case ClientMessage: + return ev->xclient.window; default: if (ev->type == damage_event + XDamageNotify) { return ((XDamageNotifyEvent *)ev)->drawable; From a08c22945a825eb43607edc7317611c6b6049232 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 19 Sep 2012 20:49:16 +0800 Subject: [PATCH 034/178] Improvement: Change fading mechanism - Change fading mechanism for better modularity. Remove fade queue and use members in struct _win to record fading data. In contrast to previous commits, this one actually could make the program slower (but very slightly, hardly noticeable if your CPU is anywhere close to AMD K7). As this commit changes lots of things, bugs are to be expected. - Currently -F does not do its job. -f actually equals -fF. (While in the past -F equals nothing and -f is just -f.) A fix will be made soon. I suppose it isn't hard. - Add a preprocessor function paint_preprocess() and move all preprocessing code in paint_all() to it. - Add window flag support but currently unused. - Add DamageNotify handling to ev_window(). - I'm considering removing HAS_NAME_WINDOW_PIXMAP = 0 support as I couldn't see what it is good for. Nor do I know what CAN_DO_USABLE does. Basically all my changes ignore these cases. --- compton.c | 449 ++++++++++++++++++++++++------------------------------ compton.h | 97 +++++++----- 2 files changed, 259 insertions(+), 287 deletions(-) diff --git a/compton.c b/compton.c index d620a7e66..877eb2896 100644 --- a/compton.c +++ b/compton.c @@ -17,7 +17,6 @@ struct timeval time_start = { 0, 0 }; win *list; -fade *fades; Display *dpy; int scr; @@ -97,10 +96,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; +/// How much to fade in in a single fading step. +opacity_t fade_in_step = 0.028 * OPAQUE; +/// How much to fade out in a single fading step. +opacity_t fade_out_step = 0.03 * OPAQUE; +unsigned long fade_delta = 10; +unsigned long fade_time = 0; Bool fade_trans = False; Bool clear_shadow = False; @@ -125,7 +126,13 @@ Bool synchronize = False; * Fades */ -static int +/** + * Get current system clock in milliseconds. + * + * The return type must be unsigned long because so many milliseconds have + * passed since the epoch. + */ +static unsigned long get_time_in_milliseconds() { struct timeval tv; @@ -134,153 +141,70 @@ get_time_in_milliseconds() { 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; - } +/** + * Get the time left before next fading point. + * + * In milliseconds. + */ +static int +fade_timeout(void) { + int diff = fade_delta - get_time_in_milliseconds() + fade_time; - return 0; -} + if (diff < 0) + diff = 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; - } - } + return diff; } +/** + * Run fading on a window. + * + * @param steps steps of fading + */ static void -cleanup_fade(Display *dpy, win *w) { - fade *f = find_fade (w); - if (f) { - dequeue_fade(dpy, f); +run_fade(Display *dpy, win *w, unsigned steps) { + // If we reach target opacity, set fade_fin so the callback gets + // executed + if (w->opacity == w->opacity_tgt) { + w->fade_fin = True; + return; } -} -static void -enqueue_fade(Display *dpy, fade *f) { - if (!fades) { - fade_time = get_time_in_milliseconds() + fade_delta; + if (!w->fade) + w->opacity = w->opacity_tgt; + else if (steps) { + // Use double below because opacity_t will probably overflow during + // calculations + if (w->opacity < w->opacity_tgt) + w->opacity = normalize_d_range( + (double) w->opacity + (double) fade_in_step * steps, + 0.0, w->opacity_tgt); + else + w->opacity = normalize_d_range( + (double) w->opacity - (double) fade_out_step * steps, + w->opacity_tgt, OPAQUE); } - 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) { + if (w->opacity == w->opacity_tgt) { + w->fade_fin = True; 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; - set_opacity(dpy, w, f->cur * OPAQUE); - - /* 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; + w->fade_fin = False; } +/** + * Set fade callback of a window, and possibly execute the previous + * callback. + * + * @param exec_callback whether the previous callback is to be executed + */ 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); - - /* Must do this last as it might - destroy f->w in callbacks */ - if (need_dequeue) dequeue_fade(dpy, f); - } - - fade_time = now + fade_delta; +set_fade_callback(Display *dpy, win *w, + void (*callback) (Display *dpy, win *w), Bool exec_callback) { + if (exec_callback && w->fade_callback) + (w->fade_callback)(dpy, w); + w->fade_callback = callback; } /** @@ -1003,62 +927,39 @@ get_frame_extents(Display *dpy, Window w, } } -static void -paint_all(Display *dpy, XserverRegion region) { +static win * +paint_preprocess(Display *dpy, win *list) { win *w; - win *t = 0; - - if (!region) { - region = get_screen_region(dpy); - } - -#ifdef MONITOR_REPAINT - root_buffer = root_picture; -#else - if (!root_buffer) { - Pixmap root_pixmap = XCreatePixmap( - dpy, root, root_width, root_height, - DefaultDepth(dpy, scr)); - - root_buffer = XRenderCreatePicture(dpy, root_pixmap, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), - 0, 0); + win *t = NULL, *next = NULL; + // Sounds like the timeout in poll() frequently does not work + // accurately, asking it to wait to 20ms, and often it would wait for + // 19ms, so the step value has to be rounded. + unsigned steps = roundl((double) (get_time_in_milliseconds() - fade_time) / fade_delta); - XFreePixmap(dpy, root_pixmap); - } -#endif + // Reset fade_time + fade_time = get_time_in_milliseconds(); - XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); + for (w = list; w; w = next) { + // In case calling the fade callback function destroys this window + next = w->next; + opacity_t opacity_old = w->opacity; -#ifdef MONITOR_REPAINT - XRenderComposite( - dpy, PictOpSrc, black_picture, None, - root_picture, 0, 0, 0, 0, 0, 0, - root_width, root_height); -#endif - -#ifdef 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; + // Run fading + run_fade(dpy, w, steps); - /* if invisible, ignore it */ - if (w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 + // Give up if it's not damaged or invisible + if (!w->damaged + || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= root_width || w->a.y >= root_height) { + check_fade_fin(dpy, w); continue; } -#ifdef DEBUG_REPAINT - printf(" %#010lx", w->id); -#endif - + // Fetch the picture and pixmap if needed if (!w->picture) { XRenderPictureAttributes pa; XRenderPictFormat *format; @@ -1078,6 +979,7 @@ paint_all(Display *dpy, XserverRegion region) { dpy, draw, format, CPSubwindowMode, &pa); } + // Fetch bounding region and extents if needed if (!w->border_size) { w->border_size = border_size(dpy, w); } @@ -1086,6 +988,17 @@ paint_all(Display *dpy, XserverRegion region) { w->extents = win_extents(dpy, w); } + // If opacity changes + if (w->opacity != opacity_old) { + determine_mode(dpy, w); + add_damage_win(dpy, w); + } + + if (!w->opacity) { + check_fade_fin(dpy, w); + continue; + } + // Rebuild alpha_pict only if necessary if (OPAQUE != w->opacity && (!w->alpha_pict || w->opacity != w->opacity_cur)) { @@ -1137,15 +1050,48 @@ paint_all(Display *dpy, XserverRegion region) { t = w; } -#ifdef DEBUG_REPAINT - printf("\n"); - fflush(stdout); + return t; +} + +static void +paint_all(Display *dpy, XserverRegion region, win *t) { + win *w; + + if (!region) { + region = get_screen_region(dpy); + } + +#ifdef MONITOR_REPAINT + root_buffer = root_picture; +#else + if (!root_buffer) { + Pixmap root_pixmap = XCreatePixmap( + dpy, root, root_width, root_height, + DefaultDepth(dpy, scr)); + + root_buffer = XRenderCreatePicture(dpy, root_pixmap, + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + 0, 0); + + XFreePixmap(dpy, root_pixmap); + } #endif - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); + XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); + +#ifdef MONITOR_REPAINT + XRenderComposite( + dpy, PictOpSrc, black_picture, None, + root_picture, 0, 0, 0, 0, 0, 0, + root_width, root_height); +#endif paint_root(dpy); +#ifdef DEBUG_REPAINT + printf("paint:"); +#endif + for (w = t; w; w = w->prev_trans) { int x, y, wid, hei; @@ -1161,6 +1107,10 @@ paint_all(Display *dpy, XserverRegion region) { hei = w->a.height; #endif +#ifdef DEBUG_REPAINT + printf(" %#010lx", w->id); +#endif + // Allow shadow to be painted anywhere in the damaged region XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); @@ -1226,8 +1176,15 @@ paint_all(Display *dpy, XserverRegion region) { } XFixesDestroyRegion(dpy, paint_reg); + + check_fade_fin(dpy, w); } +#ifdef DEBUG_REPAINT + printf("\n"); + fflush(stdout); +#endif + XFixesDestroyRegion(dpy, region); if (root_buffer != root_picture) { @@ -1415,18 +1372,12 @@ map_win(Display *dpy, Window id, w->a.map_state = IsViewable; w->window_type = determine_wintype(dpy, w->id, w->id); - // Window type change could affect shadow - { - Bool shadow_old = w->shadow; - determine_shadow(dpy, w); - if (w->shadow != shadow_old) { - calc_shadow_geometry(dpy, w); - if (w->extents) { - free_region(dpy, &w->extents); - w->extents = win_extents(dpy, w); - } - } - } + // Window type change could affect shadow and fade + determine_shadow(dpy, w); + determine_fade(dpy, w); + + // Determine mode here just in case the colormap changes + determine_mode(dpy, w); #ifdef DEBUG_WINTYPE printf("map_win(): window %#010lx type %s\n", @@ -1461,22 +1412,18 @@ map_win(Display *dpy, Window id, recheck_focus(dpy); } + // Fading in calc_opacity(dpy, w, True); - calc_dim(dpy, w); + set_fade_callback(dpy, w, NULL, True); - determine_mode(dpy, w); + calc_dim(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; + w->damaged = 1; - 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 @@ -1508,12 +1455,10 @@ finish_unmap_win(Display *dpy, win *w) { free_picture(dpy, &w->shadow_pict); } -#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) { @@ -1523,6 +1468,10 @@ unmap_win(Display *dpy, Window id, Bool fade) { w->a.map_state = IsUnmapped; + // Fading out + w->opacity_tgt = 0; + set_fade_callback(dpy, w, unmap_callback, False); + // don't care about properties anymore // Will get BadWindow if the window is destroyed set_ignore(dpy, NextRequest(dpy)); @@ -1532,14 +1481,6 @@ unmap_win(Display *dpy, Window id, Bool fade) { set_ignore(dpy, NextRequest(dpy)); XSelectInput(dpy, w->client_win, 0); } - -#if HAS_NAME_WINDOW_PIXMAP - if (w->pixmap && fade && win_type_fade[w->window_type]) { - set_fade(dpy, w, get_opacity_percent(dpy, w), 0.0, - fade_out_step, unmap_callback, False, True); - } else -#endif - finish_unmap_win(dpy, w); } static opacity_t @@ -1594,15 +1535,6 @@ determine_mode(Display *dpy, win *w) { add_damage_win(dpy, w); } -static void -set_opacity(Display *dpy, win *w, opacity_t opacity) { - // Do nothing if the opacity does not change - if (w->opacity == opacity) return; - - w->opacity = opacity; - determine_mode(dpy, w); -} - /** * Calculate and set the opacity of a window. * @@ -1650,7 +1582,7 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { opacity = inactive_opacity; } - set_opacity(dpy, w, opacity); + w->opacity_tgt = opacity; } static void @@ -1670,11 +1602,32 @@ calc_dim(Display *dpy, win *w) { } /** - * Determine if a window should have shadow. + * Determine if a window should fade on opacity change. + */ +static void +determine_fade(Display *dpy, win *w) { + w->fade = win_type_fade[w->window_type]; +} + +/** + * Determine if a window should have shadow, and update things depending + * on shadow state. */ static void determine_shadow(Display *dpy, win *w) { + Bool shadow_old = w->shadow; + w->shadow = win_type_shadow[w->window_type]; + + // Window extents need update on shadow state change + if (w->shadow != shadow_old) { + // Shadow geometry currently doesn't change on shadow state change + // calc_shadow_geometry(dpy, w); + if (w->extents) { + free_region(dpy, &w->extents); + w->extents = win_extents(dpy, w); + } + } } /** @@ -1776,9 +1729,13 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_dy = 0; new->shadow_width = 0; new->shadow_height = 0; - new->opacity = OPAQUE; + new->opacity = 0; + new->opacity_tgt = 0; new->opacity_cur = OPAQUE; new->opacity_prop = OPAQUE; + new->fade = False; + new->fade_callback = NULL; + new->fade_fin = False; new->alpha_pict = None; new->frame_opacity = 1.0; new->frame_opacity_cur = 1.0; @@ -1976,7 +1933,6 @@ finish_destroy_win(Display *dpy, Window id) { free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); - cleanup_fade(dpy, w); free(w); break; } @@ -1994,17 +1950,12 @@ static void destroy_win(Display *dpy, Window id, Bool fade) { win *w = find_win(dpy, id); - if (w) w->destroyed = True; + 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, get_opacity_percent(dpy, w), - 0.0, fade_out_step, destroy_callback, - False, True); - } else -#endif - { - finish_destroy_win(dpy, id); + // Fading out the window + w->opacity_tgt = 0; + set_fade_callback(dpy, w, destroy_callback, False); } } @@ -2273,6 +2224,8 @@ ev_window(XEvent *ev) { return ev->xcreatewindow.window; case ConfigureNotify: return ev->xconfigure.window; + case DestroyNotify: + return ev->xdestroywindow.window; case MapNotify: return ev->xmap.window; case UnmapNotify: @@ -2748,6 +2701,7 @@ main(int argc, char **argv) { double shadow_red = 0.0; double shadow_green = 0.0; double shadow_blue = 0.0; + win *t; gettimeofday(&time_start, NULL); @@ -2792,16 +2746,10 @@ main(int argc, char **argv) { } break; case 'I': - fade_in_step = atof(optarg); - if (fade_in_step <= 0) { - fade_in_step = 0.01; - } + fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'O': - fade_out_step = atof(optarg); - if (fade_out_step <= 0) { - fade_out_step = 0.01; - } + fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': for (i = 0; i < NUM_WINTYPES; ++i) { @@ -2882,6 +2830,8 @@ main(int argc, char **argv) { track_focus = True; } + fade_time = get_time_in_milliseconds(); + dpy = XOpenDisplay(display); if (!dpy) { fprintf(stderr, "Can't open display\n"); @@ -2994,13 +2944,13 @@ main(int argc, char **argv) { ufd.fd = ConnectionNumber(dpy); ufd.events = POLLIN; - paint_all(dpy, None); + t = paint_preprocess(dpy, list); + paint_all(dpy, None, t); for (;;) { do { if (!QLength(dpy)) { if (poll(&ufd, 1, fade_timeout()) == 0) { - run_fades(dpy); break; } } @@ -3009,9 +2959,10 @@ main(int argc, char **argv) { ev_handle((XEvent *)&ev); } while (QLength(dpy)); + t = paint_preprocess(dpy, list); if (all_damage) { static int paint; - paint_all(dpy, all_damage); + paint_all(dpy, all_damage, t); paint++; XSync(dpy, False); all_damage = None; diff --git a/compton.h b/compton.h index 19f5f2aac..4246dc4c9 100644 --- a/compton.h +++ b/compton.h @@ -107,13 +107,12 @@ typedef struct _win { Bool destroyed; /// Cached width/height of the window including border. int widthb, heightb; - unsigned int left_width; - unsigned int right_width; - unsigned int top_width; - unsigned int bottom_width; + // Opacity-related members /// Current window opacity. opacity_t opacity; + /// Target window opacity. + opacity_t opacity_tgt; /// Opacity of current alpha_pict. opacity_t opacity_cur; /// Cached value of opacity window attribute. @@ -121,13 +120,26 @@ typedef struct _win { /// Alpha mask Picture to render window with opacity. Picture alpha_pict; + // Fading-related members + /// Do not fade if it's false. Change on window type change. + /// Used by fading blacklist in the future. + Bool fade; + /// Callback to be called after fading completed. + void (*fade_callback) (Display *dpy, struct _win *w); + /// Whether fading is finished. + Bool fade_fin; + + // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. double frame_opacity; /// Opacity of current frame_alpha_pict. opacity_t frame_opacity_cur; /// Alpha mask Picture to render window frame with opacity. Picture frame_alpha_pict; + /// Frame widths. Determined by client window attributes. + unsigned int left_width, right_width, top_width, bottom_width; + // Shadow-related members /// Whether a window has shadow. Affected by window type. Bool shadow; /// Opacity of the shadow. Affected by window opacity and frame opacity. @@ -146,6 +158,7 @@ typedef struct _win { /// shadow opacity. Picture shadow_pict; + // Dim-related members /// Whether the window is to be dimmed. Bool dim; @@ -165,16 +178,6 @@ typedef struct _conv { 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; - typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, @@ -217,16 +220,30 @@ normalize_i_range(int i, int min, int max) { return i; } +/** + * Normalize a double value to a specific range. + * + * @param d double value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline double +normalize_d_range(double d, double min, double max) { + if (d > max) return max; + if (d < min) return min; + return d; +} + /** * Normalize a double value to 0.\ 0 - 1.\ 0. * * @param d double value to normalize + * @return normalized value */ static inline double normalize_d(double d) { - if (d > 1.0) return 1.0; - if (d < 0.0) return 0.0; - return d; + return normalize_d_range(d, 0.0, 1.0); } /** @@ -388,32 +405,33 @@ win_get_children(Display *dpy, Window w, return True; } -static int +static unsigned long get_time_in_milliseconds(void); -static fade * -find_fade(win *w); - -static void -dequeue_fade(Display *dpy, fade *f); - -static void -cleanup_fade(Display *dpy, win *w); +static int +fade_timeout(void); static void -enqueue_fade(Display *dpy, fade *f); +run_fade(Display *dpy, win *w, unsigned steps); 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); +set_fade_callback(Display *dpy, win *w, + void (*callback) (Display *dpy, win *w), Bool exec_callback); -static int -fade_timeout(void); +/** + * Execute fade callback of a window if fading finished. + */ +static inline void +check_fade_fin(Display *dpy, win *w) { + if (w->fade_fin) { + set_fade_callback(dpy, w, NULL, True); + w->fade_fin = False; + } +} static void -run_fades(Display *dpy); +set_fade_callback(Display *dpy, win *w, + void (*callback) (Display *dpy, win *w), Bool exec_callback); static double gaussian(double r, double x, double y); @@ -476,8 +494,11 @@ get_frame_extents(Display *dpy, Window w, unsigned int *top, unsigned int *bottom); +static win * +paint_preprocess(Display *dpy, win *list); + static void -paint_all(Display *dpy, XserverRegion region); +paint_all(Display *dpy, XserverRegion region, win *t); static void add_damage(Display *dpy, XserverRegion damage); @@ -516,15 +537,15 @@ get_opacity_percent(Display *dpy, win *w); static void determine_mode(Display *dpy, win *w); -static void -set_opacity(Display *dpy, win *w, opacity_t opacity); - static void calc_opacity(Display *dpy, win *w, Bool refetch_prop); static void calc_dim(Display *dpy, win *w); +static void +determine_fade(Display *dpy, win *w); + static void determine_shadow(Display *dpy, win *w); From fd0900ef892c97ae51563ac32ebbe895b7fc80a8 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 20 Sep 2012 13:50:27 +0800 Subject: [PATCH 035/178] Bug fix: Detect and mark WM windows as active See chjj's comments on issue #39: https://github.com/chjj/compton/issues/39#issuecomment-8533360 - Add a switch --mark-wmwin-focused that try to detect WM windows and mark them active. - Fix a bug that causes BadDrawable, etc. if a window is mapped then immediately unmapped. - Fix a bug in determine_evmask(). - Add a debug option DEBUG_CLIENTWIN. - Force window repaint on window frame extent change. - Code cleanup. --- compton.c | 118 +++++++++++++++++++++++++++--------------------------- compton.h | 14 ++++--- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/compton.c b/compton.c index 877eb2896..984447e75 100644 --- a/compton.c +++ b/compton.c @@ -66,10 +66,11 @@ XRectangle *expose_rects = 0; int size_expose = 0; int n_expose = 0; -/* atoms */ -Atom atom_client_attr; +// atoms Atom extents_atom; Atom opacity_atom; +Atom frame_extents_atom; +Atom client_atom; Atom win_type_atom; Atom win_type[NUM_WINTYPES]; double win_type_opacity[NUM_WINTYPES]; @@ -117,6 +118,9 @@ double frame_opacity = 0.0; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim = 0.0; +/// Whether to try to detect WM windows and mark them as focused. +double mark_wmwin_focused = False; + /// Whether compton needs to track focus changes. Bool track_focus = False; @@ -620,7 +624,7 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { if (track_focus) evmask |= FocusChangeMask; } - if (WIN_EVMODE_CLIENT == mode || find_client_win(dpy, wid)) { + if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) { if (frame_opacity) evmask |= PropertyChangeMask; } @@ -718,9 +722,7 @@ recheck_focus(Display *dpy) { // And we set the focus state and opacity here if (w) { - w->focused = True; - calc_opacity(dpy, w, False); - calc_dim(dpy, w); + set_focused(dpy, w, True); return w; } @@ -861,7 +863,7 @@ border_size(Display *dpy, win *w) { static Window find_client_win(Display *dpy, Window w) { - if (win_has_attr(dpy, w, atom_client_attr)) { + if (win_has_attr(dpy, w, client_atom)) { return w; } @@ -885,11 +887,7 @@ find_client_win(Display *dpy, Window w) { } static void -get_frame_extents(Display *dpy, Window w, - unsigned int *left, - unsigned int *right, - unsigned int *top, - unsigned int *bottom) { +get_frame_extents(Display *dpy, win *w, Window client) { long *extents; Atom type; int format; @@ -897,31 +895,24 @@ get_frame_extents(Display *dpy, Window w, unsigned char *data = NULL; int result; - *left = 0; - *right = 0; - *top = 0; - *bottom = 0; - - // w = find_client_win(dpy, w); - if (!w) return; + w->left_width = 0; + w->right_width = 0; + w->top_width = 0; + w->bottom_width = 0; result = XGetWindowProperty( - dpy, w, XInternAtom(dpy, "_NET_FRAME_EXTENTS", False), + dpy, client, frame_extents_atom, 0L, 4L, False, AnyPropertyType, &type, &format, &nitems, &after, - (unsigned char **)&data); + &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]; + extents = (long *) data; + w->left_width = extents[0]; + w->right_width = extents[1]; + w->top_width = extents[2]; + w->bottom_width = extents[3]; } XFree(data); } @@ -959,6 +950,17 @@ paint_preprocess(Display *dpy, win *list) { continue; } + // If opacity changes + if (w->opacity != opacity_old) { + determine_mode(dpy, w); + add_damage_win(dpy, w); + } + + if (!w->opacity) { + check_fade_fin(dpy, w); + continue; + } + // Fetch the picture and pixmap if needed if (!w->picture) { XRenderPictureAttributes pa; @@ -988,17 +990,6 @@ paint_preprocess(Display *dpy, win *list) { w->extents = win_extents(dpy, w); } - // If opacity changes - if (w->opacity != opacity_old) { - determine_mode(dpy, w); - add_damage_win(dpy, w); - } - - if (!w->opacity) { - check_fade_fin(dpy, w); - continue; - } - // Rebuild alpha_pict only if necessary if (OPAQUE != w->opacity && (!w->alpha_pict || w->opacity != w->opacity_cur)) { @@ -1395,12 +1386,20 @@ map_win(Display *dpy, Window id, // Detect client window here instead of in add_win() as the client // window should have been prepared at this point - if (!(w->client_win)) { + if (!w->client_win) { Window cw = find_client_win(dpy, w->id); +#ifdef DEBUG_CLIENTWIN + printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); +#endif if (cw) { mark_client_win(dpy, w, cw); } } + else if (frame_opacity) { + // Refetch frame extents just in case it changes when the window is + // unmapped + get_frame_extents(dpy, w, w->client_win); + } /* * Occasionally compton does not seem able to get a FocusIn event from a @@ -1410,6 +1409,10 @@ map_win(Display *dpy, Window id, */ if (track_focus) { recheck_focus(dpy); + // Consider a window without client window a WM window and mark it + // focused if mark_wmwin_focused is on + if (mark_wmwin_focused && !w->client_win) + w->focused = True; } // Fading in @@ -1667,8 +1670,7 @@ mark_client_win(Display *dpy, win *w, Window client) { // Get the frame width and monitor further frame width changes on client // window if necessary if (frame_opacity) { - get_frame_extents(dpy, client, - &w->left_width, &w->right_width, &w->top_width, &w->bottom_width); + get_frame_extents(dpy, w, client); } XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); } @@ -2265,9 +2267,7 @@ ev_focus_in(XFocusChangeEvent *ev) { // To deal with events sent from windows just destroyed if (!w) return; - w->focused = True; - calc_opacity(dpy, w, False); - calc_dim(dpy, w); + set_focused(dpy, w, True); } inline static void @@ -2286,10 +2286,7 @@ ev_focus_out(XFocusChangeEvent *ev) { // To deal with events sent from windows just destroyed if (!w) return; - w->focused = False; - - calc_opacity(dpy, w, False); - calc_dim(dpy, w); + set_focused(dpy, w, False); } inline static void @@ -2400,9 +2397,9 @@ ev_property_notify(XPropertyEvent *ev) { 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); + get_frame_extents(dpy, w, ev->window); + // If frame extents change, the window needs repaint + add_damage_win(dpy, w); } } } @@ -2634,10 +2631,10 @@ fork_after(void) { static void get_atoms(void) { - extents_atom = XInternAtom(dpy, - "_NET_FRAME_EXTENTS", False); - opacity_atom = XInternAtom(dpy, - "_NET_WM_WINDOW_OPACITY", False); + extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); + opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); + frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); + client_atom = XInternAtom(dpy, "WM_STATE", False); win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); @@ -2680,6 +2677,7 @@ main(int argc, char **argv) { { "shadow-blue", required_argument, NULL, 0 }, { "inactive-opacity-override", no_argument, NULL, 0 }, { "inactive-dim", required_argument, NULL, 0 }, + { "mark-wmwin-focused", no_argument, NULL, 0 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -2733,6 +2731,9 @@ main(int argc, char **argv) { case 4: inactive_dim = normalize_d(atof(optarg)); break; + case 5: + mark_wmwin_focused = True; + break; } break; // Short options @@ -2845,7 +2846,6 @@ main(int argc, char **argv) { scr = DefaultScreen(dpy); root = RootWindow(dpy, scr); - atom_client_attr = XInternAtom(dpy, "WM_STATE", False); if (!XRenderQueryExtension(dpy, &render_event, &render_error)) { fprintf(stderr, "No render extension\n"); diff --git a/compton.h b/compton.h index 4246dc4c9..0249e8470 100644 --- a/compton.h +++ b/compton.h @@ -34,6 +34,7 @@ // #define DEBUG_EVENTS 1 // #define DEBUG_RESTACK 1 // #define DEBUG_WINTYPE 1 +// #define DEBUG_CLIENTWIN 1 // #define MONITOR_REPAINT 1 // For printing timestamps @@ -488,11 +489,7 @@ static Window find_client_win(Display *dpy, Window w); static void -get_frame_extents(Display *dpy, Window w, - unsigned int *left, - unsigned int *right, - unsigned int *top, - unsigned int *bottom); +get_frame_extents(Display *dpy, win *w, Window client); static win * paint_preprocess(Display *dpy, win *list); @@ -543,6 +540,13 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop); static void calc_dim(Display *dpy, win *w); +static inline void +set_focused(Display *dpy, win *w, Bool focused) { + w->focused = focused; + calc_opacity(dpy, w, False); + calc_dim(dpy, w); +} + static void determine_fade(Display *dpy, win *w); From cbdaa9c000fada417cabf2092751945cbafc6ec0 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 22 Sep 2012 11:42:39 +0800 Subject: [PATCH 036/178] Feature: Issue #29: Alternative shadow blacklist implementation - Add shadow blacklist feature, but a different implementation from nicklan's. 5 matching modes (exact, starts-with, contains, wildcard, PCRE) and 3 matching targets (window name, window class instance, window general class). Not extensively tested, bugs to be expected. It's slower for exact matching than nicklan's as it uses linear search instead of hash table. Also, PCRE's JIT optimization may cause issues on PaX kernels. - Add dependency to libpcre. Could be made optional if we have a graceful way to handle that in Makefile. - Some matching functions are GNU extensions of glibc. So this version may have troubles running on platforms not using glibc. - Fix a bug that access freed memory blocks in set_fade_callcack() and check_fade_fin(). valgrind found it out. - Use WM_CLASS to detect client windows instead of WM_STATE. Some client windows (like notification windows) have WM_CLASS but not WM_STATE. - Mark the extents as damaged if shadow state changed in determine_shadow(). - Rewrite wid_get_name(). Code clean-up. - Two debugging options: DEBUG_WINDATA and DEBUG_WINMATCH. - As the matching system is ready, it should be rather easy to add other kinds of blacklists, like fading blacklist. --- compton.c | 527 +++++++++++++++++++++++++++++++++++++++++++++++------- compton.h | 117 ++++++++++-- 2 files changed, 568 insertions(+), 76 deletions(-) diff --git a/compton.c b/compton.c index 984447e75..015f563fd 100644 --- a/compton.c +++ b/compton.c @@ -35,7 +35,7 @@ Bool has_name_pixmap; int root_height, root_width; /* errors */ -ignore *ignore_head, **ignore_tail = &ignore_head; +ignore *ignore_head = NULL, **ignore_tail = &ignore_head; int xfixes_event, xfixes_error; int damage_event, damage_error; int composite_event, composite_error; @@ -71,8 +71,14 @@ Atom extents_atom; Atom opacity_atom; Atom frame_extents_atom; Atom client_atom; +Atom name_atom; +Atom name_ewmh_atom; +Atom class_atom; + Atom win_type_atom; Atom win_type[NUM_WINTYPES]; + +// Window type settings double win_type_opacity[NUM_WINTYPES]; Bool win_type_shadow[NUM_WINTYPES]; Bool win_type_fade[NUM_WINTYPES]; @@ -123,6 +129,13 @@ double mark_wmwin_focused = False; /// Whether compton needs to track focus changes. Bool track_focus = False; +/// Whether compton needs to track window name and class. +Bool track_wdata = False; + +/// Shadow blacklist. A linked list of conditions. +wincond *shadow_blacklist = NULL; +/// Fading blacklist. A linked list of conditions. +wincond *fade_blacklist = NULL; Bool synchronize = False; @@ -206,9 +219,12 @@ run_fade(Display *dpy, win *w, unsigned steps) { static void set_fade_callback(Display *dpy, win *w, void (*callback) (Display *dpy, win *w), Bool exec_callback) { - if (exec_callback && w->fade_callback) - (w->fade_callback)(dpy, w); + void (*old_callback) (Display *dpy, win *w) = w->fade_callback; + w->fade_callback = callback; + // Must be the last line as the callback could destroy w! + if (exec_callback && old_callback) + old_callback(dpy, w); } /** @@ -615,6 +631,231 @@ should_ignore(Display *dpy, unsigned long sequence) { * Windows */ +/** + * Match a window against a single window condition. + * + * @return true if matched, false otherwise. + */ +static bool +win_match_once(win *w, const wincond *cond) { + const char *target; + bool matched = false; + +#ifdef DEBUG_WINMATCH + printf("win_match_once(%#010lx \"%s\"): cond = %p", w->id, w->name, + cond); +#endif + + // Determine the target + target = NULL; + switch (cond->target) { + case CONDTGT_NAME: + target = w->name; + break; + case CONDTGT_CLASSI: + target = w->class_instance; + break; + case CONDTGT_CLASSG: + target = w->class_general; + break; + } + + if (!target) { +#ifdef DEBUG_WINMATCH + printf(": Target not found\n"); +#endif + return false; + } + + // Determine pattern type and match + switch (cond->type) { + case CONDTP_EXACT: + if (cond->flags & CONDF_IGNORECASE) + matched = !strcasecmp(target, cond->pattern); + else + matched = !strcmp(target, cond->pattern); + break; + case CONDTP_ANYWHERE: + if (cond->flags & CONDF_IGNORECASE) + matched = strcasestr(target, cond->pattern); + else + matched = strstr(target, cond->pattern); + break; + case CONDTP_FROMSTART: + if (cond->flags & CONDF_IGNORECASE) + matched = !strncasecmp(target, cond->pattern, + strlen(cond->pattern)); + else + matched = !strncmp(target, cond->pattern, + strlen(cond->pattern)); + break; + case CONDTP_WILDCARD: + { + int flags = 0; + if (cond->flags & CONDF_IGNORECASE) + flags = FNM_CASEFOLD; + matched = !fnmatch(cond->pattern, target, flags); + } + break; + case CONDTP_REGEX_PCRE: +#ifdef CONFIG_REGEX_PCRE + matched = (pcre_exec(cond->regex_pcre, cond->regex_pcre_extra, + target, strlen(target), 0, 0, NULL, 0) >= 0); +#endif + break; + } + +#ifdef DEBUG_WINMATCH + printf(", matched = %d\n", matched); +#endif + + return matched; +} + +/** + * Match a window against a condition linked list. + * + * @param cache a place to cache the last matched condition + * @return true if matched, false otherwise. + */ +static bool +win_match(win *w, wincond *condlst, wincond **cache) { + // Check if the cached entry matches firstly + if (cache && *cache && win_match_once(w, *cache)) + return true; + + // Then go through the whole linked list + for (; condlst; condlst = condlst->next) { + if (win_match_once(w, condlst)) { + *cache = condlst; + return true; + } + } + + return false; +} + +/** + * Add a pattern to a condition linked list. + */ +static Bool +condlst_add(wincond **pcondlst, const char *pattern) { + unsigned plen = strlen(pattern); + wincond *cond; + const char *pos; + + if (plen < 4 || ':' != pattern[1] || !strchr(pattern + 2, ':')) { + printf("Pattern \"%s\": Format invalid.\n", pattern); + return False; + } + + // Allocate memory for the new condition + cond = malloc(sizeof(wincond)); + + // Determine the pattern target + switch (pattern[0]) { + case 'n': + cond->target = CONDTGT_NAME; + break; + case 'i': + cond->target = CONDTGT_CLASSI; + break; + case 'g': + cond->target = CONDTGT_CLASSG; + break; + default: + printf("Pattern \"%s\": Target \"%c\" invalid.\n", + pattern, pattern[0]); + free(cond); + return False; + } + + // Determine the pattern type + switch (pattern[2]) { + case 'e': + cond->type = CONDTP_EXACT; + break; + case 'a': + cond->type = CONDTP_ANYWHERE; + break; + case 's': + cond->type = CONDTP_FROMSTART; + break; + case 'w': + cond->type = CONDTP_WILDCARD; + break; +#ifdef CONFIG_REGEX_PCRE + case 'p': + cond->type = CONDTP_REGEX_PCRE; + break; +#endif + default: + printf("Pattern \"%s\": Type \"%c\" invalid.\n", + pattern, pattern[2]); + free(cond); + return False; + } + + // Determine the pattern flags + pos = &pattern[3]; + cond->flags = 0; + while (':' != *pos) { + switch (*pos) { + case 'i': + cond->flags |= CONDF_IGNORECASE; + break; + default: + printf("Pattern \"%s\": Flag \"%c\" invalid.\n", + pattern, *pos); + break; + } + ++pos; + } + + // Copy the pattern + ++pos; + cond->pattern = NULL; +#ifdef CONFIG_REGEX_PCRE + cond->regex_pcre = NULL; + cond->regex_pcre_extra = NULL; +#endif + if (CONDTP_REGEX_PCRE == cond->type) { +#ifdef CONFIG_REGEX_PCRE + const char *error = NULL; + int erroffset = 0; + int options = 0; + + if (cond->flags & CONDF_IGNORECASE) + options |= PCRE_CASELESS; + + cond->regex_pcre = pcre_compile(pos, options, &error, &erroffset, + NULL); + if (!cond->regex_pcre) { + printf("Pattern \"%s\": PCRE regular expression parsing failed on " + "offset %d: %s\n", pattern, erroffset, error); + free(cond); + return False; + } +#ifdef CONFIG_REGEX_PCRE_JIT + cond->regex_pcre_extra = pcre_study(cond->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error); + if (!cond->regex_pcre_extra) { + printf("Pattern \"%s\": PCRE regular expression study failed: %s", + pattern, error); + } +#endif +#endif + } + else { + cond->pattern = mstrcpy(pos); + } + + // Insert it into the linked list + cond->next = *pcondlst; + *pcondlst = cond; + + return True; +} + static long determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { long evmask = NoEventMask; @@ -625,7 +866,7 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { } if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) { - if (frame_opacity) + if (frame_opacity || track_wdata) evmask |= PropertyChangeMask; } @@ -1106,7 +1347,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); // Painting shadow - if (win_type_shadow[w->window_type]) { + if (w->shadow) { XRenderComposite( dpy, PictOpOver, cshadow_picture, w->shadow_pict, root_buffer, 0, 0, 0, 0, @@ -1363,13 +1604,6 @@ map_win(Display *dpy, Window id, w->a.map_state = IsViewable; w->window_type = determine_wintype(dpy, w->id, w->id); - // Window type change could affect shadow and fade - determine_shadow(dpy, w); - determine_fade(dpy, w); - - // Determine mode here just in case the colormap changes - determine_mode(dpy, w); - #ifdef DEBUG_WINTYPE printf("map_win(): window %#010lx type %s\n", w->id, wintype_name(w->window_type)); @@ -1401,6 +1635,12 @@ map_win(Display *dpy, Window id, get_frame_extents(dpy, w, w->client_win); } + // Get window name and class if we are tracking them + if (track_wdata) { + win_get_name(dpy, w); + win_get_class(dpy, w); + } + /* * Occasionally compton does not seem able to get a FocusIn event from a * window just mapped. I suspect it's a timing issue again when the @@ -1415,6 +1655,13 @@ map_win(Display *dpy, Window id, w->focused = True; } + // Window type change could affect shadow and fade + determine_shadow(dpy, w); + determine_fade(dpy, w); + + // Determine mode here just in case the colormap changes + determine_mode(dpy, w); + // Fading in calc_opacity(dpy, w, True); set_fade_callback(dpy, w, NULL, True); @@ -1620,15 +1867,23 @@ static void determine_shadow(Display *dpy, win *w) { Bool shadow_old = w->shadow; - w->shadow = win_type_shadow[w->window_type]; + w->shadow = (win_type_shadow[w->window_type] + && !win_match(w, shadow_blacklist, &w->cache_sblst)); // Window extents need update on shadow state change if (w->shadow != shadow_old) { // Shadow geometry currently doesn't change on shadow state change // calc_shadow_geometry(dpy, w); if (w->extents) { - free_region(dpy, &w->extents); + // Mark the old extents as damaged if the shadow is removed + if (!w->shadow) + add_damage(dpy, w->extents); + else + free_region(dpy, &w->extents); w->extents = win_extents(dpy, w); + // Mark the new extents as damaged if the shadow is added + if (w->shadow) + add_damage_win(dpy, w); } } } @@ -1666,7 +1921,7 @@ calc_shadow_geometry(Display *dpy, win *w) { static void mark_client_win(Display *dpy, win *w, Window client) { w->client_win = client; - + // Get the frame width and monitor further frame width changes on client // window if necessary if (frame_opacity) { @@ -1721,6 +1976,12 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->damage = XDamageCreate(dpy, id, XDamageReportNonEmpty); } + new->name = NULL; + new->class_instance = NULL; + new->class_general = NULL; + new->cache_sblst = NULL; + new->cache_fblst = NULL; + new->border_size = None; new->extents = None; new->shadow = False; @@ -1815,7 +2076,7 @@ restack_win(Display *dpy, win *w, Window new_above) { if (root == c->id) { window_name = "(Root window)"; } else { - to_free = window_get_name(c->id, &window_name); + to_free = wid_get_name(dpy, c->id, &window_name); } desc = ""; @@ -2131,39 +2392,127 @@ expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) { add_damage(dpy, region); } -#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) -static int -window_get_name(Window w, char **name) { - Atom prop = XInternAtom(dpy, "_NET_WM_NAME", False); - Atom utf8_type = XInternAtom(dpy, "UTF8_STRING", False); - Atom actual_type; - int actual_format; - unsigned long nitems; - unsigned long leftover; - char *data = NULL; - Status ret; +static Bool +wid_get_text_prop(Display *dpy, Window wid, Atom prop, + char ***pstrlst, int *pnstr) { + XTextProperty text_prop; - set_ignore(dpy, NextRequest(dpy)); + if (!(XGetTextProperty(dpy, wid, &text_prop, prop) && text_prop.value)) + return False; - if (Success != (ret = XGetWindowProperty(dpy, w, prop, 0L, (long) BUFSIZ, - False, utf8_type, &actual_type, &actual_format, &nitems, - &leftover, (unsigned char **) &data))) { - if (BadWindow == ret) return 0; + if (Success != + XmbTextPropertyToTextList(dpy, &text_prop, pstrlst, pnstr) + || !*pnstr) { + *pnstr = 0; + if (*pstrlst) + XFreeStringList(*pstrlst); + return False; + } - set_ignore(dpy, NextRequest(dpy)); - printf("Window %#010lx: _NET_WM_NAME unset, falling back to WM_NAME.\n", w); + return True; +} - if (!XFetchName(dpy, w, &data)) { - return 0; +static Bool +wid_get_name(Display *dpy, Window wid, char **name) { + XTextProperty text_prop; + char **strlst = NULL; + int nstr = 0; + + // set_ignore(dpy, NextRequest(dpy)); + if (!(XGetTextProperty(dpy, wid, &text_prop, name_ewmh_atom) + && text_prop.value)) { + // set_ignore(dpy, NextRequest(dpy)); +#ifdef DEBUG_WINDATA + printf("wid_get_name(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); +#endif + + if (!(XGetWMName(dpy, wid, &text_prop) && text_prop.value)) { + return False; } } + if (Success != + XmbTextPropertyToTextList(dpy, &text_prop, &strlst, &nstr) + || !nstr || !strlst) { + if (strlst) + XFreeStringList(strlst); + return False; + } + *name = mstrcpy(strlst[0]); + + XFreeStringList(strlst); + + return True; +} + +static int +win_get_name(Display *dpy, win *w) { + Bool ret; + char *name_old = w->name; + + // Can't do anything if there's no client window + if (!w->client_win) + return False; + + // Get the name + ret = wid_get_name(dpy, w->client_win, &w->name); + + // Return -1 if wid_get_name() failed, 0 if name didn't change, 1 if + // it changes + if (!ret) + ret = -1; + else if (name_old && !strcmp(w->name, name_old)) + ret = 0; + else + ret = 1; + + // Keep the old name if there's no new one + if (w->name != name_old) + free(name_old); + +#ifdef DEBUG_WINDATA + printf("win_get_name(%#010lx): client = %#010lx, name = \"%s\", " + "ret = %d\n", w->id, w->client_win, w->name, ret); +#endif - // if (actual_type == utf8_type && actual_format == 8) - *name = (char *) data; - return 1; + return ret; } + +static Bool +win_get_class(Display *dpy, win *w) { + char **strlst = NULL; + int nstr = 0; + + // Can't do anything if there's no client window + if (!w->client_win) + return False; + + // Free and reset old strings + free(w->class_instance); + free(w->class_general); + w->class_instance = NULL; + w->class_general = NULL; + + // Retrieve the property string list + if (!wid_get_text_prop(dpy, w->client_win, class_atom, &strlst, &nstr)) + return False; + + // Copy the strings if successful + w->class_instance = mstrcpy(strlst[0]); + + if (nstr > 1) + w->class_general = mstrcpy(strlst[1]); + + XFreeStringList(strlst); + +#ifdef DEBUG_WINDATA + printf("win_get_class(%#010lx): client = %#010lx, " + "instance = \"%s\", general = \"%s\"\n", + w->id, w->client_win, w->class_instance, w->class_general); #endif + return True; +} + #ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev) { @@ -2394,6 +2743,7 @@ ev_property_notify(XPropertyEvent *ev) { } } + // If frame extents property changes if (frame_opacity && ev->atom == extents_atom) { win *w = find_toplevel(dpy, ev->window); if (w) { @@ -2402,6 +2752,23 @@ ev_property_notify(XPropertyEvent *ev) { add_damage_win(dpy, w); } } + + // If name changes + if (track_wdata + && (name_atom == ev->atom || name_ewmh_atom == ev->atom)) { + win *w = find_toplevel(dpy, ev->window); + if (w && 1 == win_get_name(dpy, w)) + determine_shadow(dpy, w); + } + + // If class changes + if (track_wdata && class_atom == ev->atom) { + win *w = find_toplevel(dpy, ev->window); + if (w) { + win_get_class(dpy, w); + determine_shadow(dpy, w); + } + } } inline static void @@ -2433,38 +2800,37 @@ ev_shape_notify(XShapeEvent *ev) { inline static void ev_handle(XEvent *ev) { -#ifdef DEBUG_EVENTS - Window w; - char *window_name; - Bool to_free = False; -#endif - if ((ev->type & 0x7f) != KeymapNotify) { discard_ignore(dpy, ev->xany.serial); } #ifdef DEBUG_EVENTS - w = ev_window(ev); - window_name = "(Failed to get title)"; + if (ev->type != damage_event + XDamageNotify) { + Window w; + char *window_name; + Bool to_free = False; - if (w) { - if (root == w) { - window_name = "(Root window)"; - } else { - to_free = (Bool) window_get_name(w, &window_name); + w = ev_window(ev); + window_name = "(Failed to get title)"; + + if (w) { + if (root == w) { + window_name = "(Root window)"; + } else { + to_free = (Bool) wid_get_name(dpy, w, &window_name); + } } - } - if (ev->type != damage_event + XDamageNotify) { print_timestamp(); printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", ev_name(ev), ev_serial(ev), w, window_name); - } - if (to_free) { - XFree(window_name); - window_name = NULL; + if (to_free) { + XFree(window_name); + window_name = NULL; + } } + #endif switch (ev->type) { @@ -2519,10 +2885,11 @@ ev_handle(XEvent *ev) { static void usage(void) { - fprintf(stderr, "compton v0.0.1\n"); + fprintf(stderr, "compton (development version)\n"); fprintf(stderr, "usage: compton [options]\n"); fprintf(stderr, - "Options\n" + "Options:\n" + "\n" "-d display\n" " Which display should be managed.\n" "-r radius\n" @@ -2570,7 +2937,28 @@ usage(void) { "--inactive-opacity-override\n" " Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n" "--inactive-dim value\n" - " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n"); + " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n" + "--mark-wmwin-focused\n" + " Try to detect WM windows and mark them as active.\n" + "--shadow-exclude condition\n" + " Exclude conditions for shadows.\n" + "\n" + "Format of a condition:\n" + "\n" + " condition = :[]:\n" + "\n" + " is one of \"n\" (window name), \"i\" (window class\n" + " instance), and \"g\" (window general class)\n" + "\n" + " is one of \"e\" (exact match), \"a\" (match anywhere),\n" + " \"s\" (match from start), \"w\" (wildcard), and \"p\" (PCRE\n" + " regular expressions, if compiled with the support).\n" + "\n" + " could a serious of flags. Currently the only defined\n" + " flag is \"i\" (ignore case).\n" + "\n" + " is the actual pattern string.\n" + ); exit(1); } @@ -2634,7 +3022,10 @@ get_atoms(void) { extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); - client_atom = XInternAtom(dpy, "WM_STATE", False); + client_atom = XInternAtom(dpy, "WM_CLASS", False); + name_atom = XInternAtom(dpy, "WM_NAME", False); + name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); + class_atom = XInternAtom(dpy, "WM_CLASS", False); win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); @@ -2678,6 +3069,7 @@ main(int argc, char **argv) { { "inactive-opacity-override", no_argument, NULL, 0 }, { "inactive-dim", required_argument, NULL, 0 }, { "mark-wmwin-focused", no_argument, NULL, 0 }, + { "shadow-exclude", required_argument, NULL, 0 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -2703,6 +3095,10 @@ main(int argc, char **argv) { gettimeofday(&time_start, NULL); + // Set locale so window names with special characters are interpreted + // correctly + setlocale (LC_ALL, ""); + for (i = 0; i < NUM_WINTYPES; ++i) { win_type_fade[i] = False; win_type_shadow[i] = False; @@ -2734,6 +3130,9 @@ main(int argc, char **argv) { case 5: mark_wmwin_focused = True; break; + case 6: + condlst_add(&shadow_blacklist, optarg); + break; } break; // Short options @@ -2831,6 +3230,10 @@ main(int argc, char **argv) { track_focus = True; } + // Determine whether we need to track window name and class + if (shadow_blacklist || fade_blacklist) + track_wdata = True; + fade_time = get_time_in_milliseconds(); dpy = XOpenDisplay(display); diff --git a/compton.h b/compton.h index 0249e8470..a46213569 100644 --- a/compton.h +++ b/compton.h @@ -4,6 +4,32 @@ // Throw everything in here. +// === Options === + +#define CAN_DO_USABLE 0 + +// Debug options, enable them using -D in CFLAGS +// #define DEBUG_REPAINT 1 +// #define DEBUG_EVENTS 1 +// #define DEBUG_RESTACK 1 +// #define DEBUG_WINTYPE 1 +// #define DEBUG_CLIENTWIN 1 +// #define DEBUG_WINDATA 1 +// #define DEBUG_WINMATCH 1 +// #define MONITOR_REPAINT 1 + +// Whether to enable PCRE regular expression support in blacklists, enabled +// by default +#define CONFIG_REGEX_PCRE 1 +// Whether to enable JIT support of libpcre. This may cause problems on PaX +// kernels. +#define CONFIG_REGEX_PCRE_JIT 1 + +// === Includes === + +// For some special functions +#define _GNU_SOURCE + #include #include #include @@ -14,6 +40,13 @@ #include #include #include +#include +#include + +#include +#ifdef CONFIG_REGEX_PCRE +#include +#endif #include #include @@ -23,20 +56,11 @@ #include #include +// === Constants === #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 #define HAS_NAME_WINDOW_PIXMAP 1 #endif -#define CAN_DO_USABLE 0 - -// Debug options, enable them using -D in CFLAGS -// #define DEBUG_REPAINT 1 -// #define DEBUG_EVENTS 1 -// #define DEBUG_RESTACK 1 -// #define DEBUG_WINTYPE 1 -// #define DEBUG_CLIENTWIN 1 -// #define MONITOR_REPAINT 1 - // For printing timestamps #include extern struct timeval time_start; @@ -83,6 +107,34 @@ typedef struct _ignore { unsigned long sequence; } ignore; +enum wincond_target { + CONDTGT_NAME, + CONDTGT_CLASSI, + CONDTGT_CLASSG, +}; + +enum wincond_type { + CONDTP_EXACT, + CONDTP_ANYWHERE, + CONDTP_FROMSTART, + CONDTP_WILDCARD, + CONDTP_REGEX_PCRE, +}; + +#define CONDF_IGNORECASE 0x0001 + +typedef struct _wincond { + enum wincond_target target; + enum wincond_type type; + char *pattern; +#ifdef CONFIG_REGEX_PCRE + pcre *regex_pcre; + pcre_extra *regex_pcre_extra; +#endif + int16_t flags; + struct _wincond *next; +} wincond; + typedef struct _win { struct _win *next; Window id; @@ -109,6 +161,13 @@ typedef struct _win { /// Cached width/height of the window including border. int widthb, heightb; + // Blacklist related members + char *name; + char *class_instance; + char *class_general; + wincond *cache_sblst; + wincond *cache_fblst; + // Opacity-related members /// Current window opacity. opacity_t opacity; @@ -206,6 +265,18 @@ set_ignore(Display *dpy, unsigned long sequence); static int should_ignore(Display *dpy, unsigned long sequence); +/** + * Allocate the space and copy a string. + */ +static inline char * +mstrcpy(const char *src) { + char *str = malloc(sizeof(char) * (strlen(src) + 1)); + + strcpy(str, src); + + return str; +} + /** * Normalize an int value to a specific range. * @@ -425,8 +496,9 @@ set_fade_callback(Display *dpy, win *w, static inline void check_fade_fin(Display *dpy, win *w) { if (w->fade_fin) { - set_fade_callback(dpy, w, NULL, True); w->fade_fin = False; + // Must be the last line as the callback could destroy w! + set_fade_callback(dpy, w, NULL, True); } } @@ -458,6 +530,15 @@ static Picture solid_picture(Display *dpy, Bool argb, double a, double r, double g, double b); +static bool +win_match_once(win *w, const wincond *cond); + +static bool +win_match(win *w, wincond *condlst, wincond * *cache); + +static Bool +condlst_add(wincond **pcondlst, const char *pattern); + static long determine_evmask(Display *dpy, Window wid, win_evmode_t mode); @@ -594,10 +675,18 @@ error(Display *dpy, XErrorEvent *ev); static void expose_root(Display *dpy, Window root, XRectangle *rects, int nrects); -#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) +static Bool +wid_get_text_prop(Display *dpy, Window wid, Atom prop, + char ***pstrlst, int *pnstr); + +static Bool +wid_get_name(Display *dpy, Window w, char **name); + static int -window_get_name(Window w, char **name); -#endif +win_get_name(Display *dpy, win *w); + +static Bool +win_get_class(Display *dpy, win *w); #ifdef DEBUG_EVENTS static int From 80a4f6d0ab1a2bce927627ecde37be4e09752e0e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 22 Sep 2012 20:49:17 +0800 Subject: [PATCH 037/178] Improvement: Change window type detection - Let window type detection start with the client window if there's one, in hope to enhance performance. - Change get_wintype_prop() to fetch the property only once. - Default to WINTYPE_UNKNOWN instead of WINTYPE_NORMAL if _NET_WM_WINDOW_TYPE is missing. - Fix a mistake in calc_opacity(). - Add some items to .gitignore. - Fix a typo in usage(). --- compton.c | 91 ++++++++++++++++++++++--------------------------------- compton.h | 2 +- 2 files changed, 38 insertions(+), 55 deletions(-) diff --git a/compton.c b/compton.c index 015f563fd..c6c6279dc 100644 --- a/compton.c +++ b/compton.c @@ -1517,50 +1517,39 @@ wintype_name(wintype type) { #endif static wintype -get_wintype_prop(Display * dpy, Window w) { +get_wintype_prop(Display *dpy, Window wid) { 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)); + unsigned long n = 0, left, i; + long *data = NULL; + int j; - 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; + set_ignore(dpy, NextRequest(dpy)); + if (Success != XGetWindowProperty( + dpy, wid, win_type_atom, 0L, 32L, False, XA_ATOM, + &actual, &format, &n, &left, (unsigned char **) &data) + || !data || !n) { + if (data) + XFree(data); + return WINTYPE_UNKNOWN; + } - for (i = 1; i < NUM_WINTYPES; ++i) { - Atom a; - memcpy(&a, data, sizeof(Atom)); - if (a == win_type[i]) { - /* known type */ - ret = i; - break; - } + for (i = 0; i < n; ++i) { + for (j = 1; j < NUM_WINTYPES; ++j) { + if (win_type[j] == (Atom) data[i]) { + XFree(data); + return j; } - - XFree((void *) data); } + } - ++off; - } while (left >= 4 && ret == WINTYPE_UNKNOWN); + XFree(data); - return ret; + return WINTYPE_UNKNOWN; } static wintype -determine_wintype(Display *dpy, Window w, Window top) { - Window root_return, parent_return; +determine_wintype(Display *dpy, Window w) { Window *children = NULL; unsigned int nchildren, i; wintype type; @@ -1568,16 +1557,11 @@ determine_wintype(Display *dpy, Window w, Window top) { 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); + if (!win_get_children(dpy, w, &children, &nchildren)) return WINTYPE_UNKNOWN; - } for (i = 0; i < nchildren; i++) { - type = determine_wintype(dpy, children[i], top); + type = determine_wintype(dpy, children[i]); if (type != WINTYPE_UNKNOWN) return type; } @@ -1585,11 +1569,7 @@ determine_wintype(Display *dpy, Window w, Window top) { XFree((void *)children); } - if (w != top) { - return WINTYPE_UNKNOWN; - } else { - return WINTYPE_NORMAL; - } + return WINTYPE_UNKNOWN; } static void @@ -1602,12 +1582,6 @@ map_win(Display *dpy, Window id, w->focused = False; w->a.map_state = IsViewable; - w->window_type = determine_wintype(dpy, w->id, w->id); - -#ifdef DEBUG_WINTYPE - printf("map_win(): window %#010lx type %s\n", - w->id, wintype_name(w->window_type)); -#endif // Call XSelectInput() before reading properties so that no property // changes are lost @@ -1635,6 +1609,14 @@ map_win(Display *dpy, Window id, get_frame_extents(dpy, w, w->client_win); } + if (WINTYPE_UNKNOWN == w->window_type) + w->window_type = determine_wintype(dpy, w->id); + +#ifdef DEBUG_WINTYPE + printf("map_win(%#010lx): type %s\n", + w->id, wintype_name(w->window_type)); +#endif + // Get window name and class if we are tracking them if (track_wdata) { win_get_name(dpy, w); @@ -1821,7 +1803,7 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { } if (OPAQUE == (opacity = w->opacity_prop)) { - if (OPAQUE != win_type_opacity[w->window_type]) { + if (1.0 != win_type_opacity[w->window_type]) { opacity = win_type_opacity[w->window_type] * OPAQUE; } } @@ -1928,6 +1910,8 @@ mark_client_win(Display *dpy, win *w, Window client) { get_frame_extents(dpy, w, client); } XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); + if (WINTYPE_UNKNOWN == w->window_type) + w->window_type = get_wintype_prop(dpy, w->client_win); } static void @@ -2026,7 +2010,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { *p = new; if (new->a.map_state == IsViewable) { - new->window_type = determine_wintype(dpy, id, id); map_win(dpy, id, new->damage_sequence - 1, True, override_redirect); } } @@ -2954,7 +2937,7 @@ usage(void) { " \"s\" (match from start), \"w\" (wildcard), and \"p\" (PCRE\n" " regular expressions, if compiled with the support).\n" "\n" - " could a serious of flags. Currently the only defined\n" + " could be a series of flags. Currently the only defined\n" " flag is \"i\" (ignore case).\n" "\n" " is the actual pattern string.\n" diff --git a/compton.h b/compton.h index a46213569..c57dd2fd2 100644 --- a/compton.h +++ b/compton.h @@ -588,7 +588,7 @@ static wintype get_wintype_prop(Display * dpy, Window w); static wintype -determine_wintype(Display *dpy, Window w, Window top); +determine_wintype(Display *dpy, Window w); static void map_win(Display *dpy, Window id, From 9a839bc66114c37e86cd6056997317d552619ad6 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 23 Sep 2012 11:49:02 +0800 Subject: [PATCH 038/178] Misc: Fix two memory leaks - Fix two small memory leaks. valgrind detects another memory leak possibly caused by XGetTextProperty(), probably a bug in libX11, I couldn't fix that one. - Use predefined atoms in Xatom.h to replace a few XInternAtom() calls. --- compton.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/compton.c b/compton.c index c6c6279dc..8945c7749 100644 --- a/compton.c +++ b/compton.c @@ -986,6 +986,7 @@ root_tile_f(Display *dpy) { pixmap = None; for (p = 0; background_props[p]; p++) { + prop = NULL; if (XGetWindowProperty(dpy, root, XInternAtom(dpy, background_props[p], False), 0, 4, False, AnyPropertyType, &actual_type, @@ -997,7 +998,8 @@ root_tile_f(Display *dpy) { XFree(prop); fill = False; break; - } + } else if (prop) + XFree(prop); } if (!pixmap) { @@ -2178,6 +2180,9 @@ finish_destroy_win(Display *dpy, Window id) { free_picture(dpy, &w->frame_alpha_pict); free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); + free(w->name); + free(w->class_instance); + free(w->class_general); free(w); break; @@ -3005,10 +3010,10 @@ get_atoms(void) { extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); - client_atom = XInternAtom(dpy, "WM_CLASS", False); - name_atom = XInternAtom(dpy, "WM_NAME", False); + client_atom = XA_WM_CLASS; + name_atom = XA_WM_NAME; name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); - class_atom = XInternAtom(dpy, "WM_CLASS", False); + class_atom = XA_WM_CLASS; win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); From 0c67b84349c9408b5dc659a5227d599f7a9ed01f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 25 Sep 2012 10:19:20 +0800 Subject: [PATCH 039/178] Feature: Configuration file parsing - Add support for parsing configuration files using libconfig. (Dependency on libconfig could be made optional once we get some better building system.) Few tests has been done, bugs to be expected. compton searches for a configuration file mostly according to the XDG standard. Firstly the configuration file requested by --config, then $XDG_CONFIG_HOME/compton.conf (~/.config/compton.conf, usually), then ~/.compton.conf, then compton.conf under $XDG_DATA_DIRS (often /etc/xdg/compton.conf). A sample configuration file is supplied as compton.sample.conf. Configuration file syntax may change in the future. Commandline switches has higher priority than configuration file, except for --shadow-exclude. Use --config /dev/null to temporarily disable configuration file. - Fix a bug that causes windows to disappear or be partially rendered on opacity changes. - Fix a bug that causes some windows to ignore -i (inactive_opacity) and --inactive-dim, caused by the default window type change in a5d9955ca4. --- compton.c | 522 ++++++++++++++++++++++++++++++++++++++++-------------- compton.h | 71 +++++++- 2 files changed, 460 insertions(+), 133 deletions(-) diff --git a/compton.c b/compton.c index 8945c7749..519c8ca1a 100644 --- a/compton.c +++ b/compton.c @@ -87,10 +87,6 @@ Bool win_type_fade[NUM_WINTYPES]; * Macros */ -#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) @@ -98,6 +94,7 @@ Bool win_type_fade[NUM_WINTYPES]; * Options */ +char *display = NULL; int shadow_radius = 12; int shadow_offset_x = -15; int shadow_offset_y = -15; @@ -137,8 +134,20 @@ wincond *shadow_blacklist = NULL; /// Fading blacklist. A linked list of conditions. wincond *fade_blacklist = NULL; +/// Whether to fork to background. +Bool fork_after_register = False; +/// Red, green and blue tone of the shadow. +double shadow_red = 0.0, shadow_green = 0.0, shadow_blue = 0.0; + Bool synchronize = False; +// Temporary options +Bool shadow_enable = False; +Bool fading_enable = False; +Bool no_dock_shadow = False; +Bool no_dnd_shadow = False; +double menu_opacity = 1.0; + /** * Fades */ @@ -740,6 +749,9 @@ win_match(win *w, wincond *condlst, wincond **cache) { */ static Bool condlst_add(wincond **pcondlst, const char *pattern) { + if (!pattern) + return False; + unsigned plen = strlen(pattern); wincond *cond; const char *pos; @@ -1231,6 +1243,10 @@ paint_preprocess(Display *dpy, win *list) { if (!w->extents) { w->extents = win_extents(dpy, w); + // If w->extents does not exist, the previous add_damage_win() + // call when opacity changes has no effect, so redo it here. + if (w->opacity != opacity_old) + add_damage_win(dpy, w); } // Rebuild alpha_pict only if necessary @@ -1765,8 +1781,6 @@ determine_mode(Display *dpy, win *w) { } w->mode = mode; - - add_damage_win(dpy, w); } /** @@ -1811,7 +1825,7 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { } // Respect inactive_opacity in some cases - if (inactive_opacity && IS_NORMAL_WIN(w) && False == w->focused + if (inactive_opacity && is_normal_win(w) && False == w->focused && (OPAQUE == opacity || inactive_opacity_override)) { opacity = inactive_opacity; } @@ -1823,7 +1837,7 @@ static void calc_dim(Display *dpy, win *w) { Bool dim; - if (inactive_dim && IS_NORMAL_WIN(w) && !(w->focused)) { + if (inactive_dim && is_normal_win(w) && !(w->focused)) { dim = True; } else { dim = False; @@ -2916,6 +2930,8 @@ usage(void) { " Daemonize process.\n" "-S\n" " Enable synchronous operation (for debugging).\n" + "--config path\n" + " Look for configuration file at the path.\n" "--shadow-red value\n" " Red color value of shadow (0.0 - 1.0, defaults to 0).\n" "--shadow-green value\n" @@ -3005,87 +3021,229 @@ fork_after(void) { freopen("/dev/null", "w", stderr); } +#ifdef CONFIG_LIBCONFIG +/** + * Get a file stream of the configuration file to read. + * + * Follows the XDG specification to search for the configuration file. + */ +static FILE * +open_config_file(char *cpath, char **ppath) { + const static char *config_filename = "/compton.conf"; + const static char *config_filename_legacy = "/.compton.conf"; + const static char *config_home_suffix = "/.config"; + const static char *config_system_dir = "/etc/xdg"; + + char *dir = NULL, *home = NULL; + char *path = cpath; + FILE *f = NULL; + + if (path) { + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + else + free(path); + return f; + } + + // Check user configuration file in $XDG_CONFIG_HOME firstly + if (!((dir = getenv("XDG_CONFIG_HOME")) && strlen(dir))) { + if (!((home = getenv("HOME")) && strlen(home))) + return NULL; + + path = mstrjoin3(home, config_home_suffix, config_filename); + } + else + path = mstrjoin(dir, config_filename); + + f = fopen(path, "r"); + + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + + // Then check user configuration file in $HOME + if ((home = getenv("HOME")) && strlen(home)) { + path = mstrjoin(home, config_filename_legacy); + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + } + + // Check system configuration file in $XDG_CONFIG_DIRS at last + if ((dir = getenv("XDG_CONFIG_DIRS")) && strlen(dir)) { + char *part = strtok(dir, ":"); + while (part) { + path = mstrjoin(part, config_filename); + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + part = strtok(NULL, ":"); + } + } + else { + path = mstrjoin(config_system_dir, config_filename); + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + } + + return NULL; +} + +/** + * Parse a configuration file from default location. + */ static void -get_atoms(void) { - extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); - opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); - frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); - client_atom = XA_WM_CLASS; - name_atom = XA_WM_NAME; - name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); - class_atom = XA_WM_CLASS; +parse_config(char *cpath) { + char *path = NULL, *parent = NULL; + FILE *f; + config_t cfg; + int ival = 0; + double dval = 0.0; + + f = open_config_file(cpath, &path); + if (!f) { + if (cpath) + printf("Failed to read the specified configuration file.\n"); + return; + } - 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); + config_init(&cfg); + parent = dirname(path); + if (parent) + config_set_include_dir(&cfg, parent); + + if (CONFIG_FALSE == config_read(&cfg, f)) { + printf("Error when reading configuration file \"%s\", line %d: %s\n", + path, config_error_line(&cfg), config_error_text(&cfg)); + config_destroy(&cfg); + free(path); + return; + } + config_set_auto_convert(&cfg, 1); + + free(path); + + // Get options from the configuration file. We don't do range checking + // right now. It will be done later + + // -D (fade_delta) + if (config_lookup_int(&cfg, "fade-delta", &ival)) + fade_delta = ival; + // -I (fade_in_step) + if (config_lookup_float(&cfg, "fade-in-step", &dval)) + fade_in_step = normalize_d(dval) * OPAQUE; + // -O (fade_out_step) + if (config_lookup_float(&cfg, "fade-out-step", &dval)) + fade_out_step = normalize_d(dval) * OPAQUE; + // -r (shadow_radius) + config_lookup_int(&cfg, "shadow-radius", &shadow_radius); + // -o (shadow_opacity) + config_lookup_float(&cfg, "shadow-opacity", &shadow_opacity); + // -l (shadow_offset_x) + config_lookup_int(&cfg, "shadow-offset-x", &shadow_offset_x); + // -t (shadow_offset_y) + config_lookup_int(&cfg, "shadow-offset-y", &shadow_offset_y); + // -i (inactive_opacity) + if (config_lookup_float(&cfg, "inactive-opacity", &dval)) + inactive_opacity = normalize_d(dval) * OPAQUE; + // -e (frame_opacity) + config_lookup_float(&cfg, "frame-opacity", &frame_opacity); + // -z (clear_shadow) + if (config_lookup_bool(&cfg, "clear-shadow", &ival)) + clear_shadow = ival; + // -c (shadow_enable) + if (config_lookup_bool(&cfg, "shadow", &ival)) + shadow_enable = ival; + // -C (no_dock_shadow) + if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) + no_dock_shadow = ival; + // -G (no_dnd_shadow) + if (config_lookup_bool(&cfg, "no-dnd-shadow", &ival)) + no_dnd_shadow = ival; + // -m (menu_opacity) + config_lookup_float(&cfg, "menu-opacity", &menu_opacity); + // -f (fading_enable) + if (config_lookup_bool(&cfg, "fading", &ival)) + fading_enable = ival; + // --shadow-red + config_lookup_float(&cfg, "shadow-red", &shadow_red); + // --shadow-green + config_lookup_float(&cfg, "shadow-green", &shadow_green); + // --shadow-blue + config_lookup_float(&cfg, "shadow-blue", &shadow_blue); + // --inactive-opacity-override + if (config_lookup_bool(&cfg, "inactive-opacity-override", &ival)) + inactive_opacity_override = ival; + // --inactive-dim + config_lookup_float(&cfg, "inactive-dim", &inactive_dim); + // --mark-wmwin-focused + if (config_lookup_bool(&cfg, "mark-wmwin-focused", &ival)) + mark_wmwin_focused = ival; + // --shadow-exclude + { + config_setting_t *shadow_blacklist_setting = + config_lookup(&cfg, "shadow-exclude"); + if (shadow_blacklist_setting) { + // Parse an array of shadow-exclude + if (config_setting_is_array(shadow_blacklist_setting)) { + int i = config_setting_length(shadow_blacklist_setting); + while (i--) { + condlst_add(&shadow_blacklist, + config_setting_get_string_elem(shadow_blacklist_setting, i)); + } + } + // Treat it as a single pattern if it's a string + else if (CONFIG_TYPE_STRING == + config_setting_type(shadow_blacklist_setting)) { + condlst_add(&shadow_blacklist, + config_setting_get_string(shadow_blacklist_setting)); + } + } + } + + config_destroy(&cfg); } +#endif -int -main(int argc, char **argv) { - const static struct option longopt[] = { - { "shadow-red", required_argument, NULL, 0 }, - { "shadow-green", required_argument, NULL, 0 }, - { "shadow-blue", required_argument, NULL, 0 }, - { "inactive-opacity-override", no_argument, NULL, 0 }, - { "inactive-dim", required_argument, NULL, 0 }, - { "mark-wmwin-focused", no_argument, NULL, 0 }, - { "shadow-exclude", required_argument, NULL, 0 }, +/** + * Process arguments and configuration files. + */ +static void +get_cfg(int argc, char *const *argv) { + const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb"; + const static struct option longopts[] = { + { "config", required_argument, NULL, 256 }, + { "shadow-red", required_argument, NULL, 257 }, + { "shadow-green", required_argument, NULL, 258 }, + { "shadow-blue", required_argument, NULL, 259 }, + { "inactive-opacity-override", no_argument, NULL, 260 }, + { "inactive-dim", required_argument, NULL, 261 }, + { "mark-wmwin-focused", no_argument, NULL, 262 }, + { "shadow-exclude", required_argument, NULL, 263 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; - 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; - int longopt_idx; - Bool no_dock_shadow = False; - Bool no_dnd_shadow = False; - Bool fork_after_register = False; - double shadow_red = 0.0; - double shadow_green = 0.0; - double shadow_blue = 0.0; - win *t; - - gettimeofday(&time_start, NULL); - - // Set locale so window names with special characters are interpreted - // correctly - setlocale (LC_ALL, ""); + int o, longopt_idx, i; + char *config_file = NULL; for (i = 0; i < NUM_WINTYPES; ++i) { win_type_fade[i] = False; @@ -3093,45 +3251,31 @@ main(int argc, char **argv) { win_type_opacity[i] = 1.0; } - while ((o = getopt_long(argc, argv, - "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb", - longopt, &longopt_idx)) != -1) { + // Pre-parse the commandline arguments to check for --config and invalid + // switches + while (-1 != + (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { + if (256 == o) + config_file = mstrcpy(optarg); + else if ('?' == o || ':' == o) + usage(); + } + +#ifdef CONFIG_LIBCONFIG + parse_config(config_file); +#endif + + // Parse commandline arguments. Range checking will be done later. + optind = 1; + while (-1 != + (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { switch (o) { - // Long options - case 0: - switch (longopt_idx) { - case 0: - shadow_red = normalize_d(atof(optarg)); - break; - case 1: - shadow_green = normalize_d(atof(optarg)); - break; - case 2: - shadow_blue = normalize_d(atof(optarg)); - break; - case 3: - inactive_opacity_override = True; - break; - case 4: - inactive_dim = normalize_d(atof(optarg)); - break; - case 5: - mark_wmwin_focused = True; - break; - case 6: - condlst_add(&shadow_blacklist, optarg); - break; - } - break; // Short options case 'd': display = optarg; break; case 'D': fade_delta = atoi(optarg); - if (fade_delta < 1) { - fade_delta = 10; - } break; case 'I': fade_in_step = normalize_d(atof(optarg)) * OPAQUE; @@ -3140,22 +3284,16 @@ main(int argc, char **argv) { fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': - for (i = 0; i < NUM_WINTYPES; ++i) { - win_type_shadow[i] = True; - } - win_type_shadow[WINTYPE_DESKTOP] = False; + shadow_enable = True; 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); + menu_opacity = atof(optarg); break; case 'f': - for (i = 0; i < NUM_WINTYPES; ++i) { - win_type_fade[i] = True; - } + fading_enable = True; break; case 'F': fade_trans = True; @@ -3167,7 +3305,7 @@ main(int argc, char **argv) { shadow_radius = atoi(optarg); break; case 'o': - shadow_opacity = normalize_d(atof(optarg)); + shadow_opacity = atof(optarg); break; case 'l': shadow_offset_x = atoi(optarg); @@ -3177,12 +3315,9 @@ main(int argc, char **argv) { break; case 'i': inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); - if (OPAQUE == inactive_opacity) { - inactive_opacity = 0; - } break; case 'e': - frame_opacity = normalize_d(atof(optarg)); + frame_opacity = atof(optarg); break; case 'z': clear_shadow = True; @@ -3199,20 +3334,79 @@ main(int argc, char **argv) { case 'b': fork_after_register = True; break; + // Long options + case 256: + // --config + break; + case 257: + // --shadow-red + shadow_red = atof(optarg); + break; + case 258: + // --shadow-green + shadow_green = atof(optarg); + break; + case 259: + // --shadow-blue + shadow_blue = atof(optarg); + break; + case 260: + // --inactive-opacity-override + inactive_opacity_override = True; + break; + case 261: + // --inactive-dim + inactive_dim = atof(optarg); + break; + case 262: + // --mark-wmwin-focused + mark_wmwin_focused = True; + break; + case 263: + // --shadow-exclude + condlst_add(&shadow_blacklist, optarg); + break; default: usage(); break; } } - if (no_dock_shadow) { - win_type_shadow[WINTYPE_DOCK] = False; + // Range checking and option assignments + fade_delta = max_i(fade_delta, 1); + shadow_radius = max_i(shadow_radius, 1); + shadow_red = normalize_d(shadow_red); + shadow_green = normalize_d(shadow_green); + shadow_blue = normalize_d(shadow_blue); + inactive_dim = normalize_d(inactive_dim); + frame_opacity = normalize_d(frame_opacity); + shadow_opacity = normalize_d(shadow_opacity); + menu_opacity = normalize_d(menu_opacity); + if (OPAQUE == inactive_opacity) { + inactive_opacity = 0; + } + if (shadow_enable) { + for (i = 0; i < NUM_WINTYPES; ++i) { + win_type_shadow[i] = True; + } + win_type_shadow[WINTYPE_DESKTOP] = False; + if (no_dock_shadow) + win_type_shadow[WINTYPE_DOCK] = False; + if (no_dnd_shadow) + win_type_shadow[WINTYPE_DND] = False; + } + if (fading_enable) { + for (i = 0; i < NUM_WINTYPES; ++i) { + win_type_fade[i] = True; + } } - - if (no_dnd_shadow) { - win_type_shadow[WINTYPE_DND] = False; + if (1.0 != menu_opacity) { + win_type_opacity[WINTYPE_DROPDOWN_MENU] = menu_opacity; + win_type_opacity[WINTYPE_POPUP_MENU] = menu_opacity; } + // Other variables determined by options + // Determine whether we need to track focus changes if (inactive_opacity || inactive_dim) { track_focus = True; @@ -3221,6 +3415,70 @@ main(int argc, char **argv) { // Determine whether we need to track window name and class if (shadow_blacklist || fade_blacklist) track_wdata = True; +} + +static void +get_atoms(void) { + extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); + opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); + frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); + client_atom = XA_WM_CLASS; + name_atom = XA_WM_NAME; + name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); + class_atom = XA_WM_CLASS; + + 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; + win *t; + + gettimeofday(&time_start, NULL); + + // Set locale so window names with special characters are interpreted + // correctly + setlocale (LC_ALL, ""); + + get_cfg(argc, argv); fade_time = get_time_in_milliseconds(); diff --git a/compton.h b/compton.h index c57dd2fd2..88ecbf9fc 100644 --- a/compton.h +++ b/compton.h @@ -24,6 +24,8 @@ // Whether to enable JIT support of libpcre. This may cause problems on PaX // kernels. #define CONFIG_REGEX_PCRE_JIT 1 +// Whether to enable parsing of configuration files using libconfig +#define CONFIG_LIBCONFIG 1 // === Includes === @@ -44,10 +46,16 @@ #include #include + #ifdef CONFIG_REGEX_PCRE #include #endif +#ifdef CONFIG_LIBCONFIG +#include +#include +#endif + #include #include #include @@ -277,6 +285,34 @@ mstrcpy(const char *src) { return str; } +/** + * Allocate the space and join two strings. + */ +static inline char * +mstrjoin(const char *src1, const char *src2) { + char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); + + strcpy(str, src1); + strcat(str, src2); + + return str; +} + +/** + * Allocate the space and join two strings; + */ +static inline char * +mstrjoin3(const char *src1, const char *src2, const char *src3) { + char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + + strlen(src3) + 1)); + + strcpy(str, src1); + strcat(str, src2); + strcat(str, src3); + + return str; +} + /** * Normalize an int value to a specific range. * @@ -285,13 +321,29 @@ mstrcpy(const char *src) { * @param max maximum value * @return normalized value */ -static inline double +static inline int normalize_i_range(int i, int min, int max) { if (i > max) return max; if (i < min) return min; return i; } +/** + * Select the larger integer of two. + */ +static inline int +max_i(int a, int b) { + return (a > b ? a : b); +} + +/** + * Select the smaller integer of two. + */ +static inline int +min_i(int a, int b) { + return (a > b ? b : a); +} + /** * Normalize a double value to a specific range. * @@ -530,6 +582,12 @@ static Picture solid_picture(Display *dpy, Bool argb, double a, double r, double g, double b); +static inline bool is_normal_win(const win *w) { + return (WINTYPE_NORMAL == w->window_type + || WINTYPE_UTILITY == w->window_type + || WINTYPE_UNKNOWN == w->window_type); +} + static bool win_match_once(win *w, const wincond *cond); @@ -789,5 +847,16 @@ ev_handle(XEvent *ev); static void fork_after(void); +#ifdef CONFIG_LIBCONFIG +static FILE * +open_config_file(char *cpath, char **path); + +static void +parse_config(char *cpath); +#endif + +static void +get_cfg(int argc, char *const *argv); + static void get_atoms(void); From 760188dbefc5ab686af7df091f43852ddccf2ea4 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 25 Sep 2012 21:04:10 +0800 Subject: [PATCH 040/178] Feature: Wintype-specific options in configuration files - Add support of window-type-specific options (fade, shadow, and opacity) in configuration file parsing. Syntax shown in compton.sample.conf. - Replace wintype_name() with an array of window type names. Code clean-up. --- compton.c | 155 +++++++++++++++++++++++------------------------------- compton.h | 12 +++++ 2 files changed, 79 insertions(+), 88 deletions(-) diff --git a/compton.c b/compton.c index 519c8ca1a..9ee131a22 100644 --- a/compton.c +++ b/compton.c @@ -14,6 +14,24 @@ * Shared */ +const char *WINTYPES[NUM_WINTYPES] = { + "unknown", + "desktop", + "dock", + "toolbar", + "menu", + "utility", + "splash", + "dialog", + "normal", + "dropdown_menu", + "popup_menu", + "tooltip", + "notify", + "combo", + "dnd", +}; + struct timeval time_start = { 0, 0 }; win *list; @@ -142,8 +160,8 @@ double shadow_red = 0.0, shadow_green = 0.0, shadow_blue = 0.0; Bool synchronize = False; // Temporary options -Bool shadow_enable = False; -Bool fading_enable = False; +int shadow_enable = 0; +int fading_enable = 0; Bool no_dock_shadow = False; Bool no_dnd_shadow = False; double menu_opacity = 1.0; @@ -1477,63 +1495,6 @@ repair_win(Display *dpy, win *w) { w->damaged = 1; } -#ifdef 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 wid) { Atom actual; @@ -1632,7 +1593,7 @@ map_win(Display *dpy, Window id, #ifdef DEBUG_WINTYPE printf("map_win(%#010lx): type %s\n", - w->id, wintype_name(w->window_type)); + w->id, WINTYPES[w->window_type]); #endif // Get window name and class if we are tracking them @@ -3170,8 +3131,10 @@ parse_config(char *cpath) { if (config_lookup_bool(&cfg, "clear-shadow", &ival)) clear_shadow = ival; // -c (shadow_enable) - if (config_lookup_bool(&cfg, "shadow", &ival)) - shadow_enable = ival; + if (config_lookup_bool(&cfg, "shadow", &ival) && ival) { + shadow_enable = 2; + wintype_arr_enable(win_type_shadow); + } // -C (no_dock_shadow) if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) no_dock_shadow = ival; @@ -3181,8 +3144,10 @@ parse_config(char *cpath) { // -m (menu_opacity) config_lookup_float(&cfg, "menu-opacity", &menu_opacity); // -f (fading_enable) - if (config_lookup_bool(&cfg, "fading", &ival)) - fading_enable = ival; + if (config_lookup_bool(&cfg, "fading", &ival) && ival) { + fading_enable = 2; + wintype_arr_enable(win_type_fade); + } // --shadow-red config_lookup_float(&cfg, "shadow-red", &shadow_red); // --shadow-green @@ -3199,22 +3164,39 @@ parse_config(char *cpath) { mark_wmwin_focused = ival; // --shadow-exclude { - config_setting_t *shadow_blacklist_setting = + config_setting_t *setting = config_lookup(&cfg, "shadow-exclude"); - if (shadow_blacklist_setting) { + if (setting) { // Parse an array of shadow-exclude - if (config_setting_is_array(shadow_blacklist_setting)) { - int i = config_setting_length(shadow_blacklist_setting); + if (config_setting_is_array(setting)) { + int i = config_setting_length(setting); while (i--) { condlst_add(&shadow_blacklist, - config_setting_get_string_elem(shadow_blacklist_setting, i)); + config_setting_get_string_elem(setting, i)); } } // Treat it as a single pattern if it's a string - else if (CONFIG_TYPE_STRING == - config_setting_type(shadow_blacklist_setting)) { + else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { condlst_add(&shadow_blacklist, - config_setting_get_string(shadow_blacklist_setting)); + config_setting_get_string(setting)); + } + } + } + // Wintype settings + { + wintype i; + + for (i = 0; i < NUM_WINTYPES; ++i) { + char *str = mstrjoin("wintypes.", WINTYPES[i]); + config_setting_t *setting = config_lookup(&cfg, str); + free(str); + if (setting) { + if (config_setting_lookup_bool(setting, "shadow", &ival)) + win_type_shadow[i] = (Bool) ival; + if (config_setting_lookup_bool(setting, "fade", &ival)) + win_type_fade[i] = (Bool) ival; + config_setting_lookup_float(setting, "opacity", + &win_type_opacity[i]); } } } @@ -3284,7 +3266,8 @@ get_cfg(int argc, char *const *argv) { fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': - shadow_enable = True; + if (2 != shadow_enable) + shadow_enable = 1; break; case 'C': no_dock_shadow = True; @@ -3293,7 +3276,8 @@ get_cfg(int argc, char *const *argv) { menu_opacity = atof(optarg); break; case 'f': - fading_enable = True; + if (2 != fading_enable) + fading_enable = 1; break; case 'F': fade_trans = True; @@ -3385,20 +3369,15 @@ get_cfg(int argc, char *const *argv) { if (OPAQUE == inactive_opacity) { inactive_opacity = 0; } - if (shadow_enable) { - for (i = 0; i < NUM_WINTYPES; ++i) { - win_type_shadow[i] = True; - } - win_type_shadow[WINTYPE_DESKTOP] = False; - if (no_dock_shadow) - win_type_shadow[WINTYPE_DOCK] = False; - if (no_dnd_shadow) - win_type_shadow[WINTYPE_DND] = False; - } - if (fading_enable) { - for (i = 0; i < NUM_WINTYPES; ++i) { - win_type_fade[i] = True; - } + if (1 == shadow_enable) + wintype_arr_enable(win_type_shadow); + win_type_shadow[WINTYPE_DESKTOP] = False; + if (no_dock_shadow) + win_type_shadow[WINTYPE_DOCK] = False; + if (no_dnd_shadow) + win_type_shadow[WINTYPE_DND] = False; + if (1 == fading_enable) { + wintype_arr_enable(win_type_fade); } if (1.0 != menu_opacity) { win_type_opacity[WINTYPE_DROPDOWN_MENU] = menu_opacity; diff --git a/compton.h b/compton.h index 88ecbf9fc..c20f1e9b4 100644 --- a/compton.h +++ b/compton.h @@ -273,6 +273,18 @@ set_ignore(Display *dpy, unsigned long sequence); static int should_ignore(Display *dpy, unsigned long sequence); +/** + * Set a Bool array of all wintypes to true. + */ +static void +wintype_arr_enable(Bool arr[]) { + wintype i; + + for (i = 0; i < NUM_WINTYPES; ++i) { + arr[i] = True; + } +} + /** * Allocate the space and copy a string. */ From a0439e57dd9d1e6fb8fa1174a5ed19da6377638f Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 25 Sep 2012 20:53:18 -0500 Subject: [PATCH 041/178] add global options struct. --- compton.c | 369 +++++++++++++++++++++++++----------------------------- compton.h | 58 +++++++++ 2 files changed, 232 insertions(+), 195 deletions(-) diff --git a/compton.c b/compton.c index 9ee131a22..fad26365e 100644 --- a/compton.c +++ b/compton.c @@ -112,59 +112,38 @@ Bool win_type_fade[NUM_WINTYPES]; * Options */ -char *display = NULL; -int shadow_radius = 12; -int shadow_offset_x = -15; -int shadow_offset_y = -15; -double shadow_opacity = .75; - -/// How much to fade in in a single fading step. -opacity_t fade_in_step = 0.028 * OPAQUE; -/// How much to fade out in a single fading step. -opacity_t fade_out_step = 0.03 * OPAQUE; -unsigned long fade_delta = 10; -unsigned long fade_time = 0; -Bool fade_trans = False; - -Bool clear_shadow = False; - -/// Default opacity for inactive windows. -/// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for -/// not enabled, default. -opacity_t inactive_opacity = 0; -/// Whether inactive_opacity overrides the opacity set by window -/// attributes. -Bool inactive_opacity_override = False; -double frame_opacity = 0.0; -/// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. -double inactive_dim = 0.0; - -/// Whether to try to detect WM windows and mark them as focused. -double mark_wmwin_focused = False; - -/// Whether compton needs to track focus changes. -Bool track_focus = False; -/// Whether compton needs to track window name and class. -Bool track_wdata = False; - -/// Shadow blacklist. A linked list of conditions. -wincond *shadow_blacklist = NULL; -/// Fading blacklist. A linked list of conditions. -wincond *fade_blacklist = NULL; - -/// Whether to fork to background. -Bool fork_after_register = False; -/// Red, green and blue tone of the shadow. -double shadow_red = 0.0, shadow_green = 0.0, shadow_blue = 0.0; - -Bool synchronize = False; - -// Temporary options -int shadow_enable = 0; -int fading_enable = 0; -Bool no_dock_shadow = False; -Bool no_dnd_shadow = False; -double menu_opacity = 1.0; +static options_t options = { + .display = NULL, + .shadow_radius = 12, + .shadow_offset_x = -15, + .shadow_offset_y = -15, + .shadow_opacity = .75, + .fade_in_step = 0.028 * OPAQUE, + .fade_out_step = 0.03 * OPAQUE, + .fade_delta = 10, + .fade_time = 0, + .fade_trans = False, + .clear_shadow = False, + .inactive_opacity = 0, + .inactive_opacity_override = False, + .frame_opacity = 0.0, + .inactive_dim = 0.0, + .mark_wmwin_focused = False, + .track_focus = False, + .track_wdata = False, + .shadow_blacklist = NULL, + .fade_blacklist = NULL, + .fork_after_register = False, + .shadow_red = 0.0, + .shadow_green = 0.0, + .shadow_blue = 0.0, + .synchronize = False, + .shadow_enable = 0, + .fading_enable = 0, + .no_dock_shadow = False, + .no_dnd_shadow = False, + .menu_opacity = 1.0 +}; /** * Fades @@ -192,7 +171,7 @@ get_time_in_milliseconds() { */ static int fade_timeout(void) { - int diff = fade_delta - get_time_in_milliseconds() + fade_time; + int diff = options.fade_delta - get_time_in_milliseconds() + options.fade_time; if (diff < 0) diff = 0; @@ -221,11 +200,11 @@ run_fade(Display *dpy, win *w, unsigned steps) { // calculations if (w->opacity < w->opacity_tgt) w->opacity = normalize_d_range( - (double) w->opacity + (double) fade_in_step * steps, + (double) w->opacity + (double) options.fade_in_step * steps, 0.0, w->opacity_tgt); else w->opacity = normalize_d_range( - (double) w->opacity - (double) fade_out_step * steps, + (double) w->opacity - (double) options.fade_out_step * steps, w->opacity_tgt, OPAQUE); } @@ -441,12 +420,12 @@ make_shadow(Display *dpy, double opacity, * center (fill the complete data array) */ - // If clear_shadow is enabled and the border & corner shadow (which + // If options.clear_shadow is enabled and the border & corner shadow (which // later will be filled) could entirely cover the area of the shadow // that will be displayed, do not bother filling other pixels. If it // can't, we must fill the other pixels here. - if (!(clear_shadow && shadow_offset_x <= 0 && shadow_offset_x >= -cgsize - && shadow_offset_y <= 0 && shadow_offset_y >= -cgsize)) { + if (!(options.clear_shadow && options.shadow_offset_x <= 0 && options.shadow_offset_x >= -cgsize + && options.shadow_offset_y <= 0 && options.shadow_offset_y >= -cgsize)) { if (cgsize > 0) { d = shadow_top[opacity_int * (cgsize + 1) + cgsize]; } else { @@ -517,14 +496,14 @@ make_shadow(Display *dpy, double opacity, } } - if (clear_shadow) { + if (options.clear_shadow) { // Clear the region in the shadow that the window would cover based // on shadow_offset_{x,y} user provides - int xstart = normalize_i_range(- (int) shadow_offset_x, 0, swidth); - int xrange = normalize_i_range(width - (int) shadow_offset_x, + int xstart = normalize_i_range(- (int) options.shadow_offset_x, 0, swidth); + int xrange = normalize_i_range(width - (int) options.shadow_offset_x, 0, swidth) - xstart; - int ystart = normalize_i_range(- (int) shadow_offset_y, 0, sheight); - int yend = normalize_i_range(height - (int) shadow_offset_y, + int ystart = normalize_i_range(- (int) options.shadow_offset_y, 0, sheight); + int yend = normalize_i_range(height - (int) options.shadow_offset_y, 0, sheight); int y; @@ -892,11 +871,11 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { if (WIN_EVMODE_FRAME == mode || find_win(dpy, wid)) { evmask |= PropertyChangeMask; - if (track_focus) evmask |= FocusChangeMask; + if (options.track_focus) evmask |= FocusChangeMask; } if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) { - if (frame_opacity || track_wdata) + if (options.frame_opacity || options.track_wdata) evmask |= PropertyChangeMask; } @@ -1198,10 +1177,10 @@ paint_preprocess(Display *dpy, win *list) { // Sounds like the timeout in poll() frequently does not work // accurately, asking it to wait to 20ms, and often it would wait for // 19ms, so the step value has to be rounded. - unsigned steps = roundl((double) (get_time_in_milliseconds() - fade_time) / fade_delta); + unsigned steps = roundl((double) (get_time_in_milliseconds() - options.fade_time) / options.fade_delta); - // Reset fade_time - fade_time = get_time_in_milliseconds(); + // Reset options.fade_time + options.fade_time = get_time_in_milliseconds(); for (w = list; w; w = next) { // In case calling the fade callback function destroys this window @@ -1276,9 +1255,9 @@ paint_preprocess(Display *dpy, win *list) { w->opacity_cur = w->opacity; } - // Calculate frame_opacity - if (frame_opacity && 1.0 != frame_opacity && w->top_width) - w->frame_opacity = get_opacity_percent(dpy, w) * frame_opacity; + // Calculate options.frame_opacity + if (options.frame_opacity && 1.0 != options.frame_opacity && w->top_width) + w->frame_opacity = get_opacity_percent(dpy, w) * options.frame_opacity; else w->frame_opacity = 0.0; @@ -1294,9 +1273,9 @@ paint_preprocess(Display *dpy, win *list) { // Calculate shadow opacity if (w->frame_opacity) - w->shadow_opacity = shadow_opacity * w->frame_opacity; + w->shadow_opacity = options.shadow_opacity * w->frame_opacity; else - w->shadow_opacity = shadow_opacity * get_opacity_percent(dpy, w); + w->shadow_opacity = options.shadow_opacity * get_opacity_percent(dpy, w); // Rebuild shadow_pict if necessary if (w->flags & WFLAG_SIZE_CHANGE) @@ -1582,7 +1561,7 @@ map_win(Display *dpy, Window id, mark_client_win(dpy, w, cw); } } - else if (frame_opacity) { + else if (options.frame_opacity) { // Refetch frame extents just in case it changes when the window is // unmapped get_frame_extents(dpy, w, w->client_win); @@ -1597,7 +1576,7 @@ map_win(Display *dpy, Window id, #endif // Get window name and class if we are tracking them - if (track_wdata) { + if (options.track_wdata) { win_get_name(dpy, w); win_get_class(dpy, w); } @@ -1608,11 +1587,11 @@ map_win(Display *dpy, Window id, * XSelectInput() is called too late. We have to recheck the focused * window here. */ - if (track_focus) { + if (options.track_focus) { recheck_focus(dpy); // Consider a window without client window a WM window and mark it - // focused if mark_wmwin_focused is on - if (mark_wmwin_focused && !w->client_win) + // focused if options.mark_wmwin_focused is on + if (options.mark_wmwin_focused && !w->client_win) w->focused = True; } @@ -1747,17 +1726,17 @@ determine_mode(Display *dpy, win *w) { /** * Calculate and set the opacity of a window. * - * If window is inactive and inactive_opacity_override is set, the + * If window is inactive and options.inactive_opacity_override is set, the * priority is: (Simulates the old behavior) * - * inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) + * options.inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) * > window type default opacity * * Otherwise: * * _NET_WM_WINDOW_OPACITY (if not opaque) * > window type default opacity (if not opaque) - * > inactive_opacity + * > options.inactive_opacity * * @param dpy X display to use * @param w struct _win object representing the window @@ -1785,10 +1764,10 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { } } - // Respect inactive_opacity in some cases - if (inactive_opacity && is_normal_win(w) && False == w->focused - && (OPAQUE == opacity || inactive_opacity_override)) { - opacity = inactive_opacity; + // Respect options.inactive_opacity in some cases + if (options.inactive_opacity && is_normal_win(w) && False == w->focused + && (OPAQUE == opacity || options.inactive_opacity_override)) { + opacity = options.inactive_opacity; } w->opacity_tgt = opacity; @@ -1798,7 +1777,7 @@ static void calc_dim(Display *dpy, win *w) { Bool dim; - if (inactive_dim && is_normal_win(w) && !(w->focused)) { + if (options.inactive_dim && is_normal_win(w) && !(w->focused)) { dim = True; } else { dim = False; @@ -1827,7 +1806,7 @@ determine_shadow(Display *dpy, win *w) { Bool shadow_old = w->shadow; w->shadow = (win_type_shadow[w->window_type] - && !win_match(w, shadow_blacklist, &w->cache_sblst)); + && !win_match(w, options.shadow_blacklist, &w->cache_sblst)); // Window extents need update on shadow state change if (w->shadow != shadow_old) { @@ -1864,8 +1843,8 @@ calc_win_size(Display *dpy, win *w) { */ static void calc_shadow_geometry(Display *dpy, win *w) { - w->shadow_dx = shadow_offset_x; - w->shadow_dy = shadow_offset_y; + w->shadow_dx = options.shadow_offset_x; + w->shadow_dy = options.shadow_offset_y; w->shadow_width = w->widthb + gaussian_map->size; w->shadow_height = w->heightb + gaussian_map->size; } @@ -1883,7 +1862,7 @@ mark_client_win(Display *dpy, win *w, Window client) { // Get the frame width and monitor further frame width changes on client // window if necessary - if (frame_opacity) { + if (options.frame_opacity) { get_frame_extents(dpy, w, client); } XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); @@ -2222,7 +2201,7 @@ damage_win(Display *dpy, XDamageNotifyEvent *de) { && w->a.height <= w->damage_bounds.y + w->damage_bounds.height) { if (win_type_fade[w->window_type]) { set_fade(dpy, w, 0, get_opacity_percent(dpy, w), - fade_in_step, 0, True, True); + options.fade_in_step, 0, True, True); } w->usable = True; } @@ -2707,7 +2686,7 @@ ev_property_notify(XPropertyEvent *ev) { } // If frame extents property changes - if (frame_opacity && ev->atom == extents_atom) { + if (options.frame_opacity && ev->atom == extents_atom) { win *w = find_toplevel(dpy, ev->window); if (w) { get_frame_extents(dpy, w, ev->window); @@ -2717,7 +2696,7 @@ ev_property_notify(XPropertyEvent *ev) { } // If name changes - if (track_wdata + if (options.track_wdata && (name_atom == ev->atom || name_ewmh_atom == ev->atom)) { win *w = find_toplevel(dpy, ev->window); if (w && 1 == win_get_name(dpy, w)) @@ -2725,7 +2704,7 @@ ev_property_notify(XPropertyEvent *ev) { } // If class changes - if (track_wdata && class_atom == ev->atom) { + if (options.track_wdata && class_atom == ev->atom) { win *w = find_toplevel(dpy, ev->window); if (w) { win_get_class(dpy, w); @@ -3105,63 +3084,63 @@ parse_config(char *cpath) { // Get options from the configuration file. We don't do range checking // right now. It will be done later - // -D (fade_delta) + // -D (options.fade_delta) if (config_lookup_int(&cfg, "fade-delta", &ival)) - fade_delta = ival; - // -I (fade_in_step) + options.fade_delta = ival; + // -I (options.fade_in_step) if (config_lookup_float(&cfg, "fade-in-step", &dval)) - fade_in_step = normalize_d(dval) * OPAQUE; - // -O (fade_out_step) + options.fade_in_step = normalize_d(dval) * OPAQUE; + // -O (options.fade_out_step) if (config_lookup_float(&cfg, "fade-out-step", &dval)) - fade_out_step = normalize_d(dval) * OPAQUE; - // -r (shadow_radius) - config_lookup_int(&cfg, "shadow-radius", &shadow_radius); - // -o (shadow_opacity) - config_lookup_float(&cfg, "shadow-opacity", &shadow_opacity); - // -l (shadow_offset_x) - config_lookup_int(&cfg, "shadow-offset-x", &shadow_offset_x); - // -t (shadow_offset_y) - config_lookup_int(&cfg, "shadow-offset-y", &shadow_offset_y); - // -i (inactive_opacity) + options.fade_out_step = normalize_d(dval) * OPAQUE; + // -r (options.shadow_radius) + config_lookup_int(&cfg, "shadow-radius", &options.shadow_radius); + // -o (options.shadow_opacity) + config_lookup_float(&cfg, "shadow-opacity", &options.shadow_opacity); + // -l (options.shadow_offset_x) + config_lookup_int(&cfg, "shadow-offset-x", &options.shadow_offset_x); + // -t (options.shadow_offset_y) + config_lookup_int(&cfg, "shadow-offset-y", &options.shadow_offset_y); + // -i (options.inactive_opacity) if (config_lookup_float(&cfg, "inactive-opacity", &dval)) - inactive_opacity = normalize_d(dval) * OPAQUE; - // -e (frame_opacity) - config_lookup_float(&cfg, "frame-opacity", &frame_opacity); - // -z (clear_shadow) + options.inactive_opacity = normalize_d(dval) * OPAQUE; + // -e (options.frame_opacity) + config_lookup_float(&cfg, "frame-opacity", &options.frame_opacity); + // -z (options.clear_shadow) if (config_lookup_bool(&cfg, "clear-shadow", &ival)) - clear_shadow = ival; - // -c (shadow_enable) + options.clear_shadow = ival; + // -c (options.shadow_enable) if (config_lookup_bool(&cfg, "shadow", &ival) && ival) { - shadow_enable = 2; + options.shadow_enable = 2; wintype_arr_enable(win_type_shadow); } - // -C (no_dock_shadow) + // -C (options.no_dock_shadow) if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) - no_dock_shadow = ival; - // -G (no_dnd_shadow) + options.no_dock_shadow = ival; + // -G (options.no_dnd_shadow) if (config_lookup_bool(&cfg, "no-dnd-shadow", &ival)) - no_dnd_shadow = ival; - // -m (menu_opacity) - config_lookup_float(&cfg, "menu-opacity", &menu_opacity); - // -f (fading_enable) + options.no_dnd_shadow = ival; + // -m (options.menu_opacity) + config_lookup_float(&cfg, "menu-opacity", &options.menu_opacity); + // -f (options.fading_enable) if (config_lookup_bool(&cfg, "fading", &ival) && ival) { - fading_enable = 2; + options.fading_enable = 2; wintype_arr_enable(win_type_fade); } // --shadow-red - config_lookup_float(&cfg, "shadow-red", &shadow_red); + config_lookup_float(&cfg, "shadow-red", &options.shadow_red); // --shadow-green - config_lookup_float(&cfg, "shadow-green", &shadow_green); + config_lookup_float(&cfg, "shadow-green", &options.shadow_green); // --shadow-blue - config_lookup_float(&cfg, "shadow-blue", &shadow_blue); + config_lookup_float(&cfg, "shadow-blue", &options.shadow_blue); // --inactive-opacity-override if (config_lookup_bool(&cfg, "inactive-opacity-override", &ival)) - inactive_opacity_override = ival; + options.inactive_opacity_override = ival; // --inactive-dim - config_lookup_float(&cfg, "inactive-dim", &inactive_dim); + config_lookup_float(&cfg, "inactive-dim", &options.inactive_dim); // --mark-wmwin-focused if (config_lookup_bool(&cfg, "mark-wmwin-focused", &ival)) - mark_wmwin_focused = ival; + options.mark_wmwin_focused = ival; // --shadow-exclude { config_setting_t *setting = @@ -3171,13 +3150,13 @@ parse_config(char *cpath) { if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) { - condlst_add(&shadow_blacklist, + condlst_add(&options.shadow_blacklist, config_setting_get_string_elem(setting, i)); } } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { - condlst_add(&shadow_blacklist, + condlst_add(&options.shadow_blacklist, config_setting_get_string(setting)); } } @@ -3254,57 +3233,57 @@ get_cfg(int argc, char *const *argv) { switch (o) { // Short options case 'd': - display = optarg; + options.display = optarg; break; case 'D': - fade_delta = atoi(optarg); + options.fade_delta = atoi(optarg); break; case 'I': - fade_in_step = normalize_d(atof(optarg)) * OPAQUE; + options.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'O': - fade_out_step = normalize_d(atof(optarg)) * OPAQUE; + options.fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': - if (2 != shadow_enable) - shadow_enable = 1; + if (2 != options.shadow_enable) + options.shadow_enable = 1; break; case 'C': - no_dock_shadow = True; + options.no_dock_shadow = True; break; case 'm': - menu_opacity = atof(optarg); + options.menu_opacity = atof(optarg); break; case 'f': - if (2 != fading_enable) - fading_enable = 1; + if (2 != options.fading_enable) + options.fading_enable = 1; break; case 'F': - fade_trans = True; + options.fade_trans = True; break; case 'S': - synchronize = True; + options.synchronize = True; break; case 'r': - shadow_radius = atoi(optarg); + options.shadow_radius = atoi(optarg); break; case 'o': - shadow_opacity = atof(optarg); + options.shadow_opacity = atof(optarg); break; case 'l': - shadow_offset_x = atoi(optarg); + options.shadow_offset_x = atoi(optarg); break; case 't': - shadow_offset_y = atoi(optarg); + options.shadow_offset_y = atoi(optarg); break; case 'i': - inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); + options.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; case 'e': - frame_opacity = atof(optarg); + options.frame_opacity = atof(optarg); break; case 'z': - clear_shadow = True; + options.clear_shadow = True; break; case 'n': case 'a': @@ -3313,10 +3292,10 @@ get_cfg(int argc, char *const *argv) { "-n, -a, and -s have been removed.\n"); break; case 'G': - no_dnd_shadow = True; + options.no_dnd_shadow = True; break; case 'b': - fork_after_register = True; + options.fork_after_register = True; break; // Long options case 256: @@ -3324,31 +3303,31 @@ get_cfg(int argc, char *const *argv) { break; case 257: // --shadow-red - shadow_red = atof(optarg); + options.shadow_red = atof(optarg); break; case 258: // --shadow-green - shadow_green = atof(optarg); + options.shadow_green = atof(optarg); break; case 259: // --shadow-blue - shadow_blue = atof(optarg); + options.shadow_blue = atof(optarg); break; case 260: // --inactive-opacity-override - inactive_opacity_override = True; + options.inactive_opacity_override = True; break; case 261: // --inactive-dim - inactive_dim = atof(optarg); + options.inactive_dim = atof(optarg); break; case 262: // --mark-wmwin-focused - mark_wmwin_focused = True; + options.mark_wmwin_focused = True; break; case 263: // --shadow-exclude - condlst_add(&shadow_blacklist, optarg); + condlst_add(&options.shadow_blacklist, optarg); break; default: usage(); @@ -3357,43 +3336,43 @@ get_cfg(int argc, char *const *argv) { } // Range checking and option assignments - fade_delta = max_i(fade_delta, 1); - shadow_radius = max_i(shadow_radius, 1); - shadow_red = normalize_d(shadow_red); - shadow_green = normalize_d(shadow_green); - shadow_blue = normalize_d(shadow_blue); - inactive_dim = normalize_d(inactive_dim); - frame_opacity = normalize_d(frame_opacity); - shadow_opacity = normalize_d(shadow_opacity); - menu_opacity = normalize_d(menu_opacity); - if (OPAQUE == inactive_opacity) { - inactive_opacity = 0; - } - if (1 == shadow_enable) + options.fade_delta = max_i(options.fade_delta, 1); + options.shadow_radius = max_i(options.shadow_radius, 1); + options.shadow_red = normalize_d(options.shadow_red); + options.shadow_green = normalize_d(options.shadow_green); + options.shadow_blue = normalize_d(options.shadow_blue); + options.inactive_dim = normalize_d(options.inactive_dim); + options.frame_opacity = normalize_d(options.frame_opacity); + options.shadow_opacity = normalize_d(options.shadow_opacity); + options.menu_opacity = normalize_d(options.menu_opacity); + if (OPAQUE == options.inactive_opacity) { + options.inactive_opacity = 0; + } + if (1 == options.shadow_enable) wintype_arr_enable(win_type_shadow); win_type_shadow[WINTYPE_DESKTOP] = False; - if (no_dock_shadow) + if (options.no_dock_shadow) win_type_shadow[WINTYPE_DOCK] = False; - if (no_dnd_shadow) + if (options.no_dnd_shadow) win_type_shadow[WINTYPE_DND] = False; - if (1 == fading_enable) { + if (1 == options.fading_enable) { wintype_arr_enable(win_type_fade); } - if (1.0 != menu_opacity) { - win_type_opacity[WINTYPE_DROPDOWN_MENU] = menu_opacity; - win_type_opacity[WINTYPE_POPUP_MENU] = menu_opacity; + if (1.0 != options.menu_opacity) { + win_type_opacity[WINTYPE_DROPDOWN_MENU] = options.menu_opacity; + win_type_opacity[WINTYPE_POPUP_MENU] = options.menu_opacity; } // Other variables determined by options // Determine whether we need to track focus changes - if (inactive_opacity || inactive_dim) { - track_focus = True; + if (options.inactive_opacity || options.inactive_dim) { + options.track_focus = True; } // Determine whether we need to track window name and class - if (shadow_blacklist || fade_blacklist) - track_wdata = True; + if (options.shadow_blacklist || options.fade_blacklist) + options.track_wdata = True; } static void @@ -3459,16 +3438,16 @@ main(int argc, char **argv) { get_cfg(argc, argv); - fade_time = get_time_in_milliseconds(); + options.fade_time = get_time_in_milliseconds(); - dpy = XOpenDisplay(display); + dpy = XOpenDisplay(options.display); if (!dpy) { fprintf(stderr, "Can't open display\n"); exit(1); } XSetErrorHandler(error); - if (synchronize) { + if (options.synchronize) { XSynchronize(dpy, 1); } @@ -3510,13 +3489,13 @@ main(int argc, char **argv) { register_cm(scr); - if (fork_after_register) fork_after(); + if (options.fork_after_register) fork_after(); get_atoms(); pa.subwindow_mode = IncludeInferiors; - gaussian_map = make_gaussian_map(dpy, shadow_radius); + gaussian_map = make_gaussian_map(dpy, options.shadow_radius); presum_gaussian(gaussian_map); root_width = DisplayWidth(dpy, scr); @@ -3530,16 +3509,16 @@ main(int argc, char **argv) { // Generates another Picture for shadows if the color is modified by // user - if (!shadow_red && !shadow_green && !shadow_blue) { + if (!options.shadow_red && !options.shadow_green && !options.shadow_blue) { cshadow_picture = black_picture; } else { cshadow_picture = solid_picture(dpy, True, 1, - shadow_red, shadow_green, shadow_blue); + options.shadow_red, options.shadow_green, options.shadow_blue); } - // Generates a picture for inactive_dim - if (inactive_dim) { - dim_picture = solid_picture(dpy, True, inactive_dim, 0, 0, 0); + // Generates a picture for options.inactive_dim + if (options.inactive_dim) { + dim_picture = solid_picture(dpy, True, options.inactive_dim, 0, 0, 0); } all_damage = None; @@ -3563,7 +3542,7 @@ main(int argc, char **argv) { XFree(children); - if (track_focus) { + if (options.track_focus) { recheck_focus(dpy); } diff --git a/compton.h b/compton.h index c20f1e9b4..922f02282 100644 --- a/compton.h +++ b/compton.h @@ -241,6 +241,64 @@ typedef struct _win { struct _win *prev_trans; } win; +typedef struct _options { + char *display; + int shadow_radius; + int shadow_offset_x; + int shadow_offset_y; + double shadow_opacity; + + /// How much to fade in in a single fading step. + opacity_t fade_in_step; + /// How much to fade out in a single fading step. + opacity_t fade_out_step; + unsigned long fade_delta; + unsigned long fade_time; + Bool fade_trans; + + Bool clear_shadow; + + /// Default opacity for inactive windows. + /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for + /// not enabled, default. + opacity_t inactive_opacity; + /// Whether inactive_opacity overrides the opacity set by window + /// attributes. + Bool inactive_opacity_override; + double frame_opacity; + /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. + double inactive_dim; + + /// Whether to try to detect WM windows and mark them as focused. + double mark_wmwin_focused; + + /// Whether compton needs to track focus changes. + Bool track_focus; + /// Whether compton needs to track window name and class. + Bool track_wdata; + + /// Shadow blacklist. A linked list of conditions. + wincond *shadow_blacklist; + /// Fading blacklist. A linked list of conditions. + wincond *fade_blacklist; + + /// Whether to fork to background. + Bool fork_after_register; + /// Red, green and blue tone of the shadow. + double shadow_red; + double shadow_green; + double shadow_blue; + + Bool synchronize; + + // Temporary options + int shadow_enable; + int fading_enable; + Bool no_dock_shadow; + Bool no_dnd_shadow; + double menu_opacity; +} options_t; + typedef struct _conv { int size; double *data; From fdf1593a098b02bd79ced0ae05749268dd486220 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 26 Sep 2012 02:37:11 -0500 Subject: [PATCH 042/178] remove fade_time from options struct. --- compton.c | 13 +++++++------ compton.h | 1 - 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compton.c b/compton.c index fad26365e..8c493689d 100644 --- a/compton.c +++ b/compton.c @@ -121,7 +121,6 @@ static options_t options = { .fade_in_step = 0.028 * OPAQUE, .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, - .fade_time = 0, .fade_trans = False, .clear_shadow = False, .inactive_opacity = 0, @@ -149,6 +148,8 @@ static options_t options = { * Fades */ +unsigned long fade_time; + /** * Get current system clock in milliseconds. * @@ -171,7 +172,7 @@ get_time_in_milliseconds() { */ static int fade_timeout(void) { - int diff = options.fade_delta - get_time_in_milliseconds() + options.fade_time; + int diff = options.fade_delta - get_time_in_milliseconds() + fade_time; if (diff < 0) diff = 0; @@ -1177,10 +1178,10 @@ paint_preprocess(Display *dpy, win *list) { // Sounds like the timeout in poll() frequently does not work // accurately, asking it to wait to 20ms, and often it would wait for // 19ms, so the step value has to be rounded. - unsigned steps = roundl((double) (get_time_in_milliseconds() - options.fade_time) / options.fade_delta); + unsigned steps = roundl((double) (get_time_in_milliseconds() - fade_time) / options.fade_delta); - // Reset options.fade_time - options.fade_time = get_time_in_milliseconds(); + // Reset fade_time + fade_time = get_time_in_milliseconds(); for (w = list; w; w = next) { // In case calling the fade callback function destroys this window @@ -3438,7 +3439,7 @@ main(int argc, char **argv) { get_cfg(argc, argv); - options.fade_time = get_time_in_milliseconds(); + fade_time = get_time_in_milliseconds(); dpy = XOpenDisplay(options.display); if (!dpy) { diff --git a/compton.h b/compton.h index 922f02282..78c7c97a9 100644 --- a/compton.h +++ b/compton.h @@ -253,7 +253,6 @@ typedef struct _options { /// How much to fade out in a single fading step. opacity_t fade_out_step; unsigned long fade_delta; - unsigned long fade_time; Bool fade_trans; Bool clear_shadow; From 93de3d1de2ca90e509760c543b02ed9328831485 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 26 Sep 2012 03:18:10 -0500 Subject: [PATCH 043/178] initialize fade_time just to be explicit. --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 8c493689d..f49f03a73 100644 --- a/compton.c +++ b/compton.c @@ -148,7 +148,7 @@ static options_t options = { * Fades */ -unsigned long fade_time; +unsigned long fade_time = 0; /** * Get current system clock in milliseconds. From 60f724a30a4814095bb219433f704d099ad46ebb Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 26 Sep 2012 18:54:35 +0800 Subject: [PATCH 044/178] Misc: Code clean-up - Change some members of options_t. Clean up wrongly replaced option names in comments. Rename "options" to "opts", to avoid breaking line width too much, and to minimize typing as it's so frequently used. :-) - Let general options in commandline arguments override wintype-specific options in a configuration file, which could be a more natural behavior. --- compton.c | 363 ++++++++++++++++++++++++++---------------------------- compton.h | 60 +++++---- 2 files changed, 210 insertions(+), 213 deletions(-) diff --git a/compton.c b/compton.c index fad26365e..0d1f30e7a 100644 --- a/compton.c +++ b/compton.c @@ -96,10 +96,7 @@ Atom class_atom; Atom win_type_atom; Atom win_type[NUM_WINTYPES]; -// Window type settings -double win_type_opacity[NUM_WINTYPES]; -Bool win_type_shadow[NUM_WINTYPES]; -Bool win_type_fade[NUM_WINTYPES]; +unsigned long fade_time; /** * Macros @@ -112,7 +109,7 @@ Bool win_type_fade[NUM_WINTYPES]; * Options */ -static options_t options = { +static options_t opts = { .display = NULL, .shadow_radius = 12, .shadow_offset_x = -15, @@ -121,7 +118,6 @@ static options_t options = { .fade_in_step = 0.028 * OPAQUE, .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, - .fade_time = 0, .fade_trans = False, .clear_shadow = False, .inactive_opacity = 0, @@ -129,20 +125,18 @@ static options_t options = { .frame_opacity = 0.0, .inactive_dim = 0.0, .mark_wmwin_focused = False, - .track_focus = False, - .track_wdata = False, .shadow_blacklist = NULL, .fade_blacklist = NULL, .fork_after_register = False, .shadow_red = 0.0, .shadow_green = 0.0, .shadow_blue = 0.0, + .wintype_opacity = { 0.0 }, + .wintype_shadow = { False }, + .wintype_fade = { False }, .synchronize = False, - .shadow_enable = 0, - .fading_enable = 0, - .no_dock_shadow = False, - .no_dnd_shadow = False, - .menu_opacity = 1.0 + .track_focus = False, + .track_wdata = False, }; /** @@ -171,7 +165,7 @@ get_time_in_milliseconds() { */ static int fade_timeout(void) { - int diff = options.fade_delta - get_time_in_milliseconds() + options.fade_time; + int diff = opts.fade_delta - get_time_in_milliseconds() + fade_time; if (diff < 0) diff = 0; @@ -200,11 +194,11 @@ run_fade(Display *dpy, win *w, unsigned steps) { // calculations if (w->opacity < w->opacity_tgt) w->opacity = normalize_d_range( - (double) w->opacity + (double) options.fade_in_step * steps, + (double) w->opacity + (double) opts.fade_in_step * steps, 0.0, w->opacity_tgt); else w->opacity = normalize_d_range( - (double) w->opacity - (double) options.fade_out_step * steps, + (double) w->opacity - (double) opts.fade_out_step * steps, w->opacity_tgt, OPAQUE); } @@ -420,12 +414,12 @@ make_shadow(Display *dpy, double opacity, * center (fill the complete data array) */ - // If options.clear_shadow is enabled and the border & corner shadow (which + // If clear_shadow is enabled and the border & corner shadow (which // later will be filled) could entirely cover the area of the shadow // that will be displayed, do not bother filling other pixels. If it // can't, we must fill the other pixels here. - if (!(options.clear_shadow && options.shadow_offset_x <= 0 && options.shadow_offset_x >= -cgsize - && options.shadow_offset_y <= 0 && options.shadow_offset_y >= -cgsize)) { + if (!(opts.clear_shadow && opts.shadow_offset_x <= 0 && opts.shadow_offset_x >= -cgsize + && opts.shadow_offset_y <= 0 && opts.shadow_offset_y >= -cgsize)) { if (cgsize > 0) { d = shadow_top[opacity_int * (cgsize + 1) + cgsize]; } else { @@ -496,14 +490,14 @@ make_shadow(Display *dpy, double opacity, } } - if (options.clear_shadow) { + if (opts.clear_shadow) { // Clear the region in the shadow that the window would cover based // on shadow_offset_{x,y} user provides - int xstart = normalize_i_range(- (int) options.shadow_offset_x, 0, swidth); - int xrange = normalize_i_range(width - (int) options.shadow_offset_x, + int xstart = normalize_i_range(- (int) opts.shadow_offset_x, 0, swidth); + int xrange = normalize_i_range(width - (int) opts.shadow_offset_x, 0, swidth) - xstart; - int ystart = normalize_i_range(- (int) options.shadow_offset_y, 0, sheight); - int yend = normalize_i_range(height - (int) options.shadow_offset_y, + int ystart = normalize_i_range(- (int) opts.shadow_offset_y, 0, sheight); + int yend = normalize_i_range(height - (int) opts.shadow_offset_y, 0, sheight); int y; @@ -871,11 +865,11 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { if (WIN_EVMODE_FRAME == mode || find_win(dpy, wid)) { evmask |= PropertyChangeMask; - if (options.track_focus) evmask |= FocusChangeMask; + if (opts.track_focus) evmask |= FocusChangeMask; } if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) { - if (options.frame_opacity || options.track_wdata) + if (opts.frame_opacity || opts.track_wdata) evmask |= PropertyChangeMask; } @@ -1177,10 +1171,10 @@ paint_preprocess(Display *dpy, win *list) { // Sounds like the timeout in poll() frequently does not work // accurately, asking it to wait to 20ms, and often it would wait for // 19ms, so the step value has to be rounded. - unsigned steps = roundl((double) (get_time_in_milliseconds() - options.fade_time) / options.fade_delta); + unsigned steps = roundl((double) (get_time_in_milliseconds() - fade_time) / opts.fade_delta); - // Reset options.fade_time - options.fade_time = get_time_in_milliseconds(); + // Reset fade_time + fade_time = get_time_in_milliseconds(); for (w = list; w; w = next) { // In case calling the fade callback function destroys this window @@ -1255,9 +1249,9 @@ paint_preprocess(Display *dpy, win *list) { w->opacity_cur = w->opacity; } - // Calculate options.frame_opacity - if (options.frame_opacity && 1.0 != options.frame_opacity && w->top_width) - w->frame_opacity = get_opacity_percent(dpy, w) * options.frame_opacity; + // Calculate frame_opacity + if (opts.frame_opacity && 1.0 != opts.frame_opacity && w->top_width) + w->frame_opacity = get_opacity_percent(dpy, w) * opts.frame_opacity; else w->frame_opacity = 0.0; @@ -1273,9 +1267,9 @@ paint_preprocess(Display *dpy, win *list) { // Calculate shadow opacity if (w->frame_opacity) - w->shadow_opacity = options.shadow_opacity * w->frame_opacity; + w->shadow_opacity = opts.shadow_opacity * w->frame_opacity; else - w->shadow_opacity = options.shadow_opacity * get_opacity_percent(dpy, w); + w->shadow_opacity = opts.shadow_opacity * get_opacity_percent(dpy, w); // Rebuild shadow_pict if necessary if (w->flags & WFLAG_SIZE_CHANGE) @@ -1561,7 +1555,7 @@ map_win(Display *dpy, Window id, mark_client_win(dpy, w, cw); } } - else if (options.frame_opacity) { + else if (opts.frame_opacity) { // Refetch frame extents just in case it changes when the window is // unmapped get_frame_extents(dpy, w, w->client_win); @@ -1576,7 +1570,7 @@ map_win(Display *dpy, Window id, #endif // Get window name and class if we are tracking them - if (options.track_wdata) { + if (opts.track_wdata) { win_get_name(dpy, w); win_get_class(dpy, w); } @@ -1587,11 +1581,11 @@ map_win(Display *dpy, Window id, * XSelectInput() is called too late. We have to recheck the focused * window here. */ - if (options.track_focus) { + if (opts.track_focus) { recheck_focus(dpy); // Consider a window without client window a WM window and mark it - // focused if options.mark_wmwin_focused is on - if (options.mark_wmwin_focused && !w->client_win) + // focused if mark_wmwin_focused is on + if (opts.mark_wmwin_focused && !w->client_win) w->focused = True; } @@ -1726,17 +1720,17 @@ determine_mode(Display *dpy, win *w) { /** * Calculate and set the opacity of a window. * - * If window is inactive and options.inactive_opacity_override is set, the + * If window is inactive and inactive_opacity_override is set, the * priority is: (Simulates the old behavior) * - * options.inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) + * inactive_opacity > _NET_WM_WINDOW_OPACITY (if not opaque) * > window type default opacity * * Otherwise: * * _NET_WM_WINDOW_OPACITY (if not opaque) * > window type default opacity (if not opaque) - * > options.inactive_opacity + * > inactive_opacity * * @param dpy X display to use * @param w struct _win object representing the window @@ -1759,15 +1753,15 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { } if (OPAQUE == (opacity = w->opacity_prop)) { - if (1.0 != win_type_opacity[w->window_type]) { - opacity = win_type_opacity[w->window_type] * OPAQUE; + if (1.0 != opts.wintype_opacity[w->window_type]) { + opacity = opts.wintype_opacity[w->window_type] * OPAQUE; } } - // Respect options.inactive_opacity in some cases - if (options.inactive_opacity && is_normal_win(w) && False == w->focused - && (OPAQUE == opacity || options.inactive_opacity_override)) { - opacity = options.inactive_opacity; + // Respect inactive_opacity in some cases + if (opts.inactive_opacity && is_normal_win(w) && False == w->focused + && (OPAQUE == opacity || opts.inactive_opacity_override)) { + opacity = opts.inactive_opacity; } w->opacity_tgt = opacity; @@ -1777,7 +1771,7 @@ static void calc_dim(Display *dpy, win *w) { Bool dim; - if (options.inactive_dim && is_normal_win(w) && !(w->focused)) { + if (opts.inactive_dim && is_normal_win(w) && !(w->focused)) { dim = True; } else { dim = False; @@ -1794,7 +1788,7 @@ calc_dim(Display *dpy, win *w) { */ static void determine_fade(Display *dpy, win *w) { - w->fade = win_type_fade[w->window_type]; + w->fade = opts.wintype_fade[w->window_type]; } /** @@ -1805,8 +1799,8 @@ static void determine_shadow(Display *dpy, win *w) { Bool shadow_old = w->shadow; - w->shadow = (win_type_shadow[w->window_type] - && !win_match(w, options.shadow_blacklist, &w->cache_sblst)); + w->shadow = (opts.wintype_shadow[w->window_type] + && !win_match(w, opts.shadow_blacklist, &w->cache_sblst)); // Window extents need update on shadow state change if (w->shadow != shadow_old) { @@ -1843,8 +1837,8 @@ calc_win_size(Display *dpy, win *w) { */ static void calc_shadow_geometry(Display *dpy, win *w) { - w->shadow_dx = options.shadow_offset_x; - w->shadow_dy = options.shadow_offset_y; + w->shadow_dx = opts.shadow_offset_x; + w->shadow_dy = opts.shadow_offset_y; w->shadow_width = w->widthb + gaussian_map->size; w->shadow_height = w->heightb + gaussian_map->size; } @@ -1862,7 +1856,7 @@ mark_client_win(Display *dpy, win *w, Window client) { // Get the frame width and monitor further frame width changes on client // window if necessary - if (options.frame_opacity) { + if (opts.frame_opacity) { get_frame_extents(dpy, w, client); } XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); @@ -2199,9 +2193,9 @@ damage_win(Display *dpy, XDamageNotifyEvent *de) { && 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) { - if (win_type_fade[w->window_type]) { + if (opts.wintype_fade[w->window_type]) { set_fade(dpy, w, 0, get_opacity_percent(dpy, w), - options.fade_in_step, 0, True, True); + opts.fade_in_step, 0, True, True); } w->usable = True; } @@ -2686,7 +2680,7 @@ ev_property_notify(XPropertyEvent *ev) { } // If frame extents property changes - if (options.frame_opacity && ev->atom == extents_atom) { + if (opts.frame_opacity && ev->atom == extents_atom) { win *w = find_toplevel(dpy, ev->window); if (w) { get_frame_extents(dpy, w, ev->window); @@ -2696,7 +2690,7 @@ ev_property_notify(XPropertyEvent *ev) { } // If name changes - if (options.track_wdata + if (opts.track_wdata && (name_atom == ev->atom || name_ewmh_atom == ev->atom)) { win *w = find_toplevel(dpy, ev->window); if (w && 1 == win_get_name(dpy, w)) @@ -2704,7 +2698,7 @@ ev_property_notify(XPropertyEvent *ev) { } // If class changes - if (options.track_wdata && class_atom == ev->atom) { + if (opts.track_wdata && class_atom == ev->atom) { win *w = find_toplevel(dpy, ev->window); if (w) { win_get_class(dpy, w); @@ -3051,7 +3045,7 @@ open_config_file(char *cpath, char **ppath) { * Parse a configuration file from default location. */ static void -parse_config(char *cpath) { +parse_config(char *cpath, struct options_tmp *pcfgtmp) { char *path = NULL, *parent = NULL; FILE *f; config_t cfg; @@ -3084,63 +3078,55 @@ parse_config(char *cpath) { // Get options from the configuration file. We don't do range checking // right now. It will be done later - // -D (options.fade_delta) + // -D (fade_delta) if (config_lookup_int(&cfg, "fade-delta", &ival)) - options.fade_delta = ival; - // -I (options.fade_in_step) + opts.fade_delta = ival; + // -I (fade_in_step) if (config_lookup_float(&cfg, "fade-in-step", &dval)) - options.fade_in_step = normalize_d(dval) * OPAQUE; - // -O (options.fade_out_step) + opts.fade_in_step = normalize_d(dval) * OPAQUE; + // -O (fade_out_step) if (config_lookup_float(&cfg, "fade-out-step", &dval)) - options.fade_out_step = normalize_d(dval) * OPAQUE; - // -r (options.shadow_radius) - config_lookup_int(&cfg, "shadow-radius", &options.shadow_radius); - // -o (options.shadow_opacity) - config_lookup_float(&cfg, "shadow-opacity", &options.shadow_opacity); - // -l (options.shadow_offset_x) - config_lookup_int(&cfg, "shadow-offset-x", &options.shadow_offset_x); - // -t (options.shadow_offset_y) - config_lookup_int(&cfg, "shadow-offset-y", &options.shadow_offset_y); - // -i (options.inactive_opacity) + opts.fade_out_step = normalize_d(dval) * OPAQUE; + // -r (shadow_radius) + config_lookup_int(&cfg, "shadow-radius", &opts.shadow_radius); + // -o (shadow_opacity) + config_lookup_float(&cfg, "shadow-opacity", &opts.shadow_opacity); + // -l (shadow_offset_x) + config_lookup_int(&cfg, "shadow-offset-x", &opts.shadow_offset_x); + // -t (shadow_offset_y) + config_lookup_int(&cfg, "shadow-offset-y", &opts.shadow_offset_y); + // -i (inactive_opacity) if (config_lookup_float(&cfg, "inactive-opacity", &dval)) - options.inactive_opacity = normalize_d(dval) * OPAQUE; - // -e (options.frame_opacity) - config_lookup_float(&cfg, "frame-opacity", &options.frame_opacity); - // -z (options.clear_shadow) - if (config_lookup_bool(&cfg, "clear-shadow", &ival)) - options.clear_shadow = ival; - // -c (options.shadow_enable) - if (config_lookup_bool(&cfg, "shadow", &ival) && ival) { - options.shadow_enable = 2; - wintype_arr_enable(win_type_shadow); - } - // -C (options.no_dock_shadow) - if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) - options.no_dock_shadow = ival; - // -G (options.no_dnd_shadow) - if (config_lookup_bool(&cfg, "no-dnd-shadow", &ival)) - options.no_dnd_shadow = ival; - // -m (options.menu_opacity) - config_lookup_float(&cfg, "menu-opacity", &options.menu_opacity); - // -f (options.fading_enable) - if (config_lookup_bool(&cfg, "fading", &ival) && ival) { - options.fading_enable = 2; - wintype_arr_enable(win_type_fade); - } + opts.inactive_opacity = normalize_d(dval) * OPAQUE; + // -e (frame_opacity) + config_lookup_float(&cfg, "frame-opacity", &opts.frame_opacity); + // -z (clear_shadow) + lcfg_lookup_bool(&cfg, "clear-shadow", &opts.clear_shadow); + // -c (shadow_enable) + if (config_lookup_bool(&cfg, "shadow", &ival) && ival) + wintype_arr_enable(opts.wintype_shadow); + // -C (no_dock_shadow) + lcfg_lookup_bool(&cfg, "no-dock-shadow", &pcfgtmp->no_dock_shadow); + // -G (no_dnd_shadow) + lcfg_lookup_bool(&cfg, "no-dnd-shadow", &pcfgtmp->no_dnd_shadow); + // -m (menu_opacity) + config_lookup_float(&cfg, "menu-opacity", &pcfgtmp->menu_opacity); + // -f (fading_enable) + if (config_lookup_bool(&cfg, "fading", &ival) && ival) + wintype_arr_enable(opts.wintype_fade); // --shadow-red - config_lookup_float(&cfg, "shadow-red", &options.shadow_red); + config_lookup_float(&cfg, "shadow-red", &opts.shadow_red); // --shadow-green - config_lookup_float(&cfg, "shadow-green", &options.shadow_green); + config_lookup_float(&cfg, "shadow-green", &opts.shadow_green); // --shadow-blue - config_lookup_float(&cfg, "shadow-blue", &options.shadow_blue); + config_lookup_float(&cfg, "shadow-blue", &opts.shadow_blue); // --inactive-opacity-override - if (config_lookup_bool(&cfg, "inactive-opacity-override", &ival)) - options.inactive_opacity_override = ival; + lcfg_lookup_bool(&cfg, "inactive-opacity-override", + &opts.inactive_opacity_override); // --inactive-dim - config_lookup_float(&cfg, "inactive-dim", &options.inactive_dim); + config_lookup_float(&cfg, "inactive-dim", &opts.inactive_dim); // --mark-wmwin-focused - if (config_lookup_bool(&cfg, "mark-wmwin-focused", &ival)) - options.mark_wmwin_focused = ival; + lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opts.mark_wmwin_focused); // --shadow-exclude { config_setting_t *setting = @@ -3150,13 +3136,13 @@ parse_config(char *cpath) { if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) { - condlst_add(&options.shadow_blacklist, + condlst_add(&opts.shadow_blacklist, config_setting_get_string_elem(setting, i)); } } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { - condlst_add(&options.shadow_blacklist, + condlst_add(&opts.shadow_blacklist, config_setting_get_string(setting)); } } @@ -3171,11 +3157,11 @@ parse_config(char *cpath) { free(str); if (setting) { if (config_setting_lookup_bool(setting, "shadow", &ival)) - win_type_shadow[i] = (Bool) ival; + opts.wintype_shadow[i] = (Bool) ival; if (config_setting_lookup_bool(setting, "fade", &ival)) - win_type_fade[i] = (Bool) ival; + opts.wintype_fade[i] = (Bool) ival; config_setting_lookup_float(setting, "opacity", - &win_type_opacity[i]); + &opts.wintype_opacity[i]); } } } @@ -3203,13 +3189,19 @@ get_cfg(int argc, char *const *argv) { { NULL, 0, NULL, 0 }, }; + struct options_tmp cfgtmp = { + .no_dock_shadow = False, + .no_dnd_shadow = False, + .menu_opacity = 1.0, + }; + Bool shadow_enable = False, fading_enable = False; int o, longopt_idx, i; char *config_file = NULL; for (i = 0; i < NUM_WINTYPES; ++i) { - win_type_fade[i] = False; - win_type_shadow[i] = False; - win_type_opacity[i] = 1.0; + opts.wintype_fade[i] = False; + opts.wintype_shadow[i] = False; + opts.wintype_opacity[i] = 1.0; } // Pre-parse the commandline arguments to check for --config and invalid @@ -3223,7 +3215,7 @@ get_cfg(int argc, char *const *argv) { } #ifdef CONFIG_LIBCONFIG - parse_config(config_file); + parse_config(config_file, &cfgtmp); #endif // Parse commandline arguments. Range checking will be done later. @@ -3233,57 +3225,58 @@ get_cfg(int argc, char *const *argv) { switch (o) { // Short options case 'd': - options.display = optarg; + opts.display = optarg; break; case 'D': - options.fade_delta = atoi(optarg); + opts.fade_delta = atoi(optarg); break; case 'I': - options.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; + opts.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'O': - options.fade_out_step = normalize_d(atof(optarg)) * OPAQUE; + opts.fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': - if (2 != options.shadow_enable) - options.shadow_enable = 1; + shadow_enable = True; break; case 'C': - options.no_dock_shadow = True; + cfgtmp.no_dock_shadow = True; + break; + case 'G': + cfgtmp.no_dnd_shadow = True; break; case 'm': - options.menu_opacity = atof(optarg); + cfgtmp.menu_opacity = atof(optarg); break; case 'f': - if (2 != options.fading_enable) - options.fading_enable = 1; + fading_enable = True; break; case 'F': - options.fade_trans = True; + opts.fade_trans = True; break; case 'S': - options.synchronize = True; + opts.synchronize = True; break; case 'r': - options.shadow_radius = atoi(optarg); + opts.shadow_radius = atoi(optarg); break; case 'o': - options.shadow_opacity = atof(optarg); + opts.shadow_opacity = atof(optarg); break; case 'l': - options.shadow_offset_x = atoi(optarg); + opts.shadow_offset_x = atoi(optarg); break; case 't': - options.shadow_offset_y = atoi(optarg); + opts.shadow_offset_y = atoi(optarg); break; case 'i': - options.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); + opts.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; case 'e': - options.frame_opacity = atof(optarg); + opts.frame_opacity = atof(optarg); break; case 'z': - options.clear_shadow = True; + opts.clear_shadow = True; break; case 'n': case 'a': @@ -3291,11 +3284,8 @@ get_cfg(int argc, char *const *argv) { fprintf(stderr, "Warning: " "-n, -a, and -s have been removed.\n"); break; - case 'G': - options.no_dnd_shadow = True; - break; case 'b': - options.fork_after_register = True; + opts.fork_after_register = True; break; // Long options case 256: @@ -3303,31 +3293,31 @@ get_cfg(int argc, char *const *argv) { break; case 257: // --shadow-red - options.shadow_red = atof(optarg); + opts.shadow_red = atof(optarg); break; case 258: // --shadow-green - options.shadow_green = atof(optarg); + opts.shadow_green = atof(optarg); break; case 259: // --shadow-blue - options.shadow_blue = atof(optarg); + opts.shadow_blue = atof(optarg); break; case 260: // --inactive-opacity-override - options.inactive_opacity_override = True; + opts.inactive_opacity_override = True; break; case 261: // --inactive-dim - options.inactive_dim = atof(optarg); + opts.inactive_dim = atof(optarg); break; case 262: // --mark-wmwin-focused - options.mark_wmwin_focused = True; + opts.mark_wmwin_focused = True; break; case 263: // --shadow-exclude - condlst_add(&options.shadow_blacklist, optarg); + condlst_add(&opts.shadow_blacklist, optarg); break; default: usage(); @@ -3336,43 +3326,42 @@ get_cfg(int argc, char *const *argv) { } // Range checking and option assignments - options.fade_delta = max_i(options.fade_delta, 1); - options.shadow_radius = max_i(options.shadow_radius, 1); - options.shadow_red = normalize_d(options.shadow_red); - options.shadow_green = normalize_d(options.shadow_green); - options.shadow_blue = normalize_d(options.shadow_blue); - options.inactive_dim = normalize_d(options.inactive_dim); - options.frame_opacity = normalize_d(options.frame_opacity); - options.shadow_opacity = normalize_d(options.shadow_opacity); - options.menu_opacity = normalize_d(options.menu_opacity); - if (OPAQUE == options.inactive_opacity) { - options.inactive_opacity = 0; - } - if (1 == options.shadow_enable) - wintype_arr_enable(win_type_shadow); - win_type_shadow[WINTYPE_DESKTOP] = False; - if (options.no_dock_shadow) - win_type_shadow[WINTYPE_DOCK] = False; - if (options.no_dnd_shadow) - win_type_shadow[WINTYPE_DND] = False; - if (1 == options.fading_enable) { - wintype_arr_enable(win_type_fade); - } - if (1.0 != options.menu_opacity) { - win_type_opacity[WINTYPE_DROPDOWN_MENU] = options.menu_opacity; - win_type_opacity[WINTYPE_POPUP_MENU] = options.menu_opacity; + opts.fade_delta = max_i(opts.fade_delta, 1); + opts.shadow_radius = max_i(opts.shadow_radius, 1); + opts.shadow_red = normalize_d(opts.shadow_red); + opts.shadow_green = normalize_d(opts.shadow_green); + opts.shadow_blue = normalize_d(opts.shadow_blue); + opts.inactive_dim = normalize_d(opts.inactive_dim); + opts.frame_opacity = normalize_d(opts.frame_opacity); + opts.shadow_opacity = normalize_d(opts.shadow_opacity); + cfgtmp.menu_opacity = normalize_d(cfgtmp.menu_opacity); + if (OPAQUE == opts.inactive_opacity) { + opts.inactive_opacity = 0; + } + if (shadow_enable) + wintype_arr_enable(opts.wintype_shadow); + opts.wintype_shadow[WINTYPE_DESKTOP] = False; + if (cfgtmp.no_dock_shadow) + opts.wintype_shadow[WINTYPE_DOCK] = False; + if (cfgtmp.no_dnd_shadow) + opts.wintype_shadow[WINTYPE_DND] = False; + if (fading_enable) + wintype_arr_enable(opts.wintype_fade); + if (1.0 != cfgtmp.menu_opacity) { + opts.wintype_opacity[WINTYPE_DROPDOWN_MENU] = cfgtmp.menu_opacity; + opts.wintype_opacity[WINTYPE_POPUP_MENU] = cfgtmp.menu_opacity; } // Other variables determined by options // Determine whether we need to track focus changes - if (options.inactive_opacity || options.inactive_dim) { - options.track_focus = True; + if (opts.inactive_opacity || opts.inactive_dim) { + opts.track_focus = True; } // Determine whether we need to track window name and class - if (options.shadow_blacklist || options.fade_blacklist) - options.track_wdata = True; + if (opts.shadow_blacklist || opts.fade_blacklist) + opts.track_wdata = True; } static void @@ -3438,16 +3427,16 @@ main(int argc, char **argv) { get_cfg(argc, argv); - options.fade_time = get_time_in_milliseconds(); + fade_time = get_time_in_milliseconds(); - dpy = XOpenDisplay(options.display); + dpy = XOpenDisplay(opts.display); if (!dpy) { fprintf(stderr, "Can't open display\n"); exit(1); } XSetErrorHandler(error); - if (options.synchronize) { + if (opts.synchronize) { XSynchronize(dpy, 1); } @@ -3489,13 +3478,13 @@ main(int argc, char **argv) { register_cm(scr); - if (options.fork_after_register) fork_after(); + if (opts.fork_after_register) fork_after(); get_atoms(); pa.subwindow_mode = IncludeInferiors; - gaussian_map = make_gaussian_map(dpy, options.shadow_radius); + gaussian_map = make_gaussian_map(dpy, opts.shadow_radius); presum_gaussian(gaussian_map); root_width = DisplayWidth(dpy, scr); @@ -3509,16 +3498,16 @@ main(int argc, char **argv) { // Generates another Picture for shadows if the color is modified by // user - if (!options.shadow_red && !options.shadow_green && !options.shadow_blue) { + if (!opts.shadow_red && !opts.shadow_green && !opts.shadow_blue) { cshadow_picture = black_picture; } else { cshadow_picture = solid_picture(dpy, True, 1, - options.shadow_red, options.shadow_green, options.shadow_blue); + opts.shadow_red, opts.shadow_green, opts.shadow_blue); } - // Generates a picture for options.inactive_dim - if (options.inactive_dim) { - dim_picture = solid_picture(dpy, True, options.inactive_dim, 0, 0, 0); + // Generates a picture for inactive_dim + if (opts.inactive_dim) { + dim_picture = solid_picture(dpy, True, opts.inactive_dim, 0, 0, 0); } all_damage = None; @@ -3542,7 +3531,7 @@ main(int argc, char **argv) { XFree(children); - if (options.track_focus) { + if (opts.track_focus) { recheck_focus(dpy); } diff --git a/compton.h b/compton.h index 922f02282..77166a666 100644 --- a/compton.h +++ b/compton.h @@ -242,22 +242,38 @@ typedef struct _win { } win; typedef struct _options { + // General char *display; + /// Whether to try to detect WM windows and mark them as focused. + Bool mark_wmwin_focused; + /// Whether to fork to background. + Bool fork_after_register; + Bool synchronize; + + // Shadow + Bool wintype_shadow[NUM_WINTYPES]; + /// Red, green and blue tone of the shadow. + double shadow_red, shadow_green, shadow_blue; int shadow_radius; - int shadow_offset_x; - int shadow_offset_y; + int shadow_offset_x, shadow_offset_y; double shadow_opacity; + Bool clear_shadow; + /// Shadow blacklist. A linked list of conditions. + wincond *shadow_blacklist; + // Fading + Bool wintype_fade[NUM_WINTYPES]; /// How much to fade in in a single fading step. opacity_t fade_in_step; /// How much to fade out in a single fading step. opacity_t fade_out_step; unsigned long fade_delta; - unsigned long fade_time; Bool fade_trans; + /// Fading blacklist. A linked list of conditions. + wincond *fade_blacklist; - Bool clear_shadow; - + // Opacity + double wintype_opacity[NUM_WINTYPES]; /// Default opacity for inactive windows. /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for /// not enabled, default. @@ -269,35 +285,19 @@ typedef struct _options { /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; - /// Whether to try to detect WM windows and mark them as focused. - double mark_wmwin_focused; - + // Calculated /// Whether compton needs to track focus changes. Bool track_focus; /// Whether compton needs to track window name and class. Bool track_wdata; - /// Shadow blacklist. A linked list of conditions. - wincond *shadow_blacklist; - /// Fading blacklist. A linked list of conditions. - wincond *fade_blacklist; - - /// Whether to fork to background. - Bool fork_after_register; - /// Red, green and blue tone of the shadow. - double shadow_red; - double shadow_green; - double shadow_blue; - - Bool synchronize; +} options_t; - // Temporary options - int shadow_enable; - int fading_enable; +struct options_tmp { Bool no_dock_shadow; Bool no_dnd_shadow; double menu_opacity; -} options_t; +}; typedef struct _conv { int size; @@ -918,11 +918,19 @@ static void fork_after(void); #ifdef CONFIG_LIBCONFIG +static void +lcfg_lookup_bool(const config_t *config, const char *path, Bool *value) { + int ival; + + if (config_lookup_bool(config, path, &ival)) + *value = ival; +} + static FILE * open_config_file(char *cpath, char **path); static void -parse_config(char *cpath); +parse_config(char *cpath, struct options_tmp *pcfgtmp); #endif static void From 10ede8999852630fd375f3eb11e501f0e60eee4e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 26 Sep 2012 19:48:36 +0800 Subject: [PATCH 045/178] Bug fix: Issue #46: Optionally mark override-redirect windows as active Attempt to fix the transparency issue of Firefox and Chromium location bar dropdown window by marking override_redirect windows as active. This may not work completely and could have other side effects. Experimental. Enable by using --mark-ovredir-focused. --- compton.c | 17 +++++++++++++++-- compton.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compton.c b/compton.c index 0d1f30e7a..8294dd99d 100644 --- a/compton.c +++ b/compton.c @@ -125,6 +125,7 @@ static options_t opts = { .frame_opacity = 0.0, .inactive_dim = 0.0, .mark_wmwin_focused = False, + .mark_ovredir_focused = False, .shadow_blacklist = NULL, .fade_blacklist = NULL, .fork_after_register = False, @@ -1584,8 +1585,10 @@ map_win(Display *dpy, Window id, if (opts.track_focus) { recheck_focus(dpy); // Consider a window without client window a WM window and mark it - // focused if mark_wmwin_focused is on - if (opts.mark_wmwin_focused && !w->client_win) + // focused if mark_wmwin_focused is on, or it's over-redirected and + // mark_ovredir_focused is on + if ((opts.mark_wmwin_focused && !w->client_win) + || (opts.mark_ovredir_focused && w->a.override_redirect)) w->focused = True; } @@ -2880,6 +2883,8 @@ usage(void) { " Try to detect WM windows and mark them as active.\n" "--shadow-exclude condition\n" " Exclude conditions for shadows.\n" + "--mark-ovredir-focused\n" + " Mark over-redirect windows as active.\n" "\n" "Format of a condition:\n" "\n" @@ -3127,6 +3132,9 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { config_lookup_float(&cfg, "inactive-dim", &opts.inactive_dim); // --mark-wmwin-focused lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opts.mark_wmwin_focused); + // --mark-ovredir-focused + lcfg_lookup_bool(&cfg, "mark-ovredir-focused", + &opts.mark_ovredir_focused); // --shadow-exclude { config_setting_t *setting = @@ -3185,6 +3193,7 @@ get_cfg(int argc, char *const *argv) { { "inactive-dim", required_argument, NULL, 261 }, { "mark-wmwin-focused", no_argument, NULL, 262 }, { "shadow-exclude", required_argument, NULL, 263 }, + { "mark-ovredir-focused", no_argument, NULL, 264 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3319,6 +3328,10 @@ get_cfg(int argc, char *const *argv) { // --shadow-exclude condlst_add(&opts.shadow_blacklist, optarg); break; + case 264: + // --mark-ovredir-focused + opts.mark_ovredir_focused = True; + break; default: usage(); break; diff --git a/compton.h b/compton.h index 77166a666..f785981e9 100644 --- a/compton.h +++ b/compton.h @@ -246,6 +246,8 @@ typedef struct _options { char *display; /// Whether to try to detect WM windows and mark them as focused. Bool mark_wmwin_focused; + /// Whether to mark override-redirect windows as focused. + Bool mark_ovredir_focused; /// Whether to fork to background. Bool fork_after_register; Bool synchronize; From 280bc0fc324953e8d2cc14f13e282393a797e4e0 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 26 Sep 2012 21:40:48 +0800 Subject: [PATCH 046/178] Feature: --no-fading-openclose to partially simulate -F -F hasn't being working for long. This commit adds a switch --no-fading-openclose (and a configuration file option of the same name) to simulate the behavior when only -F is enabled, which disables fading when opening/closing windows, and makes -F an alias for -f. --- compton.c | 41 +++++++++++++++++++++++++++++++++-------- compton.h | 5 ++++- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/compton.c b/compton.c index 8294dd99d..0b6f2a05a 100644 --- a/compton.c +++ b/compton.c @@ -118,7 +118,7 @@ static options_t opts = { .fade_in_step = 0.028 * OPAQUE, .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, - .fade_trans = False, + .no_fading_openclose = False, .clear_shadow = False, .inactive_opacity = 0, .inactive_opacity_override = False, @@ -1594,14 +1594,23 @@ map_win(Display *dpy, Window id, // Window type change could affect shadow and fade determine_shadow(dpy, w); - determine_fade(dpy, w); // Determine mode here just in case the colormap changes determine_mode(dpy, w); // Fading in calc_opacity(dpy, w, True); - set_fade_callback(dpy, w, NULL, True); + + if (opts.no_fading_openclose) { + set_fade_callback(dpy, w, finish_map_win, True); + // Must be set after we execute the old fade callback, in case we + // receive two continuous MapNotify for the same window + w->fade = False; + } + else { + set_fade_callback(dpy, w, NULL, True); + determine_fade(dpy, w); + } calc_dim(dpy, w); @@ -1620,6 +1629,12 @@ map_win(Display *dpy, Window id, } } +static void +finish_map_win(Display *dpy, win *w) { + if (opts.no_fading_openclose) + determine_fade(dpy, w); +} + static void finish_unmap_win(Display *dpy, win *w) { w->damaged = 0; @@ -1658,6 +1673,8 @@ unmap_win(Display *dpy, Window id, Bool fade) { // Fading out w->opacity_tgt = 0; set_fade_callback(dpy, w, unmap_callback, False); + if (opts.no_fading_openclose) + w->fade = False; // don't care about properties anymore // Will get BadWindow if the window is destroyed @@ -2854,9 +2871,10 @@ usage(void) { "-z\n" " Zero the part of the shadow's mask behind the window (experimental).\n" "-f\n" - " Fade windows in/out when opening/closing.\n" + " Fade windows in/out when opening/closing and when opacity\n" + " changes, unless --no-fading-openclose is used.\n" "-F\n" - " Fade windows during opacity changes.\n" + " Equals -f. Deprecated.\n" "-i opacity\n" " Opacity of inactive windows. (0.1 - 1.0)\n" "-e opacity\n" @@ -2885,6 +2903,8 @@ usage(void) { " Exclude conditions for shadows.\n" "--mark-ovredir-focused\n" " Mark over-redirect windows as active.\n" + "--no-fading-openclose\n" + " Do not fade on window open/close.\n" "\n" "Format of a condition:\n" "\n" @@ -3119,6 +3139,8 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { // -f (fading_enable) if (config_lookup_bool(&cfg, "fading", &ival) && ival) wintype_arr_enable(opts.wintype_fade); + // --no-fading-open-close + lcfg_lookup_bool(&cfg, "no-fading-openclose", &opts.no_fading_openclose); // --shadow-red config_lookup_float(&cfg, "shadow-red", &opts.shadow_red); // --shadow-green @@ -3194,6 +3216,7 @@ get_cfg(int argc, char *const *argv) { { "mark-wmwin-focused", no_argument, NULL, 262 }, { "shadow-exclude", required_argument, NULL, 263 }, { "mark-ovredir-focused", no_argument, NULL, 264 }, + { "no-fading-openclose", no_argument, NULL, 265 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3258,10 +3281,8 @@ get_cfg(int argc, char *const *argv) { cfgtmp.menu_opacity = atof(optarg); break; case 'f': - fading_enable = True; - break; case 'F': - opts.fade_trans = True; + fading_enable = True; break; case 'S': opts.synchronize = True; @@ -3332,6 +3353,10 @@ get_cfg(int argc, char *const *argv) { // --mark-ovredir-focused opts.mark_ovredir_focused = True; break; + case 265: + // --no-fading-openclose + opts.no_fading_openclose = True; + break; default: usage(); break; diff --git a/compton.h b/compton.h index f785981e9..0c6457c62 100644 --- a/compton.h +++ b/compton.h @@ -270,7 +270,7 @@ typedef struct _options { /// How much to fade out in a single fading step. opacity_t fade_out_step; unsigned long fade_delta; - Bool fade_trans; + Bool no_fading_openclose; /// Fading blacklist. A linked list of conditions. wincond *fade_blacklist; @@ -725,6 +725,9 @@ map_win(Display *dpy, Window id, unsigned long sequence, Bool fade, Bool override_redirect); +static void +finish_map_win(Display *dpy, win *w); + static void finish_unmap_win(Display *dpy, win *w); From 4deb30a903f7be941f0aacab25f2ec285e815325 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 27 Sep 2012 22:30:16 +0800 Subject: [PATCH 047/178] Bug fix: Issue #47: Locale cause problems with float arguments A locale using comma instead of dot in floating point values causes unexpected behavior when parsing floating point commandline arguments. This commits enforces LC_NUMERIC="C" during commandline argument parsing. --- compton.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compton.c b/compton.c index 0b6f2a05a..b9c3e96a1 100644 --- a/compton.c +++ b/compton.c @@ -3229,6 +3229,7 @@ get_cfg(int argc, char *const *argv) { Bool shadow_enable = False, fading_enable = False; int o, longopt_idx, i; char *config_file = NULL; + char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); for (i = 0; i < NUM_WINTYPES; ++i) { opts.wintype_fade[i] = False; @@ -3251,6 +3252,11 @@ get_cfg(int argc, char *const *argv) { #endif // Parse commandline arguments. Range checking will be done later. + + // Enforce LC_NUMERIC locale "C" here to make sure dots are recognized + // instead of commas in atof(). + setlocale(LC_NUMERIC, "C"); + optind = 1; while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { @@ -3363,6 +3369,10 @@ get_cfg(int argc, char *const *argv) { } } + // Restore LC_NUMERIC + setlocale(LC_NUMERIC, lc_numeric_old); + free(lc_numeric_old); + // Range checking and option assignments opts.fade_delta = max_i(opts.fade_delta, 1); opts.shadow_radius = max_i(opts.shadow_radius, 1); From debc0035cd5cc315e6fe5941e9b36e1eb3548f5f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 28 Sep 2012 09:10:34 +0800 Subject: [PATCH 048/178] Bug fix: #48: Compilation failure with old libconfig/libpcre - Fix compilation failure with + +// For compatiblity with Date: Sat, 29 Sep 2012 13:15:09 +0800 Subject: [PATCH 049/178] Improvement: Split shadow_pict to shadow_pict & shadow_alpha_pict Split w->shadow_pict to w->shadow_pict and w->shadow_alpha_pict, so that the whole w->shadow_pict need not to be rebuild on shadow opacity change. This greatly reduces CPU usage of compton when a window with shadow is fading. (My test shows the CPU usage of compton process dropped from 1.15% to 0.35% when constantly fading in and out a window.) It uses a rather painful and slow method in shadow_picture() to get around the limitation of PictStandardA8 to make colored shadows work. I wonder if there's a better approach. - Merge variables gsize and cgsize as they seemingly represent the same thing. --- compton.c | 98 +++++++++++++++++++++++++++++++++---------------------- compton.h | 5 +-- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/compton.c b/compton.c index d4621803b..7beafd4f5 100644 --- a/compton.c +++ b/compton.c @@ -384,11 +384,10 @@ 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 swidth = width + cgsize; + int sheight = height + cgsize; + int center = cgsize / 2; int x, y; unsigned char d; int x_diff; @@ -434,10 +433,10 @@ make_shadow(Display *dpy, double opacity, * corners */ - ylimit = gsize; + ylimit = cgsize; if (ylimit > sheight / 2) ylimit = (sheight + 1) / 2; - xlimit = gsize; + xlimit = cgsize; if (xlimit > swidth / 2) xlimit = (swidth + 1) / 2; for (y = 0; y < ylimit; y++) { @@ -460,7 +459,7 @@ make_shadow(Display *dpy, double opacity, * top/bottom */ - x_diff = swidth - (gsize * 2); + x_diff = swidth - (cgsize * 2); if (x_diff > 0 && ylimit > 0) { for (y = 0; y < ylimit; y++) { if (ylimit == cgsize) { @@ -469,8 +468,8 @@ make_shadow(Display *dpy, double opacity, 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); + memset(&data[y * swidth + cgsize], d, x_diff); + memset(&data[(sheight - y - 1) * swidth + cgsize], d, x_diff); } } @@ -485,7 +484,7 @@ make_shadow(Display *dpy, double opacity, d = sum_gaussian(gaussian_map, opacity, x - center, center, width, height); } - for (y = gsize; y < sheight - gsize; y++) { + for (y = cgsize; y < sheight - cgsize; y++) { data[y * swidth + x] = d; data[y * swidth + (swidth - x - 1)] = d; } @@ -512,48 +511,62 @@ make_shadow(Display *dpy, double opacity, static Picture shadow_picture(Display *dpy, double opacity, int width, int height) { - XImage *shadow_image; - Pixmap shadow_pixmap; - Picture shadow_picture; - GC gc; + XImage *shadow_image = NULL; + Pixmap shadow_pixmap = None, shadow_pixmap_argb = None; + Picture shadow_picture = None, shadow_picture_argb = None; + GC gc = None; shadow_image = make_shadow(dpy, opacity, width, height); - if (!shadow_image) return None; + if (!shadow_image) + return None; shadow_pixmap = XCreatePixmap(dpy, root, shadow_image->width, shadow_image->height, 8); + shadow_pixmap_argb = XCreatePixmap(dpy, root, + shadow_image->width, shadow_image->height, 32); - if (!shadow_pixmap) { - XDestroyImage(shadow_image); - return None; - } + if (!shadow_pixmap || !shadow_pixmap_argb) + goto shadow_picture_err; shadow_picture = XRenderCreatePicture(dpy, shadow_pixmap, XRenderFindStandardFormat(dpy, PictStandardA8), 0, 0); - - if (!shadow_picture) { - XDestroyImage(shadow_image); - XFreePixmap(dpy, shadow_pixmap); - return None; - } + shadow_picture_argb = XRenderCreatePicture(dpy, shadow_pixmap_argb, + XRenderFindStandardFormat(dpy, PictStandardARGB32), 0, 0); + if (!shadow_picture || !shadow_picture_argb) + goto shadow_picture_err; gc = XCreateGC(dpy, shadow_pixmap, 0, 0); - if (!gc) { - XDestroyImage(shadow_image); - XFreePixmap(dpy, shadow_pixmap); - XRenderFreePicture(dpy, shadow_picture); - return None; - } + if (!gc) + goto shadow_picture_err; - XPutImage( - dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, + XPutImage(dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, shadow_image->width, shadow_image->height); + XRenderComposite(dpy, PictOpSrc, cshadow_picture, shadow_picture, + shadow_picture_argb, 0, 0, 0, 0, 0, 0, + shadow_image->width, shadow_image->height); XFreeGC(dpy, gc); XDestroyImage(shadow_image); XFreePixmap(dpy, shadow_pixmap); + XFreePixmap(dpy, shadow_pixmap_argb); + XRenderFreePicture(dpy, shadow_picture); + + return shadow_picture_argb; - return shadow_picture; +shadow_picture_err: + if (shadow_image) + XDestroyImage(shadow_image); + if (shadow_pixmap) + XFreePixmap(dpy, shadow_pixmap); + if (shadow_pixmap_argb) + XFreePixmap(dpy, shadow_pixmap_argb); + if (shadow_picture) + XRenderFreePicture(dpy, shadow_picture); + if (shadow_picture_argb) + XRenderFreePicture(dpy, shadow_picture_argb); + if (gc) + XFreeGC(dpy, gc); + return None; } static Picture @@ -1276,12 +1289,18 @@ paint_preprocess(Display *dpy, win *list) { if (w->flags & WFLAG_SIZE_CHANGE) free_picture(dpy, &w->shadow_pict); + if (w->shadow && !w->shadow_pict) { + w->shadow_pict = shadow_picture(dpy, 1, + w->widthb, w->heightb); + } + + // Rebuild shadow_alpha_pict if necessary if (w->shadow - && (!w->shadow_pict + && (!w->shadow_alpha_pict || w->shadow_opacity != w->shadow_opacity_cur)) { - free_picture(dpy, &w->shadow_pict); - w->shadow_pict = shadow_picture(dpy, w->shadow_opacity, - w->widthb, w->heightb); + free_picture(dpy, &w->shadow_alpha_pict); + w->shadow_alpha_pict = solid_picture( + dpy, False, w->shadow_opacity, 0, 0, 0); w->shadow_opacity_cur = w->shadow_opacity; } @@ -1359,7 +1378,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { // Painting shadow if (w->shadow) { XRenderComposite( - dpy, PictOpOver, cshadow_picture, w->shadow_pict, + dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, root_buffer, 0, 0, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height); @@ -1942,6 +1961,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_opacity = 0.0; new->shadow_opacity_cur = 0.0; new->shadow_pict = None; + new->shadow_alpha_pict = None; new->shadow_dx = 0; new->shadow_dy = 0; new->shadow_width = 0; diff --git a/compton.h b/compton.h index 51118e8db..f3c32365d 100644 --- a/compton.h +++ b/compton.h @@ -228,9 +228,10 @@ typedef struct _win { int shadow_width; /// Height of shadow. Affected by window size and commandline argument. int shadow_height; - /// Alpha mask Picture to render shadow. Affected by window size and - /// shadow opacity. + /// Picture to render shadow. Affected by window size. Picture shadow_pict; + /// Alpha mask Picture to render shadow. Affected by shadow opacity. + Picture shadow_alpha_pict; // Dim-related members /// Whether the window is to be dimmed. From ec29300e2161eb5441871dce7ec7eadcb507dc5e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 29 Sep 2012 22:53:57 +0800 Subject: [PATCH 050/178] Improvement: Wait infinitely for events when idling Wait infinitely for events when idling, instead of always calling paint_preprocess(), to reduce CPU usage in the case. Thanks to valr for help. --- compton.c | 20 ++++++++++++++++++-- compton.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compton.c b/compton.c index 7beafd4f5..9e6d45b27 100644 --- a/compton.c +++ b/compton.c @@ -51,6 +51,9 @@ XserverRegion all_damage; Bool has_name_pixmap; #endif int root_height, root_width; +/// Whether the program is idling. I.e. no fading, no potential window +/// changes. +Bool idling; /* errors */ ignore *ignore_head = NULL, **ignore_tail = &ignore_head; @@ -207,6 +210,9 @@ run_fade(Display *dpy, win *w, unsigned steps) { w->fade_fin = True; return; } + else { + idling = False; + } w->fade_fin = False; } @@ -224,8 +230,12 @@ set_fade_callback(Display *dpy, win *w, w->fade_callback = callback; // Must be the last line as the callback could destroy w! - if (exec_callback && old_callback) + if (exec_callback && old_callback) { old_callback(dpy, w); + // Although currently no callback function affects window state on + // next paint, it could, in the future + idling = False; + } } /** @@ -3613,10 +3623,13 @@ main(int argc, char **argv) { t = paint_preprocess(dpy, list); paint_all(dpy, None, t); + // Initialize idling + idling = False; + for (;;) { do { if (!QLength(dpy)) { - if (poll(&ufd, 1, fade_timeout()) == 0) { + if (poll(&ufd, 1, (idling ? -1: fade_timeout())) == 0) { break; } } @@ -3625,6 +3638,9 @@ main(int argc, char **argv) { ev_handle((XEvent *)&ev); } while (QLength(dpy)); + // idling will be turned off during paint_preprocess() if needed + idling = True; + t = paint_preprocess(dpy, list); if (all_damage) { static int paint; diff --git a/compton.h b/compton.h index f3c32365d..3fd4541a4 100644 --- a/compton.h +++ b/compton.h @@ -321,6 +321,7 @@ typedef enum { extern int root_height, root_width; extern Atom atom_client_attr; +extern Bool idling; /** * Functions From 17cf40e5633a017ee346a7f2a05a28ddc5b22678 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 1 Oct 2012 10:34:40 +0800 Subject: [PATCH 051/178] Feature: #27: Detect shaped windows and disable shadow on them - Optionally detect shaped windows using X Shape extension and disable shadow on them with --shadow-ignore-shaped. - Some windows are bounding-shaped just to support rounded corners, like Chromium windows (when system titlebar is disabled in its settings). Add --detect-rounded-corners to treat them as non-shaped windows (thus enable shadow on them). The algorithm I use is not perfect and wrong detection results are pretty possible to appear. - Many windows don't use X Shape extensions to add shapes but use ARGB background instead. These windows could only be blacklisted with --shadow-blacklist. - Rename a few functions. Code clean up. --- compton.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++-------- compton.h | 128 ++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 192 insertions(+), 64 deletions(-) diff --git a/compton.c b/compton.c index 9e6d45b27..bedf0a7aa 100644 --- a/compton.c +++ b/compton.c @@ -114,31 +114,37 @@ unsigned long fade_time; static options_t opts = { .display = NULL, + .mark_wmwin_focused = False, + .mark_ovredir_focused = False, + .fork_after_register = False, + .synchronize = False, + .detect_rounded_corners = False, + + .wintype_shadow = { False }, + .shadow_red = 0.0, + .shadow_green = 0.0, + .shadow_blue = 0.0, .shadow_radius = 12, .shadow_offset_x = -15, .shadow_offset_y = -15, .shadow_opacity = .75, + .clear_shadow = False, + .shadow_blacklist = NULL, + .shadow_ignore_shaped = False, + + .wintype_fade = { False }, .fade_in_step = 0.028 * OPAQUE, .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, .no_fading_openclose = False, - .clear_shadow = False, + .fade_blacklist = NULL, + + .wintype_opacity = { 0.0 }, .inactive_opacity = 0, .inactive_opacity_override = False, .frame_opacity = 0.0, .inactive_dim = 0.0, - .mark_wmwin_focused = False, - .mark_ovredir_focused = False, - .shadow_blacklist = NULL, - .fade_blacklist = NULL, - .fork_after_register = False, - .shadow_red = 0.0, - .shadow_green = 0.0, - .shadow_blue = 0.0, - .wintype_opacity = { 0.0 }, - .wintype_shadow = { False }, - .wintype_fade = { False }, - .synchronize = False, + .track_focus = False, .track_wdata = False, }; @@ -655,6 +661,48 @@ should_ignore(Display *dpy, unsigned long sequence) { * Windows */ +/** + * Check if a window has rounded corners. + */ +static void +win_rounded_corners(Display *dpy, win *w) { + if (!w->bounding_shaped) + return; + + // Fetch its bounding region + if (!w->border_size) + w->border_size = border_size(dpy, w); + + // Quit if border_size() returns None + if (!w->border_size) + return; + + // Determine the minimum width/height of a rectangle that could mark + // a window as having rounded corners + unsigned short minwidth = max_i(w->widthb * (1 - ROUNDED_PERCENT), + w->widthb - ROUNDED_PIXELS); + unsigned short minheight = max_i(w->heightb * (1 - ROUNDED_PERCENT), + w->heightb - ROUNDED_PIXELS); + + // Get the rectangles in the bounding region + int nrects = 0, i; + XRectangle *rects = XFixesFetchRegion(dpy, w->border_size, &nrects); + if (!rects) + return; + + // Look for a rectangle large enough for this window be considered + // having rounded corners + for (i = 0; i < nrects; ++i) + if (rects[i].width >= minwidth && rects[i].height >= minheight) { + w->rounded_corners = True; + XFree(rects); + return; + } + + w->rounded_corners = False; + XFree(rects); +} + /** * Match a window against a single window condition. * @@ -1133,7 +1181,7 @@ border_size(Display *dpy, win *w) { static Window find_client_win(Display *dpy, Window w) { - if (win_has_attr(dpy, w, client_atom)) { + if (wid_has_attr(dpy, w, client_atom)) { return w; } @@ -1142,7 +1190,7 @@ find_client_win(Display *dpy, Window w) { unsigned int i; Window ret = 0; - if (!win_get_children(dpy, w, &children, &nchildren)) { + if (!wid_get_children(dpy, w, &children, &nchildren)) { return 0; } @@ -1539,7 +1587,7 @@ determine_wintype(Display *dpy, Window w) { type = get_wintype_prop(dpy, w); if (type != WINTYPE_UNKNOWN) return type; - if (!win_get_children(dpy, w, &children, &nchildren)) + if (!wid_get_children(dpy, w, &children, &nchildren)) return WINTYPE_UNKNOWN; for (i = 0; i < nchildren; i++) { @@ -1599,6 +1647,13 @@ map_win(Display *dpy, Window id, w->id, WINTYPES[w->window_type]); #endif + // Detect if the window is shaped or has rounded corners + if (opts.shadow_ignore_shaped) { + w->bounding_shaped = wid_bounding_shaped(dpy, w->id); + if (w->bounding_shaped && opts.detect_rounded_corners) + win_rounded_corners(dpy, w); + } + // Get window name and class if we are tracking them if (opts.track_wdata) { win_get_name(dpy, w); @@ -1621,7 +1676,8 @@ map_win(Display *dpy, Window id, w->focused = True; } - // Window type change could affect shadow and fade + // Window type change and bounding shape state change could affect + // shadow determine_shadow(dpy, w); // Determine mode here just in case the colormap changes @@ -1630,6 +1686,7 @@ map_win(Display *dpy, Window id, // Fading in calc_opacity(dpy, w, True); + // Set fading state if (opts.no_fading_openclose) { set_fade_callback(dpy, w, finish_map_win, True); // Must be set after we execute the old fade callback, in case we @@ -1849,7 +1906,9 @@ determine_shadow(Display *dpy, win *w) { Bool shadow_old = w->shadow; w->shadow = (opts.wintype_shadow[w->window_type] - && !win_match(w, opts.shadow_blacklist, &w->cache_sblst)); + && !win_match(w, opts.shadow_blacklist, &w->cache_sblst) + && !(opts.shadow_ignore_shaped && w->bounding_shaped + && !w->rounded_corners)); // Window extents need update on shadow state change if (w->shadow != shadow_old) { @@ -1964,6 +2023,8 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->class_general = NULL; new->cache_sblst = NULL; new->cache_fblst = NULL; + new->bounding_shaped = False; + new->rounded_corners = False; new->border_size = None; new->extents = None; @@ -2782,6 +2843,16 @@ ev_shape_notify(XShapeEvent *ev) { // Mark the new border_size as damaged add_damage(dpy, copy_region(dpy, w->border_size)); } + + // Redo bounding shape detection and rounded corner detection + if (opts.shadow_ignore_shaped) { + w->bounding_shaped = wid_bounding_shaped(dpy, w->id); + if (w->bounding_shaped && opts.detect_rounded_corners) + win_rounded_corners(dpy, w); + + // Shadow state could be changed + determine_shadow(dpy, w); + } } inline static void @@ -2935,6 +3006,11 @@ usage(void) { " Mark over-redirect windows as active.\n" "--no-fading-openclose\n" " Do not fade on window open/close.\n" + "--shadow-ignore-shaped\n" + " Do not paint shadows on shaped windows.\n" + "--detect-rounded-corners\n" + " Try to detect windows with rounded corners and don't consider\n" + " them shaped windows.\n" "\n" "Format of a condition:\n" "\n" @@ -3189,6 +3265,12 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { // --mark-ovredir-focused lcfg_lookup_bool(&cfg, "mark-ovredir-focused", &opts.mark_ovredir_focused); + // --shadow-ignore-shaped + lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", + &opts.shadow_ignore_shaped); + // --detect-rounded-corners + lcfg_lookup_bool(&cfg, "detect-rounded-corners", + &opts.detect_rounded_corners); // --shadow-exclude { config_setting_t *setting = @@ -3249,6 +3331,8 @@ get_cfg(int argc, char *const *argv) { { "shadow-exclude", required_argument, NULL, 263 }, { "mark-ovredir-focused", no_argument, NULL, 264 }, { "no-fading-openclose", no_argument, NULL, 265 }, + { "shadow-ignore-shaped", no_argument, NULL, 266 }, + { "detect-rounded-corners", no_argument, NULL, 267 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3395,6 +3479,14 @@ get_cfg(int argc, char *const *argv) { // --no-fading-openclose opts.no_fading_openclose = True; break; + case 266: + // --shadow-ignore-shaped + opts.shadow_ignore_shaped = True; + break; + case 267: + // --detect-rounded-corners + opts.detect_rounded_corners = True; + break; default: usage(); break; diff --git a/compton.h b/compton.h index 3fd4541a4..83b78769e 100644 --- a/compton.h +++ b/compton.h @@ -75,6 +75,9 @@ #define HAS_NAME_WINDOW_PIXMAP 1 #endif +#define ROUNDED_PERCENT 0.05 +#define ROUNDED_PIXELS 10 + // For printing timestamps #include extern struct timeval time_start; @@ -174,6 +177,10 @@ typedef struct _win { Bool destroyed; /// Cached width/height of the window including border. int widthb, heightb; + /// Whether the window is bounding-shaped. + Bool bounding_shaped; + /// Whether the window just have rounded corners. + Bool rounded_corners; // Blacklist related members char *name; @@ -257,6 +264,9 @@ typedef struct _options { Bool mark_ovredir_focused; /// Whether to fork to background. Bool fork_after_register; + /// Whether to detect rounded corners. + Bool detect_rounded_corners; + /// Whether to work under synchronized mode for debugging. Bool synchronize; // Shadow @@ -269,6 +279,8 @@ typedef struct _options { Bool clear_shadow; /// Shadow blacklist. A linked list of conditions. wincond *shadow_blacklist; + /// Whether bounding-shaped window should be ignored. + Bool shadow_ignore_shaped; // Fading Bool wintype_fade[NUM_WINTYPES]; @@ -322,6 +334,7 @@ typedef enum { extern int root_height, root_width; extern Atom atom_client_attr; extern Bool idling; +extern Bool shape_exists; /** * Functions @@ -563,52 +576,6 @@ free_damage(Display *dpy, Damage *p) { } } -/** - * Determine if a window has a specific attribute. - * - * @param dpy Display to use - * @param w window to check - * @param atom atom of attribute to check - * @return 1 if it has the attribute, 0 otherwise - */ -static inline Bool -win_has_attr(Display *dpy, Window w, Atom atom) { - Atom type = None; - int format; - unsigned long nitems, after; - unsigned char *data; - - if (Success == XGetWindowProperty(dpy, w, atom, 0, 0, False, - AnyPropertyType, &type, &format, &nitems, &after, &data)) { - XFree(data); - if (type) return True; - } - - return False; -} - -/** - * Get the children of a window. - * - * @param dpy Display to use - * @param w window to check - * @param children [out] an array of child window IDs - * @param nchildren [out] number of children - * @return 1 if successful, 0 otherwise - */ -static inline Bool -win_get_children(Display *dpy, Window w, - Window **children, unsigned *nchildren) { - Window troot, tparent; - - if (!XQueryTree(dpy, w, &troot, &tparent, children, nchildren)) { - *nchildren = 0; - return False; - } - - return True; -} - static unsigned long get_time_in_milliseconds(void); @@ -668,6 +635,75 @@ static inline bool is_normal_win(const win *w) { || WINTYPE_UNKNOWN == w->window_type); } +/** + * Determine if a window has a specific attribute. + * + * @param dpy Display to use + * @param w window to check + * @param atom atom of attribute to check + * @return 1 if it has the attribute, 0 otherwise + */ +static inline Bool +wid_has_attr(Display *dpy, Window w, Atom atom) { + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + + if (Success == XGetWindowProperty(dpy, w, atom, 0, 0, False, + AnyPropertyType, &type, &format, &nitems, &after, &data)) { + XFree(data); + if (type) return True; + } + + return False; +} + +/** + * Get the children of a window. + * + * @param dpy Display to use + * @param w window to check + * @param children [out] an array of child window IDs + * @param nchildren [out] number of children + * @return 1 if successful, 0 otherwise + */ +static inline Bool +wid_get_children(Display *dpy, Window w, + Window **children, unsigned *nchildren) { + Window troot, tparent; + + if (!XQueryTree(dpy, w, &troot, &tparent, children, nchildren)) { + *nchildren = 0; + return False; + } + + return True; +} + +/** + * Check if a window is bounding-shaped. + */ +static inline Bool +wid_bounding_shaped(Display *dpy, Window wid) { + if (shape_exists) { + Bool bounding_shaped = False; + Bool clip_shaped; + int x_bounding, y_bounding, x_clip, y_clip; + unsigned int w_bounding, h_bounding, w_clip, h_clip; + + XShapeQueryExtents(dpy, wid, &bounding_shaped, + &x_bounding, &y_bounding, &w_bounding, &h_bounding, + &clip_shaped, &x_clip, &y_clip, &w_clip, &h_clip); + return bounding_shaped; + } + + return False; +} + +static void +win_rounded_corners(Display *dpy, win *w); + static bool win_match_once(win *w, const wincond *cond); From 11e27d354bbf248226e22e31f2c69a9bb8f5666c Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 2 Oct 2012 21:46:37 +0800 Subject: [PATCH 052/178] Improvement: Change in client window lookup & wintype detection - Revert to WM_STATE for client window lookup, as WM_CLASS is unreliable, either, but set the client window of an override-redirect window to itself. - Conform to EWMH to make assumptions about window types when _NET_WM_WINDOW_TYPE is not available. - Remove determine_wintype() and completely rely on client window for window type detection. --- compton.c | 57 ++++++++++++++++++++++++++----------------------------- compton.h | 6 +----- 2 files changed, 28 insertions(+), 35 deletions(-) diff --git a/compton.c b/compton.c index bb9d7bb8f..80f19aeb8 100644 --- a/compton.c +++ b/compton.c @@ -95,6 +95,7 @@ Atom client_atom; Atom name_atom; Atom name_ewmh_atom; Atom class_atom; +Atom transient_atom; Atom win_type_atom; Atom win_type[NUM_WINTYPES]; @@ -1578,30 +1579,6 @@ get_wintype_prop(Display *dpy, Window wid) { return WINTYPE_UNKNOWN; } -static wintype -determine_wintype(Display *dpy, Window w) { - Window *children = NULL; - unsigned int nchildren, i; - wintype type; - - type = get_wintype_prop(dpy, w); - if (type != WINTYPE_UNKNOWN) return type; - - if (!wid_get_children(dpy, w, &children, &nchildren)) - return WINTYPE_UNKNOWN; - - for (i = 0; i < nchildren; i++) { - type = determine_wintype(dpy, children[i]); - if (type != WINTYPE_UNKNOWN) return type; - } - - if (children) { - XFree((void *)children); - } - - return WINTYPE_UNKNOWN; -} - static void map_win(Display *dpy, Window id, unsigned long sequence, Bool fade, @@ -1625,10 +1602,19 @@ map_win(Display *dpy, Window id, // Detect client window here instead of in add_win() as the client // window should have been prepared at this point if (!w->client_win) { - Window cw = find_client_win(dpy, w->id); + Window cw = 0; + if (!w->a.override_redirect) { + cw = find_client_win(dpy, w->id); +#ifdef DEBUG_CLIENTWIN + printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); +#endif + } + else { + cw = w->id; #ifdef DEBUG_CLIENTWIN - printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); + printf("find_client_win(%#010lx): client self (override-redirected)\n", w->id); #endif + } if (cw) { mark_client_win(dpy, w, cw); } @@ -1639,9 +1625,6 @@ map_win(Display *dpy, Window id, get_frame_extents(dpy, w, w->client_win); } - if (WINTYPE_UNKNOWN == w->window_type) - w->window_type = determine_wintype(dpy, w->id); - #ifdef DEBUG_WINTYPE printf("map_win(%#010lx): type %s\n", w->id, WINTYPES[w->window_type]); @@ -1968,8 +1951,21 @@ mark_client_win(Display *dpy, win *w, Window client) { get_frame_extents(dpy, w, client); } XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); + + // Detect window type here if (WINTYPE_UNKNOWN == w->window_type) w->window_type = get_wintype_prop(dpy, w->client_win); + + // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take + // override-redirect windows or windows without WM_TRANSIENT_FOR as + // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. + if (WINTYPE_UNKNOWN == w->window_type) { + if (w->a.override_redirect + || !wid_has_attr(dpy, client, transient_atom)) + w->window_type = WINTYPE_NORMAL; + else + w->window_type = WINTYPE_DIALOG; + } } static void @@ -3541,10 +3537,11 @@ get_atoms(void) { extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); - client_atom = XA_WM_CLASS; + client_atom = XInternAtom(dpy, "WM_STATE", False); name_atom = XA_WM_NAME; name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); class_atom = XA_WM_CLASS; + transient_atom = XA_WM_TRANSIENT_FOR; win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); diff --git a/compton.h b/compton.h index 83b78769e..7a71fa808 100644 --- a/compton.h +++ b/compton.h @@ -631,8 +631,7 @@ solid_picture(Display *dpy, Bool argb, double a, static inline bool is_normal_win(const win *w) { return (WINTYPE_NORMAL == w->window_type - || WINTYPE_UTILITY == w->window_type - || WINTYPE_UNKNOWN == w->window_type); + || WINTYPE_UTILITY == w->window_type); } /** @@ -761,9 +760,6 @@ repair_win(Display *dpy, win *w); static wintype get_wintype_prop(Display * dpy, Window w); -static wintype -determine_wintype(Display *dpy, Window w); - static void map_win(Display *dpy, Window id, unsigned long sequence, Bool fade, From 3ef937670802ae09662065e23f34355f828bb52b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 3 Oct 2012 21:13:34 +0800 Subject: [PATCH 053/178] Improvement: Support reading _NET_WM_OPACITY from client windows - Some WMs don't respect Keith Packard's proposal of _NET_WM_WINDOW_OPACITY, and do not copy _NET_WM_OPACITY attribute of a client window to its frame windows, thus cause opacity set by non-override-redirect windows to have no effect. This commit adds support for reading _NET_WM_OPACITY from client windows if running with --detect-client-opacity. Thanks to pvanek for reporting. - Change Makefile logic to determine options from 3 variables (NO_LIBCONFIG, NO_REGEX_PCRE, NO_REGEX_PCRE_JIT) instead of CFG to ensure compatibility when we add new options. CFG variable is no longer been respected. --- compton.c | 52 +++++++++++++++++++++++++++++++++++++++------------- compton.h | 11 ++++++++++- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/compton.c b/compton.c index 80f19aeb8..a64b0b7b2 100644 --- a/compton.c +++ b/compton.c @@ -142,6 +142,7 @@ static options_t opts = { .inactive_opacity = 0, .inactive_opacity_override = False, .frame_opacity = 0.0, + .detect_client_opacity = False, .inactive_dim = 0.0, .track_focus = False, @@ -942,7 +943,8 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { } if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) { - if (opts.frame_opacity || opts.track_wdata) + if (opts.frame_opacity || opts.track_wdata + || opts.detect_client_opacity) evmask |= PropertyChangeMask; } @@ -1757,14 +1759,14 @@ unmap_win(Display *dpy, Window id, Bool fade) { } static opacity_t -get_opacity_prop(Display *dpy, win *w, opacity_t def) { +wid_get_opacity_prop(Display *dpy, Window wid, opacity_t def) { Atom actual; int format; unsigned long n, left; unsigned char *data; int result = XGetWindowProperty( - dpy, w->id, opacity_atom, 0L, 1L, False, + dpy, wid, opacity_atom, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL) { @@ -1838,13 +1840,18 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { // Do not refetch the opacity window attribute unless necessary, this // is probably an expensive operation in some cases if (refetch_prop) { - w->opacity_prop = get_opacity_prop(dpy, w, OPAQUE); + w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); + if (!opts.detect_client_opacity || !w->client_win + || w->id == w->client_win) + w->opacity_prop_client = OPAQUE; + else + w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, + OPAQUE); } - if (OPAQUE == (opacity = w->opacity_prop)) { - if (1.0 != opts.wintype_opacity[w->window_type]) { - opacity = opts.wintype_opacity[w->window_type] * OPAQUE; - } + if (OPAQUE == (opacity = w->opacity_prop) + && OPAQUE == (opacity = w->opacity_prop_client)) { + opacity = opts.wintype_opacity[w->window_type] * OPAQUE; } // Respect inactive_opacity in some cases @@ -1945,12 +1952,13 @@ static void mark_client_win(Display *dpy, win *w, Window client) { w->client_win = client; + XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); + // Get the frame width and monitor further frame width changes on client // window if necessary if (opts.frame_opacity) { get_frame_extents(dpy, w, client); } - XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); // Detect window type here if (WINTYPE_UNKNOWN == w->window_type) @@ -2037,6 +2045,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->opacity_tgt = 0; new->opacity_cur = OPAQUE; new->opacity_prop = OPAQUE; + new->opacity_prop_client = OPAQUE; new->fade = False; new->fade_callback = NULL; new->fade_fin = False; @@ -2777,12 +2786,17 @@ ev_property_notify(XPropertyEvent *ev) { } } - /* check if Trans property was changed */ + // If _NET_WM_OPACITY changes if (ev->atom == opacity_atom) { - /* reset mode and redraw window */ - win *w = find_win(dpy, ev->window); + win *w = NULL; + if ((w = find_win(dpy, ev->window))) + w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); + else if (opts.detect_client_opacity + && (w = find_toplevel(dpy, ev->window))) + w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, + OPAQUE); if (w) { - calc_opacity(dpy, w, True); + calc_opacity(dpy, w, False); } } @@ -3007,6 +3021,10 @@ usage(void) { "--detect-rounded-corners\n" " Try to detect windows with rounded corners and don't consider\n" " them shaped windows.\n" + "--detect-client-opacity\n" + " Detect _NET_WM_OPACITY on client windows, useful for window\n" + " managers not passing _NET_WM_OPACITY of client windows to frame\n" + " windows.\n" "\n" "Format of a condition:\n" "\n" @@ -3267,6 +3285,9 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { // --detect-rounded-corners lcfg_lookup_bool(&cfg, "detect-rounded-corners", &opts.detect_rounded_corners); + // --detect-client-opacity + lcfg_lookup_bool(&cfg, "detect-client-opacity", + &opts.detect_client_opacity); // --shadow-exclude { config_setting_t *setting = @@ -3329,6 +3350,7 @@ get_cfg(int argc, char *const *argv) { { "no-fading-openclose", no_argument, NULL, 265 }, { "shadow-ignore-shaped", no_argument, NULL, 266 }, { "detect-rounded-corners", no_argument, NULL, 267 }, + { "detect-client-opacity", no_argument, NULL, 268 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3483,6 +3505,10 @@ get_cfg(int argc, char *const *argv) { // --detect-rounded-corners opts.detect_rounded_corners = True; break; + case 268: + // --detect-client-opacity + opts.detect_client_opacity = True; + break; default: usage(); break; diff --git a/compton.h b/compton.h index 7a71fa808..dca11b68a 100644 --- a/compton.h +++ b/compton.h @@ -198,6 +198,10 @@ typedef struct _win { opacity_t opacity_cur; /// Cached value of opacity window attribute. opacity_t opacity_prop; + /// Cached value of opacity window attribute on client window. For + /// broken window managers not transferring client window's + /// _NET_WM_OPACITY value + opacity_t opacity_prop_client; /// Alpha mask Picture to render window with opacity. Picture alpha_pict; @@ -302,7 +306,12 @@ typedef struct _options { /// Whether inactive_opacity overrides the opacity set by window /// attributes. Bool inactive_opacity_override; + /// Frame opacity. Relative to window opacity, also affects shadow + /// opacity. double frame_opacity; + /// Whether to detect _NET_WM_OPACITY on client windows. Used on window + /// managers that don't pass _NET_WM_OPACITY to frame windows. + Bool detect_client_opacity; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; @@ -780,7 +789,7 @@ static void unmap_win(Display *dpy, Window id, Bool fade); static opacity_t -get_opacity_prop(Display *dpy, win *w, opacity_t def); +wid_get_opacity_prop(Display *dpy, Window wid, opacity_t def); static double get_opacity_percent(Display *dpy, win *w); From ae5771e1cfad32f0c2a0a8ed59fb04867f43b5b2 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 8 Oct 2012 10:20:01 +0800 Subject: [PATCH 054/178] Feature: #7: VSync - Add VSync feature. 3 possible VSync methods available: "sw" (software, not too reliable, but at least you have something to fallback to), "drm" (using DRM_IOCTL_WAIT_VBLANK, should work only on DRI drivers), "opengl" (using SGI_swap_control extension OpenGL, might work on more drivers than the DRM method). "sw" and "opengl" are briefly tested, "drm" received utterly no test (because I use the nVidia binary blob). They are enabled with "--vsync sw" / "--vsync drm" / "--vsync opengl". - Add --refresh-rate to let user specify a refresh rate for software VSync, in case the automatic refresh rate detection does not work well. - Seemingly the automatic refresh rate detection using X RandR in software VSync detects refresh rate incorrectly. Need further investigation. - Fix a few bugs in fading timing. - Add a workaround for client window detection on Fluxbox, as Fluxbox (incorrectly?) sets the override-redirect flag upon all frame windows. - Software VSync adds dependency on librt (a part of glibc) for nanosecond-level timing functions, and libXrandr for automatic refresh rate detection; DRM VSync adds dependency on libdrm to use its drm.h, but does not link to libdrm; OpenGL VSync adds dependency on libGL. - Print timing information on DEBUG_REPAINT. --- compton.c | 545 +++++++++++++++++++++++++++++++++++++++++++++++++----- compton.h | 123 +++++++++++- 2 files changed, 622 insertions(+), 46 deletions(-) mode change 100644 => 100755 compton.c diff --git a/compton.c b/compton.c old mode 100644 new mode 100755 index a64b0b7b2..81199cd29 --- a/compton.c +++ b/compton.c @@ -51,21 +51,61 @@ XserverRegion all_damage; Bool has_name_pixmap; #endif int root_height, root_width; + /// Whether the program is idling. I.e. no fading, no potential window /// changes. Bool idling; +/// Window ID of the window we register as a symbol. +Window reg_win = 0; + +/// Currently used refresh rate. Used for Software VSync. +short refresh_rate = 0; +/// Interval between refresh in nanoseconds. Used for Software VSync. +unsigned long refresh_intv = 0; +/// Nanosecond-level offset of the first painting. +/// Used for Software VSync. +long paint_tm_offset = 0; + +#ifdef CONFIG_VSYNC_DRM +/// File descriptor of DRI device file. Used for DRM VSync. +int drm_fd = 0; +#endif + +#ifdef CONFIG_VSYNC_OPENGL +/// GLX context. +GLXContext glx_context; + +/// Pointer to glXGetVideoSyncSGI function. Used by OpenGL VSync. +f_GetVideoSync glx_get_video_sync = NULL; + +/// Pointer to glXWaitVideoSyncSGI function. Used by OpenGL VSync. +f_WaitVideoSync glx_wait_video_sync = NULL; +#endif /* errors */ ignore *ignore_head = NULL, **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; + /// Whether X Shape extension exists. -Bool shape_exists = True; +Bool shape_exists = False; /// Event base number and error base number for X Shape extension. int shape_event, shape_error; -int render_event, render_error; -int composite_opcode; + +/// Whether X RandR extension exists. +Bool randr_exists = False; +/// Event base number and error base number for X RandR extension. +int randr_event, randr_error; + +#ifdef CONFIG_VSYNC_OPENGL +/// Whether X GLX extension exists. +Bool glx_exists = False; +/// Event base number and error base number for X GLX extension. +int glx_event, glx_error; +#endif /* shadows */ conv *gaussian_map; @@ -119,6 +159,9 @@ static options_t opts = { .synchronize = False, .detect_rounded_corners = False, + .refresh_rate = 0, + .vsync = VSYNC_NONE, + .wintype_shadow = { False }, .shadow_red = 0.0, .shadow_green = 0.0, @@ -162,7 +205,7 @@ unsigned long fade_time = 0; * passed since the epoch. */ static unsigned long -get_time_in_milliseconds() { +get_time_ms() { struct timeval tv; gettimeofday(&tv, NULL); @@ -177,7 +220,7 @@ get_time_in_milliseconds() { */ static int fade_timeout(void) { - int diff = opts.fade_delta - get_time_in_milliseconds() + fade_time; + int diff = opts.fade_delta - get_time_ms() + fade_time; if (diff < 0) diff = 0; @@ -1243,13 +1286,11 @@ static win * paint_preprocess(Display *dpy, win *list) { win *w; win *t = NULL, *next = NULL; - // Sounds like the timeout in poll() frequently does not work - // accurately, asking it to wait to 20ms, and often it would wait for - // 19ms, so the step value has to be rounded. - unsigned steps = roundl((double) (get_time_in_milliseconds() - fade_time) / opts.fade_delta); - // Reset fade_time - fade_time = get_time_in_milliseconds(); + // Fading step calculation + unsigned steps = (sub_unslong(get_time_ms(), fade_time) + + FADE_DELTA_TOLERANCE * opts.fade_delta) / opts.fade_delta; + fade_time += steps * opts.fade_delta; for (w = list; w; w = next) { // In case calling the fade callback function destroys this window @@ -1411,6 +1452,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { paint_root(dpy); #ifdef DEBUG_REPAINT + print_timestamp(); printf("paint:"); #endif @@ -1605,13 +1647,15 @@ map_win(Display *dpy, Window id, // window should have been prepared at this point if (!w->client_win) { Window cw = 0; - if (!w->a.override_redirect) { - cw = find_client_win(dpy, w->id); + // Always recursively look for a window with WM_STATE, as Fluxbox + // sets override-redirect flags on all frame windows. + cw = find_client_win(dpy, w->id); #ifdef DEBUG_CLIENTWIN - printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); + printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); #endif - } - else { + // Set a window's client window to itself only if we didn't find a + // client window and the window has override-redirect flag + if (!cw && w->a.override_redirect) { cw = w->id; #ifdef DEBUG_CLIENTWIN printf("find_client_win(%#010lx): client self (override-redirected)\n", w->id); @@ -2792,7 +2836,7 @@ ev_property_notify(XPropertyEvent *ev) { if ((w = find_win(dpy, ev->window))) w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); else if (opts.detect_client_opacity - && (w = find_toplevel(dpy, ev->window))) + && (w = find_toplevel(dpy, ev->window))) w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, OPAQUE); if (w) { @@ -2865,7 +2909,22 @@ ev_shape_notify(XShapeEvent *ev) { } } -inline static void +/** + * Handle ScreenChangeNotify events from X RandR extension. + */ +static void +ev_screen_change_notify(XRRScreenChangeNotifyEvent *ev) { + if (!opts.refresh_rate) { + update_refresh_rate(dpy); + if (!refresh_rate) { + fprintf(stderr, "ev_screen_change_notify(): Refresh rate detection " + "failed, software VSync disabled."); + opts.vsync = VSYNC_NONE; + } + } +} + +static void ev_handle(XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) { discard_ignore(dpy, ev->xany.serial); @@ -2939,6 +2998,10 @@ ev_handle(XEvent *ev) { ev_shape_notify((XShapeEvent *) ev); break; } + if (randr_exists && ev->type == (randr_event + RRScreenChangeNotify)) { + ev_screen_change_notify((XRRScreenChangeNotifyEvent *) ev); + break; + } if (ev->type == damage_event + XDamageNotify) { ev_damage_notify((XDamageNotifyEvent *)ev); } @@ -2950,11 +3013,14 @@ ev_handle(XEvent *ev) { * Main */ +/** + * Print usage text and exit. + */ static void usage(void) { - fprintf(stderr, "compton (development version)\n"); - fprintf(stderr, "usage: compton [options]\n"); - fprintf(stderr, + fputs( + "compton (development version)\n" + "usage: compton [options]\n" "Options:\n" "\n" "-d display\n" @@ -3026,6 +3092,20 @@ usage(void) { " managers not passing _NET_WM_OPACITY of client windows to frame\n" " windows.\n" "\n" + "--refresh-rate val\n" + " Specify refresh rate of the screen. If not specified or 0, compton\n" + " will try detecting this with X RandR extension.\n" + "--vsync vsync-method\n" + " Set VSync method. There are 4 VSync methods currently available:\n" + " none = No VSync\n" + " sw = software VSync, basically limits compton to send a request\n" + " every 1 / refresh_rate second. Experimental.\n" + " drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n" + " drivers. Experimental.\n" + " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" + " work on some drivers. Experimental.\n" + " (Note some VSync methods may not be enabled at compile time.)\n" + "\n" "Format of a condition:\n" "\n" " condition = :[]:\n" @@ -3041,26 +3121,71 @@ usage(void) { " flag is \"i\" (ignore case).\n" "\n" " is the actual pattern string.\n" - ); + , stderr); exit(1); } +/** + * Register a window as symbol, and initialize GLX context if wanted. + */ static void -register_cm(int scr) { - Window w; +register_cm(Bool want_glxct) { Atom a; char *buf; int len, s; - if (scr < 0) return; - - w = XCreateSimpleWindow( - dpy, RootWindow(dpy, 0), - 0, 0, 1, 1, 0, None, None); +#ifdef CONFIG_VSYNC_OPENGL + // Create a window with the wanted GLX visual + if (want_glxct) { + Bool ret = False; + // Get visual for the window + int attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; + XVisualInfo *pvi = glXChooseVisual(dpy, scr, attribs); + if (!pvi) { + fprintf(stderr, "register_cm(): Failed to choose visual required " + "by fake OpenGL VSync window. OpenGL VSync turned off.\n"); + } + else { + // Create the window + XSetWindowAttributes swa = { + .colormap = XCreateColormap(dpy, root, pvi->visual, AllocNone), + .border_pixel = 0, + }; + + pvi->screen = scr; + reg_win = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, pvi->depth, + InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa); + if (!reg_win) + fprintf(stderr, "register_cm(): Failed to create window required " + "by fake OpenGL VSync. OpenGL VSync turned off.\n"); + else { + // Get GLX context + glx_context = glXCreateContext(dpy, pvi, None, GL_TRUE); + if (!glx_context) { + fprintf(stderr, "register_cm(): Failed to get GLX context. " + "OpenGL VSync turned off.\n"); + opts.vsync = VSYNC_NONE; + } + else { + // Attach GLX context + if (!(ret = glXMakeCurrent(dpy, reg_win, glx_context))) + fprintf(stderr, "register_cm(): Failed to attach GLX context." + " OpenGL VSync turned off.\n"); + } + } + } + if (!ret) + opts.vsync = VSYNC_NONE; + } +#endif + + if (!reg_win) + reg_win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, + None, None); Xutf8SetWMProperties( - dpy, w, "xcompmgr", "xcompmgr", + dpy, reg_win, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL, NULL); len = strlen(REGISTER_PROP) + 2; @@ -3077,7 +3202,7 @@ register_cm(int scr) { a = XInternAtom(dpy, buf, False); free(buf); - XSetSelectionOwner(dpy, a, w, 0); + XSetSelectionOwner(dpy, a, reg_win, 0); } static void @@ -3288,6 +3413,8 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { // --detect-client-opacity lcfg_lookup_bool(&cfg, "detect-client-opacity", &opts.detect_client_opacity); + // --refresh-rate + lcfg_lookup_int(&cfg, "refresh-rate", &opts.refresh_rate); // --shadow-exclude { config_setting_t *setting = @@ -3351,9 +3478,17 @@ get_cfg(int argc, char *const *argv) { { "shadow-ignore-shaped", no_argument, NULL, 266 }, { "detect-rounded-corners", no_argument, NULL, 267 }, { "detect-client-opacity", no_argument, NULL, 268 }, + { "refresh-rate", required_argument, NULL, 269 }, + { "vsync", required_argument, NULL, 270 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; + const static char * const vsync_str[] = { + "none", // VSYNC_NONE + "sw", // VSYNC_SW + "drm", // VSYNC_DRM + "opengl", // VSYNC_OPENGL + }; struct options_tmp cfgtmp = { .no_dock_shadow = False, @@ -3509,6 +3644,18 @@ get_cfg(int argc, char *const *argv) { // --detect-client-opacity opts.detect_client_opacity = True; break; + case 269: + // --refresh-rate + opts.refresh_rate = atoi(optarg); + break; + case 270: + // --vsync + for (vsync_t i = 0; i < sizeof(vsync_str) / sizeof(vsync_str[0]); ++i) + if (!strcasecmp(optarg, vsync_str[i])) { + opts.vsync = i; + break; + } + break; default: usage(); break; @@ -3529,6 +3676,7 @@ get_cfg(int argc, char *const *argv) { opts.frame_opacity = normalize_d(opts.frame_opacity); opts.shadow_opacity = normalize_d(opts.shadow_opacity); cfgtmp.menu_opacity = normalize_d(cfgtmp.menu_opacity); + opts.refresh_rate = normalize_i_range(opts.refresh_rate, 0, 300); if (OPAQUE == opts.inactive_opacity) { opts.inactive_opacity = 0; } @@ -3602,6 +3750,270 @@ get_atoms(void) { "_NET_WM_WINDOW_TYPE_DND", False); } +/** + * Update refresh rate info with X Randr extension. + */ +static void +update_refresh_rate(Display *dpy) { + XRRScreenConfiguration* randr_info; + + if (!(randr_info = XRRGetScreenInfo(dpy, root))) + return; + refresh_rate = XRRConfigCurrentRate(randr_info); + + XRRFreeScreenConfigInfo(randr_info); + + if (refresh_rate) + refresh_intv = NS_PER_SEC / refresh_rate; + else + refresh_intv = 0; +} + +/** + * Initialize software VSync. + * + * @return True for success, False otherwise + */ +static Bool +vsync_sw_init(void) { + // Prepare refresh rate + // Check if user provides one + refresh_rate = opts.refresh_rate; + if (refresh_rate) + refresh_intv = NS_PER_SEC / refresh_rate; + + // Auto-detect refresh rate otherwise + if (!refresh_rate && randr_exists) { + update_refresh_rate(dpy); + } + + // Turn off vsync_sw if we can't get the refresh rate + if (!refresh_rate) + return False; + + // Monitor screen changes only if vsync_sw is enabled and we are using + // an auto-detected refresh rate + if (randr_exists && !opts.refresh_rate) + XRRSelectInput(dpy, root, RRScreenChangeNotify); + + return True; +} + +/** + * Get current time in struct timespec. + * + * Note its starting time is unspecified. + */ +static inline struct timespec +get_time_timespec(void) { + struct timespec tm = { 0 }; + + clock_gettime(CLOCK_MONOTONIC, &tm); + + // Return a time of all 0 if the call fails + return tm; +} + +/** + * Get the smaller number that is bigger than dividend and is + * N times of divisor. + */ +static inline long +lceil_ntimes(long dividend, long divisor) { + // It's possible to use the more beautiful expression here: + // ret = ((dividend - 1) / divisor + 1) * divisor; + // But it does not work well for negative values. + long ret = dividend / divisor * divisor; + if (ret < dividend) + ret += divisor; + + return ret; +} + +/** + * Calculate time for which the program should wait for events if vsync_sw is + * enabled. + * + * @param timeout old timeout value, never negative! + * @return time to wait, in struct timespec + */ +static struct timespec +vsync_sw_ntimeout(int timeout) { + // Convert the old timeout to struct timespec + struct timespec next_paint_tmout = { + .tv_sec = timeout / MS_PER_SEC, + .tv_nsec = timeout % MS_PER_SEC * (NS_PER_SEC / MS_PER_SEC) + }; + // Get the nanosecond offset of the time when the we reach the timeout + // I don't think a 32-bit long could overflow here. + long target_relative_offset = (next_paint_tmout.tv_nsec + get_time_timespec().tv_nsec - paint_tm_offset) % NS_PER_SEC; + if (target_relative_offset < 0) + target_relative_offset += NS_PER_SEC; + + assert(target_relative_offset >= 0); + + // If the target time is sufficiently close to a VSync time, don't add + // an offset, to avoid certain blocking conditions. + if ((target_relative_offset % NS_PER_SEC) < VSYNC_SW_TOLERANCE) + return next_paint_tmout; + + // Add an offset so we wait until the next VSync after timeout + next_paint_tmout.tv_nsec += lceil_ntimes(target_relative_offset, refresh_intv) - target_relative_offset; + if (next_paint_tmout.tv_nsec > NS_PER_SEC) { + next_paint_tmout.tv_nsec -= NS_PER_SEC; + ++next_paint_tmout.tv_sec; + } + + return next_paint_tmout; +} + +/** + * Initialize DRM VSync. + * + * @return True for success, False otherwise + */ +static Bool +vsync_drm_init(void) { +#ifdef CONFIG_VSYNC_DRM + // Should we always open card0? + if ((drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { + fprintf(stderr, "vsync_drm_init(): Failed to open device.\n"); + return False; + } + + if (vsync_drm_wait()) + return False; + + return True; +#else + fprintf(stderr, "Program not compiled with DRM VSync support.\n"); + return False; +#endif +} + +#ifdef CONFIG_VSYNC_DRM +/** + * Wait for next VSync, DRM method. + * + * Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp + */ +static int +vsync_drm_wait(void) { + int ret = -1; + drm_wait_vblank_t vbl; + + vbl.request.type = _DRM_VBLANK_RELATIVE, + vbl.request.sequence = 1; + + do { + ret = ioctl(drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl); + vbl.request.type &= ~_DRM_VBLANK_RELATIVE; + } while (ret && errno == EINTR); + + if (ret) + fprintf(stderr, "vsync_drm_wait(): VBlank ioctl did not work, " + "unimplemented in this drmver?\n"); + + return ret; + +} +#endif + +/** + * Initialize OpenGL VSync. + * + * Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e + * Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html + * + * @return True for success, False otherwise + */ +static Bool +vsync_opengl_init(void) { +#ifdef CONFIG_VSYNC_OPENGL + // Get video sync functions + glx_get_video_sync = (f_GetVideoSync) + glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI"); + glx_wait_video_sync = (f_WaitVideoSync) + glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI"); + if (!glx_wait_video_sync || !glx_get_video_sync) { + fprintf(stderr, "vsync_opengl_init(): " + "Failed to get glXWait/GetVideoSyncSGI function.\n"); + return False; + } + + return True; +#else + fprintf(stderr, "Program not compiled with OpenGL VSync support.\n"); + return False; +#endif +} + +#ifdef CONFIG_VSYNC_OPENGL +/** + * Wait for next VSync, OpenGL method. + */ +static void +vsync_opengl_wait(void) { + unsigned vblank_count; + + glx_get_video_sync(&vblank_count); + glx_wait_video_sync(2, (vblank_count + 1) % 2, &vblank_count); + // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required? +} +#endif + +/** + * Wait for next vsync and timeout unless new events appear. + * + * @param fd struct pollfd used for poll() + * @param timeout second timeout (fading timeout) + * @return > 0 if we get some events, 0 if timeout is reached, < 0 on + * problems + */ +static Bool +vsync_wait(Display *dpy, struct pollfd *fd, int timeout) { + // Always wait infinitely if asked so, to minimize CPU usage + if (timeout < 0) { + int ret = poll(fd, 1, timeout); + // Reset fade_time so the fading steps during idling are not counted + fade_time = get_time_ms(); + return ret; + } + + if (VSYNC_NONE == opts.vsync) + return poll(fd, 1, timeout); + + // vsync_sw: Wait until the next sync right after next fading timeout + if (VSYNC_SW == opts.vsync) { + struct timespec new_tmout = vsync_sw_ntimeout(timeout); + // printf("ppoll(): %3ld:%09ld\n", new_tmout.tv_sec, new_tmout.tv_nsec); + return ppoll(fd, 1, &new_tmout, NULL); + } + +#ifdef CONFIG_VSYNC_DRM + // vsync_drm: We are not accepting events when waiting for next sync, + // so I guess this would generate a latency of at most one frame. I'm + // not sure if it's possible to add some smart logic in vsync_drm_wait() + // to avoid this problem, unless I could find more documentation... + if (VSYNC_DRM == opts.vsync) { + vsync_drm_wait(); + return 0; + } +#endif + +#ifdef CONFIG_VSYNC_OPENGL + // vsync_opengl: Same one-frame-latency issue, well, not sure how to deal it + // here. + if (VSYNC_OPENGL == opts.vsync) { + vsync_opengl_wait(); + return 0; + } +#endif + + // This place should not reached! + return 0; +} + int main(int argc, char **argv) { XEvent ev; @@ -3622,7 +4034,7 @@ main(int argc, char **argv) { get_cfg(argc, argv); - fade_time = get_time_in_milliseconds(); + fade_time = get_time_ms(); dpy = XOpenDisplay(opts.display); if (!dpy) { @@ -3667,11 +4079,39 @@ main(int argc, char **argv) { exit(1); } - if (!XShapeQueryExtension(dpy, &shape_event, &shape_error)) { - shape_exists = False; + // Query X Shape + if (XShapeQueryExtension(dpy, &shape_event, &shape_error)) { + shape_exists = True; } - register_cm(scr); + // Query X RandR + if (VSYNC_SW == opts.vsync && !opts.refresh_rate) { + if (XRRQueryExtension(dpy, &randr_event, &randr_error)) + randr_exists = True; + else + fprintf(stderr, "No XRandR extension, automatic refresh rate " + "detection impossible.\n"); + } + +#ifdef CONFIG_VSYNC_OPENGL + // Query X GLX extension + if (VSYNC_OPENGL == opts.vsync) { + if (glXQueryExtension(dpy, &glx_event, &glx_error)) + glx_exists = True; + else { + fprintf(stderr, "No GLX extension, OpenGL VSync impossible.\n"); + opts.vsync = VSYNC_NONE; + } + } +#endif + + register_cm((VSYNC_OPENGL == opts.vsync)); + + // Initialize software/DRM/OpenGL VSync + if ((VSYNC_SW == opts.vsync && !vsync_sw_init()) + || (VSYNC_DRM == opts.vsync && !vsync_drm_init()) + || (VSYNC_OPENGL == opts.vsync && !vsync_opengl_init())) + opts.vsync = VSYNC_NONE; if (opts.fork_after_register) fork_after(); @@ -3735,29 +4175,46 @@ main(int argc, char **argv) { ufd.fd = ConnectionNumber(dpy); ufd.events = POLLIN; +#ifdef DEBUG_REPAINT + struct timespec last_paint = get_time_timespec(); +#endif + + if (VSYNC_SW == opts.vsync) + paint_tm_offset = get_time_timespec().tv_nsec; + t = paint_preprocess(dpy, list); + paint_all(dpy, None, t); // Initialize idling idling = False; - for (;;) { - do { - if (!QLength(dpy)) { - if (poll(&ufd, 1, (idling ? -1: fade_timeout())) == 0) { - break; - } - } + // Main loop + while (1) { + Bool ev_received = False; + while (QLength(dpy) + || (vsync_wait(dpy, &ufd, + (ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) { XNextEvent(dpy, &ev); - ev_handle((XEvent *)&ev); - } while (QLength(dpy)); + ev_handle((XEvent *) &ev); + ev_received = True; + } // idling will be turned off during paint_preprocess() if needed idling = True; t = paint_preprocess(dpy, list); + if (all_damage) { +#ifdef DEBUG_REPAINT + struct timespec now = get_time_timespec(); + struct timespec diff = { 0 }; + timespec_subtract(&diff, &now, &last_paint); + printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); + last_paint = now; +#endif + static int paint; paint_all(dpy, all_damage, t); paint++; diff --git a/compton.h b/compton.h index dca11b68a..0f2f35e68 100644 --- a/compton.h +++ b/compton.h @@ -26,7 +26,12 @@ // #define CONFIG_REGEX_PCRE_JIT 1 // Whether to enable parsing of configuration files using libconfig // #define CONFIG_LIBCONFIG 1 +// Whether to enable DRM VSync support +// #define CONFIG_VSYNC_DRM 1 +// Whether to enable OpenGL VSync support +// #define CONFIG_VSYNC_OPENGL 1 +#define NDEBUG 1 // === Includes === // For some special functions @@ -44,6 +49,7 @@ #include #include #include +#include #include @@ -69,6 +75,21 @@ #include #include #include +#include + +#ifdef CONFIG_VSYNC_DRM +#include +// We references some definitions in drm.h, which could also be found in +// /usr/src/linux/include/drm/drm.h, but that path is probably even less +// reliable than libdrm +#include +#include +#include +#endif + +#ifdef CONFIG_VSYNC_OPENGL +#include +#endif // === Constants === #if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 @@ -89,6 +110,13 @@ extern struct timeval time_start; #define WINDOW_TRANS 1 #define WINDOW_ARGB 2 +#define FADE_DELTA_TOLERANCE 0.2 +#define VSYNC_SW_TOLERANCE 1000 + +#define NS_PER_SEC 1000000000L +#define US_PER_SEC 1000000L +#define MS_PER_SEC 1000 + // Window flags // Window size is changed @@ -259,6 +287,18 @@ typedef struct _win { struct _win *prev_trans; } win; +typedef enum _vsync_t { + VSYNC_NONE, + VSYNC_SW, + VSYNC_DRM, + VSYNC_OPENGL, +} vsync_t; + +#ifdef CONFIG_VSYNC_OPENGL +typedef int (*f_WaitVideoSync) (int, int, unsigned *); +typedef int (*f_GetVideoSync) (unsigned *); +#endif + typedef struct _options { // General char *display; @@ -273,6 +313,12 @@ typedef struct _options { /// Whether to work under synchronized mode for debugging. Bool synchronize; + // VSync + /// User-specified refresh rate. + int refresh_rate; + /// VSync method to use; + vsync_t vsync; + // Shadow Bool wintype_shadow[NUM_WINTYPES]; /// Red, green and blue tone of the shadow. @@ -363,6 +409,16 @@ set_ignore(Display *dpy, unsigned long sequence); static int should_ignore(Display *dpy, unsigned long sequence); +/** + * Subtract two unsigned long values. + * + * Truncate to 0 if the result is negative. + */ +static inline unsigned long +sub_unslong(unsigned long a, unsigned long b) { + return (a > b) ? a - b : 0; +} + /** * Set a Bool array of all wintypes to true. */ @@ -524,6 +580,41 @@ timeval_subtract(struct timeval *result, return x->tv_sec < y->tv_sec; } +/* + * Subtracting two struct timespec values. + * + * Taken from glibc manual. + * + * Subtract the `struct timespec' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. + */ +static inline int +timespec_subtract(struct timespec *result, + struct timespec *x, + struct timespec *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + int nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; + y->tv_nsec -= NS_PER_SEC * nsec; + y->tv_sec += nsec; + } + + if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { + int nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; + y->tv_nsec += NS_PER_SEC * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + /** * Print time passed since program starts execution. * @@ -586,7 +677,7 @@ free_damage(Display *dpy, Damage *p) { } static unsigned long -get_time_in_milliseconds(void); +get_time_ms(void); static int fade_timeout(void); @@ -885,7 +976,7 @@ static void usage(void); static void -register_cm(int scr); +register_cm(Bool want_glxct); inline static void ev_focus_in(XFocusChangeEvent *ev); @@ -1007,3 +1098,31 @@ get_cfg(int argc, char *const *argv); static void get_atoms(void); + +static void +update_refresh_rate(Display *dpy); + +static Bool +vsync_sw_init(void); + +static struct timespec +vsync_sw_ntimeout(int timeout); + +static Bool +vsync_drm_init(void); + +#ifdef CONFIG_VSYNC_DRM +static int +vsync_drm_wait(void); +#endif + +static Bool +vsync_opengl_init(void); + +#ifdef CONFIG_VSYNC_OPENGL +static void +vsync_opengl_wait(void); +#endif + +static Bool +vsync_wait(Display *dpy, struct pollfd *fd, int timeout); From 5d3425b5dc3b651f8638fc545a6f8efcca8fd35a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 10 Oct 2012 21:12:46 +0800 Subject: [PATCH 055/178] Misc: Code clean-up - Fix a memory leak in register_cm(). - Print a warning message if argument of --vsync is invalid. - Known bug: compton will probably freeze X if another compositing window manager is running and --vsync opengl is enabled, with nvidia-drivers-304.51. Probably an issue on the driver. I could see no workaround. --- compton.c | 26 ++++++++++++++++++++------ compton.h | 1 - 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/compton.c b/compton.c index 81199cd29..9f1448826 100755 --- a/compton.c +++ b/compton.c @@ -35,7 +35,7 @@ const char *WINTYPES[NUM_WINTYPES] = { struct timeval time_start = { 0, 0 }; win *list; -Display *dpy; +Display *dpy = NULL; int scr; Window root; @@ -3138,10 +3138,12 @@ register_cm(Bool want_glxct) { #ifdef CONFIG_VSYNC_OPENGL // Create a window with the wanted GLX visual if (want_glxct) { + XVisualInfo *pvi = NULL; Bool ret = False; // Get visual for the window int attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; - XVisualInfo *pvi = glXChooseVisual(dpy, scr, attribs); + pvi = glXChooseVisual(dpy, scr, attribs); + if (!pvi) { fprintf(stderr, "register_cm(): Failed to choose visual required " "by fake OpenGL VSync window. OpenGL VSync turned off.\n"); @@ -3156,6 +3158,7 @@ register_cm(Bool want_glxct) { pvi->screen = scr; reg_win = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, pvi->depth, InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa); + if (!reg_win) fprintf(stderr, "register_cm(): Failed to create window required " "by fake OpenGL VSync. OpenGL VSync turned off.\n"); @@ -3175,6 +3178,9 @@ register_cm(Bool want_glxct) { } } } + if (pvi) + XFree(pvi); + if (!ret) opts.vsync = VSYNC_NONE; } @@ -3650,11 +3656,17 @@ get_cfg(int argc, char *const *argv) { break; case 270: // --vsync - for (vsync_t i = 0; i < sizeof(vsync_str) / sizeof(vsync_str[0]); ++i) - if (!strcasecmp(optarg, vsync_str[i])) { - opts.vsync = i; - break; + { + vsync_t i; + for (i = 0; i < (sizeof(vsync_str) / sizeof(vsync_str[0])); ++i) + if (!strcasecmp(optarg, vsync_str[i])) { + opts.vsync = i; + break; + } + if ((sizeof(vsync_str) / sizeof(vsync_str[0])) == i) { + fputs("Invalid --vsync argument. Ignored.\n", stderr); } + } break; default: usage(); @@ -4011,6 +4023,8 @@ vsync_wait(Display *dpy, struct pollfd *fd, int timeout) { #endif // This place should not reached! + assert(0); + return 0; } diff --git a/compton.h b/compton.h index 0f2f35e68..d28018b73 100644 --- a/compton.h +++ b/compton.h @@ -31,7 +31,6 @@ // Whether to enable OpenGL VSync support // #define CONFIG_VSYNC_OPENGL 1 -#define NDEBUG 1 // === Includes === // For some special functions From 2b120a74844d94d9f06fb86692fb667e557bc613 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 13 Oct 2012 18:46:59 +0800 Subject: [PATCH 056/178] Improvement: Pregenerate alpha pictures Pregenerate alpha pictures to save time when painting. Add --alpha-step to control the step of alpha picture generation (the opacity difference between two consecutively generated alpha pictures). --- compton.c | 79 +++++++++++++++++++++++++++++++++---------------------- compton.h | 11 ++++---- 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/compton.c b/compton.c index 9f1448826..8992cd30d 100755 --- a/compton.c +++ b/compton.c @@ -52,6 +52,8 @@ Bool has_name_pixmap; #endif int root_height, root_width; +/// Pregenerated alpha pictures. +Picture *alpha_picts = NULL; /// Whether the program is idling. I.e. no fading, no potential window /// changes. Bool idling; @@ -187,6 +189,7 @@ static options_t opts = { .frame_opacity = 0.0, .detect_client_opacity = False, .inactive_dim = 0.0, + .alpha_step = 0.03, .track_focus = False, .track_wdata = False, @@ -1282,6 +1285,17 @@ get_frame_extents(Display *dpy, win *w, Window client) { } } +static inline Picture +get_alpha_pict_d(double o) { + assert((lround(normalize_d(o) / opts.alpha_step)) <= lround(1.0 / opts.alpha_step)); + return alpha_picts[lround(normalize_d(o) / opts.alpha_step)]; +} + +static inline Picture +get_alpha_pict_o(opacity_t o) { + return get_alpha_pict_d((double) o / OPAQUE); +} + static win * paint_preprocess(Display *dpy, win *list) { win *w; @@ -1356,14 +1370,7 @@ paint_preprocess(Display *dpy, win *list) { add_damage_win(dpy, w); } - // Rebuild alpha_pict only if necessary - if (OPAQUE != w->opacity - && (!w->alpha_pict || w->opacity != w->opacity_cur)) { - free_picture(dpy, &w->alpha_pict); - w->alpha_pict = solid_picture( - dpy, False, get_opacity_percent(dpy, w), 0, 0, 0); - w->opacity_cur = w->opacity; - } + w->alpha_pict = get_alpha_pict_o(w->opacity); // Calculate frame_opacity if (opts.frame_opacity && 1.0 != opts.frame_opacity && w->top_width) @@ -1371,15 +1378,7 @@ paint_preprocess(Display *dpy, win *list) { else w->frame_opacity = 0.0; - // Rebuild frame_alpha_pict only if necessary - if (w->frame_opacity - && (!w->frame_alpha_pict - || w->frame_opacity != w->frame_opacity_cur)) { - free_picture(dpy, &w->frame_alpha_pict); - w->frame_alpha_pict = solid_picture( - dpy, False, w->frame_opacity, 0, 0, 0); - w->frame_opacity_cur = w->frame_opacity; - } + w->frame_alpha_pict = get_alpha_pict_d(w->frame_opacity); // Calculate shadow opacity if (w->frame_opacity) @@ -1396,15 +1395,7 @@ paint_preprocess(Display *dpy, win *list) { w->widthb, w->heightb); } - // Rebuild shadow_alpha_pict if necessary - if (w->shadow - && (!w->shadow_alpha_pict - || w->shadow_opacity != w->shadow_opacity_cur)) { - free_picture(dpy, &w->shadow_alpha_pict); - w->shadow_alpha_pict = solid_picture( - dpy, False, w->shadow_opacity, 0, 0, 0); - w->shadow_opacity_cur = w->shadow_opacity; - } + w->shadow_alpha_pict = get_alpha_pict_d(w->shadow_opacity); // Reset flags w->flags = 0; @@ -2078,7 +2069,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->extents = None; new->shadow = False; new->shadow_opacity = 0.0; - new->shadow_opacity_cur = 0.0; new->shadow_pict = None; new->shadow_alpha_pict = None; new->shadow_dx = 0; @@ -2087,7 +2077,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_height = 0; new->opacity = 0; new->opacity_tgt = 0; - new->opacity_cur = OPAQUE; new->opacity_prop = OPAQUE; new->opacity_prop_client = OPAQUE; new->fade = False; @@ -2095,7 +2084,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->fade_fin = False; new->alpha_pict = None; new->frame_opacity = 1.0; - new->frame_opacity_cur = 1.0; new->frame_alpha_pict = None; new->dim = False; new->focused = False; @@ -2284,8 +2272,6 @@ finish_destroy_win(Display *dpy, Window id) { finish_unmap_win(dpy, w); *prev = w->next; - free_picture(dpy, &w->alpha_pict); - free_picture(dpy, &w->frame_alpha_pict); free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); free(w->name); @@ -3105,6 +3091,9 @@ usage(void) { " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" " work on some drivers. Experimental.\n" " (Note some VSync methods may not be enabled at compile time.)\n" + "--alpha-step val\n" + " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" + " 0.03.\n" "\n" "Format of a condition:\n" "\n" @@ -3421,6 +3410,8 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { &opts.detect_client_opacity); // --refresh-rate lcfg_lookup_int(&cfg, "refresh-rate", &opts.refresh_rate); + // --alpha-step + config_lookup_float(&cfg, "alpha-step", &opts.alpha_step); // --shadow-exclude { config_setting_t *setting = @@ -3486,6 +3477,7 @@ get_cfg(int argc, char *const *argv) { { "detect-client-opacity", no_argument, NULL, 268 }, { "refresh-rate", required_argument, NULL, 269 }, { "vsync", required_argument, NULL, 270 }, + { "alpha-step", required_argument, NULL, 271 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3668,6 +3660,10 @@ get_cfg(int argc, char *const *argv) { } } break; + case 271: + // --alpha-step + opts.alpha_step = atof(optarg); + break; default: usage(); break; @@ -3689,6 +3685,7 @@ get_cfg(int argc, char *const *argv) { opts.shadow_opacity = normalize_d(opts.shadow_opacity); cfgtmp.menu_opacity = normalize_d(cfgtmp.menu_opacity); opts.refresh_rate = normalize_i_range(opts.refresh_rate, 0, 300); + opts.alpha_step = normalize_d_range(opts.alpha_step, 0.01, 1.0); if (OPAQUE == opts.inactive_opacity) { opts.inactive_opacity = 0; } @@ -4028,6 +4025,25 @@ vsync_wait(Display *dpy, struct pollfd *fd, int timeout) { return 0; } +/** + * Pregenerate alpha pictures. + */ +static void +init_alpha_picts(Display *dpy) { + int i; + int num = lround(1.0 / opts.alpha_step) + 1; + + alpha_picts = malloc(sizeof(Picture) * num); + + for (i = 0; i < num; ++i) { + double o = i * opts.alpha_step; + if ((1.0 - o) > opts.alpha_step) + alpha_picts[i] = solid_picture(dpy, False, o, 0, 0, 0); + else + alpha_picts[i] = None; + } +} + int main(int argc, char **argv) { XEvent ev; @@ -4130,6 +4146,7 @@ main(int argc, char **argv) { if (opts.fork_after_register) fork_after(); get_atoms(); + init_alpha_picts(dpy); pa.subwindow_mode = IncludeInferiors; diff --git a/compton.h b/compton.h index d28018b73..56a397668 100644 --- a/compton.h +++ b/compton.h @@ -221,8 +221,6 @@ typedef struct _win { opacity_t opacity; /// Target window opacity. opacity_t opacity_tgt; - /// Opacity of current alpha_pict. - opacity_t opacity_cur; /// Cached value of opacity window attribute. opacity_t opacity_prop; /// Cached value of opacity window attribute on client window. For @@ -244,8 +242,6 @@ typedef struct _win { // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. double frame_opacity; - /// Opacity of current frame_alpha_pict. - opacity_t frame_opacity_cur; /// Alpha mask Picture to render window frame with opacity. Picture frame_alpha_pict; /// Frame widths. Determined by client window attributes. @@ -256,8 +252,6 @@ typedef struct _win { Bool shadow; /// Opacity of the shadow. Affected by window opacity and frame opacity. double shadow_opacity; - /// Opacity of current shadow_pict. - double shadow_opacity_cur; /// X offset of shadow. Affected by commandline argument. int shadow_dx; /// Y offset of shadow. Affected by commandline argument. @@ -359,6 +353,8 @@ typedef struct _options { Bool detect_client_opacity; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; + /// Step for pregenerating alpha pictures. 0.01 - 1.0. + double alpha_step; // Calculated /// Whether compton needs to track focus changes. @@ -1125,3 +1121,6 @@ vsync_opengl_wait(void); static Bool vsync_wait(Display *dpy, struct pollfd *fd, int timeout); + +static void +init_alpha_picts(Display *dpy); From 3c1e003830fe4066c4ccca4f75abf381a5cb70c3 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 21 Oct 2012 20:44:24 +0800 Subject: [PATCH 057/178] Improvement: Stop painting on regions of higher windows Sort of reverts cdf7db750d, but implements in a different way. (Well, maybe the pre-cdf7db750d way is actually better, if I'm willing to sacrifice some precious code reusability.) Basically, trading CPU for GPU, in an attempt to solve farseerfc and ichi-no-eda's problems. Highly experimental, could be revoked at any moment. --- compton.c | 321 ++++++++++++++++++++++++++++++++++++++++-------------- compton.h | 38 +++++++ 2 files changed, 280 insertions(+), 79 deletions(-) diff --git a/compton.c b/compton.c index 8992cd30d..9f77a7ae5 100755 --- a/compton.c +++ b/compton.c @@ -57,6 +57,8 @@ Picture *alpha_picts = NULL; /// Whether the program is idling. I.e. no fading, no potential window /// changes. Bool idling; +/// Whether all reg_ignore of windows should expire in this paint. +Bool reg_ignore_expire = False; /// Window ID of the window we register as a symbol. Window reg_win = 0; @@ -1160,6 +1162,36 @@ paint_root(Display *dpy) { root_width, root_height); } +/** + * Get a rectangular region a window occupies, excluding shadow. + */ +static XserverRegion +win_get_region(Display *dpy, win *w) { + XRectangle r; + + r.x = w->a.x; + r.y = w->a.y; + r.width = w->widthb; + r.height = w->heightb; + + return XFixesCreateRegion(dpy, &r, 1); +} + +/** + * Get a rectangular region a window occupies, excluding frame and shadow. + */ +static XserverRegion +win_get_region_noframe(Display *dpy, win *w) { + XRectangle r; + + r.x = w->a.x + w->left_width; + r.y = w->a.y + w->top_width; + r.width = w->a.width; + r.height = w->a.height; + + return XFixesCreateRegion(dpy, &r, 1); +} + /** * Get a rectangular region a window (and possibly its shadow) occupies. * @@ -1306,11 +1338,17 @@ paint_preprocess(Display *dpy, win *list) { + FADE_DELTA_TOLERANCE * opts.fade_delta) / opts.fade_delta; fade_time += steps * opts.fade_delta; + XserverRegion last_reg_ignore = None; + for (w = list; w; w = next) { // In case calling the fade callback function destroys this window next = w->next; opacity_t opacity_old = w->opacity; + // Destroy reg_ignore on all windows if they should expire + if (reg_ignore_expire) + free_region(dpy, &w->reg_ignore); + #if CAN_DO_USABLE if (!w->usable) continue; #endif @@ -1332,7 +1370,10 @@ paint_preprocess(Display *dpy, win *list) { add_damage_win(dpy, w); } - if (!w->opacity) { + w->alpha_pict = get_alpha_pict_o(w->opacity); + + // End the game if we are using the 0 opacity alpha_pict + if (w->alpha_pict == alpha_picts[0]) { check_fade_fin(dpy, w); continue; } @@ -1370,13 +1411,20 @@ paint_preprocess(Display *dpy, win *list) { add_damage_win(dpy, w); } - w->alpha_pict = get_alpha_pict_o(w->opacity); - // Calculate frame_opacity - if (opts.frame_opacity && 1.0 != opts.frame_opacity && w->top_width) - w->frame_opacity = get_opacity_percent(dpy, w) * opts.frame_opacity; - else - w->frame_opacity = 0.0; + { + double frame_opacity_old = w->frame_opacity; + + if (opts.frame_opacity && 1.0 != opts.frame_opacity + && w->top_width) + w->frame_opacity = get_opacity_percent(dpy, w) * + opts.frame_opacity; + else + w->frame_opacity = 0.0; + + if ((0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) + reg_ignore_expire = True; + } w->frame_alpha_pict = get_alpha_pict_d(w->frame_opacity); @@ -1397,6 +1445,34 @@ paint_preprocess(Display *dpy, win *list) { w->shadow_alpha_pict = get_alpha_pict_d(w->shadow_opacity); + // Generate ignore region for painting to reduce GPU load + if (reg_ignore_expire) { + free_region(dpy, &w->reg_ignore); + + // If the window is solid, we add the window region to the + // ignored region + if (WINDOW_SOLID == w->mode) { + if (!w->frame_opacity) + w->reg_ignore = win_get_region(dpy, w); + else + w->reg_ignore = win_get_region_noframe(dpy, w); + + XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, + w->border_size); + + if (last_reg_ignore) + XFixesUnionRegion(dpy, w->reg_ignore, w->reg_ignore, + last_reg_ignore); + } + // Otherwise we copy the last region over + else if (last_reg_ignore) + w->reg_ignore = copy_region(dpy, last_reg_ignore); + else + w->reg_ignore = None; + } + + last_reg_ignore = w->reg_ignore; + // Reset flags w->flags = 0; @@ -1407,13 +1483,89 @@ paint_preprocess(Display *dpy, win *list) { return t; } +/** + * Paint the shadow of a window. + */ +static inline void +win_paint_shadow(Display *dpy, win *w, Picture root_buffer) { + XRenderComposite( + dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, + root_buffer, 0, 0, 0, 0, + w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, + w->shadow_width, w->shadow_height); +} + +/** + * Paint a window itself and dim it if asked. + */ +static inline void +win_paint_win(Display *dpy, win *w, Picture root_buffer) { +#if HAS_NAME_WINDOW_PIXMAP + int x = w->a.x; + int y = w->a.y; + int wid = w->widthb; + int hei = w->heightb; +#else + int x = w->a.x + w->a.border_width; + int y = w->a.y + w->a.border_width; + int wid = w->a.width; + int hei = w->a.height; +#endif + + Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); + int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); + + if (!w->frame_opacity) { + XRenderComposite(dpy, op, w->picture, alpha_mask, + 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->frame_alpha_pict, + root_buffer, 0, 0, 0, 0, x, y, wid, t); + + // left + XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, + root_buffer, 0, t, 0, t, x, y + t, l, hei - t); + + // bottom + XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_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->frame_alpha_pict, + root_buffer, wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); + + // body + XRenderComposite(dpy, op, w->picture, alpha_mask, root_buffer, + l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); + + } + + // Dimming the window if needed + if (w->dim) { + XRenderComposite(dpy, PictOpOver, dim_picture, None, + root_buffer, 0, 0, 0, 0, x, y, wid, hei); + } +} + static void paint_all(Display *dpy, XserverRegion region, win *t) { win *w; + XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; if (!region) { region = get_screen_region(dpy); } + else { + // Remove the damaged area out of screen + XFixesIntersectRegion(dpy, region, region, get_screen_region(dpy)); + } #ifdef MONITOR_REPAINT root_buffer = root_picture; @@ -1440,97 +1592,85 @@ paint_all(Display *dpy, XserverRegion region, win *t) { root_width, root_height); #endif - paint_root(dpy); - #ifdef DEBUG_REPAINT print_timestamp(); printf("paint:"); #endif - for (w = t; w; w = w->prev_trans) { - int x, y, wid, hei; + if (t && t->reg_ignore) { + // Calculate the region upon which the root window is to be painted + // based on the ignore region of the lowest window, if available + reg_paint = reg_tmp = XFixesCreateRegion(dpy, NULL, 0); + XFixesSubtractRegion(dpy, reg_paint, region, t->reg_ignore); + } + else { + reg_paint = region; + } -#if HAS_NAME_WINDOW_PIXMAP - x = w->a.x; - y = w->a.y; - wid = w->widthb; - hei = w->heightb; -#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, reg_paint); + + paint_root(dpy); + // Create temporary regions for use during painting + if (!reg_tmp) + reg_tmp = XFixesCreateRegion(dpy, NULL, 0); + reg_tmp2 = XFixesCreateRegion(dpy, NULL, 0); + + for (w = t; w; w = w->prev_trans) { #ifdef DEBUG_REPAINT printf(" %#010lx", w->id); #endif - // Allow shadow to be painted anywhere in the damaged region - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); - // Painting shadow if (w->shadow) { - XRenderComposite( - dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, - root_buffer, 0, 0, 0, 0, - w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, - w->shadow_width, w->shadow_height); - } + // Shadow is to be painted based on the ignore region of current + // window + if (w->reg_ignore) { + if (w == t) { + // If it's the first cycle and reg_tmp2 is not ready, calculate + // the paint region here + reg_paint = reg_tmp; + XFixesSubtractRegion(dpy, reg_paint, region, w->reg_ignore); + } + else { + // Otherwise, used the cached region during last cycle + reg_paint = reg_tmp2; + } + XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->extents); + } + else { + reg_paint = region; + } - // The window only could be painted in its bounding region - XserverRegion paint_reg = XFixesCreateRegion(dpy, NULL, 0); - XFixesIntersectRegion(dpy, paint_reg, region, w->border_size); - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, paint_reg); + // Detect if the region is empty before painting + if (region == reg_paint || !is_region_empty(dpy, reg_paint)) { + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); - Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); - int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); + win_paint_shadow(dpy, w, root_buffer); + } + } - // Painting the window - if (!w->frame_opacity) { - XRenderComposite(dpy, op, w->picture, alpha_mask, - root_buffer, 0, 0, 0, 0, x, y, wid, hei); + // Calculate the region based on the reg_ignore of the next (higher) + // window and the bounding region + reg_paint = reg_tmp; + if (w->prev_trans && w->prev_trans->reg_ignore) { + XFixesSubtractRegion(dpy, reg_paint, region, + w->prev_trans->reg_ignore); + // Copy the subtracted region to be used for shadow painting in next + // cycle + XFixesCopyRegion(dpy, reg_tmp2, reg_paint); + XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->border_size); } 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->frame_alpha_pict, root_buffer, - 0, 0, 0, 0, x, y, wid, t); - - /* left */ - XRenderComposite( - dpy, PictOpOver, w->picture, w->frame_alpha_pict, root_buffer, - 0, t, 0, t, x, y + t, l, hei - t); - - /* bottom */ - XRenderComposite( - dpy, PictOpOver, w->picture, w->frame_alpha_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->frame_alpha_pict, root_buffer, - wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); - - /* body */ - XRenderComposite( - dpy, op, w->picture, alpha_mask, root_buffer, - l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); - + XFixesIntersectRegion(dpy, reg_paint, region, w->border_size); } - // Dimming the window if needed - if (w->dim) { - XRenderComposite(dpy, PictOpOver, dim_picture, None, - root_buffer, 0, 0, 0, 0, x, y, wid, hei); - } + if (!is_region_empty(dpy, reg_paint)) { + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); - XFixesDestroyRegion(dpy, paint_reg); + // Painting the window + win_paint_win(dpy, w, root_buffer); + } check_fade_fin(dpy, w); } @@ -1540,7 +1680,10 @@ paint_all(Display *dpy, XserverRegion region, win *t) { fflush(stdout); #endif + // Free up all temporary regions XFixesDestroyRegion(dpy, region); + XFixesDestroyRegion(dpy, reg_tmp); + XFixesDestroyRegion(dpy, reg_tmp2); if (root_buffer != root_picture) { XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, None); @@ -1578,6 +1721,10 @@ repair_win(Display *dpy, win *w) { w->a.y + w->a.border_width); } + // Remove the part in the damage area that could be ignored + if (!reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) + XFixesSubtractRegion(dpy, parts, parts, w->prev_trans->reg_ignore); + add_damage(dpy, parts); w->damaged = 1; } @@ -1622,6 +1769,8 @@ map_win(Display *dpy, Window id, if (!w) return; + reg_ignore_expire = True; + w->focused = False; w->a.map_state = IsViewable; @@ -1774,6 +1923,8 @@ unmap_win(Display *dpy, Window id, Bool fade) { if (!w) return; + reg_ignore_expire = True; + w->a.map_state = IsUnmapped; // Fading out @@ -1840,6 +1991,11 @@ determine_mode(Display *dpy, win *w) { mode = WINDOW_SOLID; } + // Expire reg_ignore if the window mode changes from solid to not, or + // vice versa + if ((WINDOW_SOLID == mode) != (WINDOW_SOLID == w->mode)) + reg_ignore_expire = True; + w->mode = mode; } @@ -2066,6 +2222,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->rounded_corners = False; new->border_size = None; + new->reg_ignore = None; new->extents = None; new->shadow = False; new->shadow_opacity = 0.0; @@ -2091,7 +2248,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->need_configure = False; new->window_type = WINTYPE_UNKNOWN; - new->prev_trans = 0; + new->prev_trans = NULL; new->left_width = 0; new->right_width = 0; @@ -2202,6 +2359,8 @@ configure_win(Display *dpy, XConfigureEvent *ce) { restack_win(dpy, w, ce->above); } + reg_ignore_expire = True; + w->need_configure = False; #if CAN_DO_USABLE @@ -2274,6 +2433,7 @@ finish_destroy_win(Display *dpy, Window id) { free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); + free_region(dpy, &w->reg_ignore); free(w->name); free(w->class_instance); free(w->class_general); @@ -4213,6 +4373,8 @@ main(int argc, char **argv) { if (VSYNC_SW == opts.vsync) paint_tm_offset = get_time_timespec().tv_nsec; + reg_ignore_expire = True; + t = paint_preprocess(dpy, list); paint_all(dpy, None, t); @@ -4237,7 +4399,7 @@ main(int argc, char **argv) { t = paint_preprocess(dpy, list); - if (all_damage) { + if (all_damage && !is_region_empty(dpy, all_damage)) { #ifdef DEBUG_REPAINT struct timespec now = get_time_timespec(); struct timespec diff = { 0 }; @@ -4248,6 +4410,7 @@ main(int argc, char **argv) { static int paint; paint_all(dpy, all_damage, t); + reg_ignore_expire = False; paint++; XSync(dpy, False); all_damage = None; diff --git a/compton.h b/compton.h index 56a397668..56c580993 100644 --- a/compton.h +++ b/compton.h @@ -276,6 +276,11 @@ typedef struct _win { Bool need_configure; XConfigureEvent queue_configure; + /// Region to be ignored when painting. Basically the region where + /// higher opaque windows will paint upon. Depends on window frame + /// opacity state, window geometry, window mapped/unmapped state, + /// window mode, of this and all higher windows. + XserverRegion reg_ignore; struct _win *prev_trans; } win; @@ -1038,6 +1043,39 @@ copy_region(Display *dpy, XserverRegion oldregion) { return region; } +/** + * Dump a region. + */ +static inline void +dump_region(Display *dpy, XserverRegion region) { + int nrects = 0, i; + XRectangle *rects = XFixesFetchRegion(dpy, region, &nrects); + if (!rects) + return; + + for (i = 0; i < nrects; ++i) + printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, + rects[i].width, rects[i].height); + + XFree(rects); +} + +/** + * Check if a region is empty. + * + * Keith Packard said this is slow: + * http://lists.freedesktop.org/archives/xorg/2007-November/030467.html + */ +static inline Bool +is_region_empty(Display *dpy, XserverRegion region) { + int nrects = 0; + XRectangle *rects = XFixesFetchRegion(dpy, region, &nrects); + + XFree(rects); + + return !nrects; +} + /** * Add a window to damaged area. * From 62a6c2957e958c34515cd5633c9bf38e7f1faf51 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 21 Oct 2012 21:19:00 +0800 Subject: [PATCH 058/178] Bug fix #55: Workaround for window type detection on Openbox menus A small workaround for a small problem. --- compton.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compton.c b/compton.c index 9f77a7ae5..ce461691a 100755 --- a/compton.c +++ b/compton.c @@ -1811,6 +1811,11 @@ map_win(Display *dpy, Window id, get_frame_extents(dpy, w, w->client_win); } + // Workaround for _NET_WM_WINDOW_TYPE for Openbox menus, which is + // set on a non-override-redirect window with no WM_STATE either + if (!w->client_win && WINTYPE_UNKNOWN == w->window_type) + w->window_type = get_wintype_prop(dpy, w->id); + #ifdef DEBUG_WINTYPE printf("map_win(%#010lx): type %s\n", w->id, WINTYPES[w->window_type]); From 2105bc894de7d9da641bcd551f08817e115c7db5 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 22 Oct 2012 08:16:52 +0800 Subject: [PATCH 059/178] Misc: Drop support for old versions of libXcomposite Basically dropping support for HAS_NAME_PIXMAP = 0 because I don't think there are many people using it, and I cannot maintain it. CAN_DO_USABLE support is under evaluation. --- compton.c | 21 --------------------- compton.h | 10 ++-------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/compton.c b/compton.c index ce461691a..c5243c147 100755 --- a/compton.c +++ b/compton.c @@ -47,9 +47,7 @@ Picture cshadow_picture; Picture dim_picture = 0; Picture root_tile; XserverRegion all_damage; -#if HAS_NAME_WINDOW_PIXMAP Bool has_name_pixmap; -#endif int root_height, root_width; /// Pregenerated alpha pictures. @@ -1384,13 +1382,11 @@ paint_preprocess(Display *dpy, win *list) { 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; @@ -1500,17 +1496,10 @@ win_paint_shadow(Display *dpy, win *w, Picture root_buffer) { */ static inline void win_paint_win(Display *dpy, win *w, Picture root_buffer) { -#if HAS_NAME_WINDOW_PIXMAP int x = w->a.x; int y = w->a.y; int wid = w->widthb; int hei = w->heightb; -#else - int x = w->a.x + w->a.border_width; - int y = w->a.y + w->a.border_width; - int wid = w->a.width; - int hei = w->a.height; -#endif Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); @@ -1908,9 +1897,7 @@ finish_unmap_win(Display *dpy, win *w) { w->extents = None; } -#if HAS_NAME_WINDOW_PIXMAP free_pixmap(dpy, &w->pixmap); -#endif free_picture(dpy, &w->picture); free_region(dpy, &w->border_size); @@ -2204,9 +2191,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { #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) { @@ -2382,10 +2367,8 @@ configure_win(Display *dpy, XConfigureEvent *ce) { w->a.y = ce->y; if (w->a.width != ce->width || w->a.height != ce->height) { -#if HAS_NAME_WINDOW_PIXMAP free_pixmap(dpy, &w->pixmap); free_picture(dpy, &w->picture); -#endif } if (w->a.width != ce->width || w->a.height != ce->height @@ -2449,12 +2432,10 @@ finish_destroy_win(Display *dpy, Window id) { } } -#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) { @@ -4258,11 +4239,9 @@ main(int argc, char **argv) { 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"); diff --git a/compton.h b/compton.h index 56c580993..6aac338e9 100644 --- a/compton.h +++ b/compton.h @@ -91,8 +91,8 @@ #endif // === Constants === -#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 -#define HAS_NAME_WINDOW_PIXMAP 1 +#if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) +#error libXcomposite version unsupported #endif #define ROUNDED_PERCENT 0.05 @@ -183,9 +183,7 @@ 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 */ @@ -871,10 +869,8 @@ finish_map_win(Display *dpy, win *w); 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); @@ -931,10 +927,8 @@ 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); From 450fc8745409cf74c4bbbcdb1a78d319be7c9c48 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 22 Oct 2012 03:04:19 -0500 Subject: [PATCH 060/178] v0.0.1 --- compton.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compton.c b/compton.c index c5243c147..576052886 100755 --- a/compton.c +++ b/compton.c @@ -3151,7 +3151,7 @@ ev_handle(XEvent *ev) { static void usage(void) { fputs( - "compton (development version)\n" + "compton (v0.0.1)\n" "usage: compton [options]\n" "Options:\n" "\n" @@ -3320,7 +3320,7 @@ register_cm(Bool want_glxct) { opts.vsync = VSYNC_NONE; } #endif - + if (!reg_win) reg_win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, None, None); @@ -4070,7 +4070,7 @@ vsync_drm_wait(void) { "unimplemented in this drmver?\n"); return ret; - + } #endif @@ -4095,7 +4095,7 @@ vsync_opengl_init(void) { "Failed to get glXWait/GetVideoSyncSGI function.\n"); return False; } - + return True; #else fprintf(stderr, "Program not compiled with OpenGL VSync support.\n"); From 10d4c2d2cb5742b214f93ba49aafb703eb86f1bc Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 22 Oct 2012 06:35:02 -0500 Subject: [PATCH 061/178] revert to c7ca345 --- compton.c | 1058 ++++++++++------------------------------------------- compton.h | 198 ++-------- 2 files changed, 222 insertions(+), 1034 deletions(-) mode change 100755 => 100644 compton.c diff --git a/compton.c b/compton.c old mode 100755 new mode 100644 index 576052886..bb9d7bb8f --- a/compton.c +++ b/compton.c @@ -35,7 +35,7 @@ const char *WINTYPES[NUM_WINTYPES] = { struct timeval time_start = { 0, 0 }; win *list; -Display *dpy = NULL; +Display *dpy; int scr; Window root; @@ -47,67 +47,25 @@ Picture cshadow_picture; Picture dim_picture = 0; Picture root_tile; XserverRegion all_damage; +#if HAS_NAME_WINDOW_PIXMAP Bool has_name_pixmap; +#endif int root_height, root_width; - -/// Pregenerated alpha pictures. -Picture *alpha_picts = NULL; /// Whether the program is idling. I.e. no fading, no potential window /// changes. Bool idling; -/// Whether all reg_ignore of windows should expire in this paint. -Bool reg_ignore_expire = False; -/// Window ID of the window we register as a symbol. -Window reg_win = 0; - -/// Currently used refresh rate. Used for Software VSync. -short refresh_rate = 0; -/// Interval between refresh in nanoseconds. Used for Software VSync. -unsigned long refresh_intv = 0; -/// Nanosecond-level offset of the first painting. -/// Used for Software VSync. -long paint_tm_offset = 0; - -#ifdef CONFIG_VSYNC_DRM -/// File descriptor of DRI device file. Used for DRM VSync. -int drm_fd = 0; -#endif - -#ifdef CONFIG_VSYNC_OPENGL -/// GLX context. -GLXContext glx_context; - -/// Pointer to glXGetVideoSyncSGI function. Used by OpenGL VSync. -f_GetVideoSync glx_get_video_sync = NULL; - -/// Pointer to glXWaitVideoSyncSGI function. Used by OpenGL VSync. -f_WaitVideoSync glx_wait_video_sync = NULL; -#endif /* errors */ ignore *ignore_head = NULL, **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; - /// Whether X Shape extension exists. -Bool shape_exists = False; +Bool shape_exists = True; /// Event base number and error base number for X Shape extension. int shape_event, shape_error; - -/// Whether X RandR extension exists. -Bool randr_exists = False; -/// Event base number and error base number for X RandR extension. -int randr_event, randr_error; - -#ifdef CONFIG_VSYNC_OPENGL -/// Whether X GLX extension exists. -Bool glx_exists = False; -/// Event base number and error base number for X GLX extension. -int glx_event, glx_error; -#endif +int render_event, render_error; +int composite_opcode; /* shadows */ conv *gaussian_map; @@ -137,7 +95,6 @@ Atom client_atom; Atom name_atom; Atom name_ewmh_atom; Atom class_atom; -Atom transient_atom; Atom win_type_atom; Atom win_type[NUM_WINTYPES]; @@ -161,9 +118,6 @@ static options_t opts = { .synchronize = False, .detect_rounded_corners = False, - .refresh_rate = 0, - .vsync = VSYNC_NONE, - .wintype_shadow = { False }, .shadow_red = 0.0, .shadow_green = 0.0, @@ -187,9 +141,7 @@ static options_t opts = { .inactive_opacity = 0, .inactive_opacity_override = False, .frame_opacity = 0.0, - .detect_client_opacity = False, .inactive_dim = 0.0, - .alpha_step = 0.03, .track_focus = False, .track_wdata = False, @@ -208,7 +160,7 @@ unsigned long fade_time = 0; * passed since the epoch. */ static unsigned long -get_time_ms() { +get_time_in_milliseconds() { struct timeval tv; gettimeofday(&tv, NULL); @@ -223,7 +175,7 @@ get_time_ms() { */ static int fade_timeout(void) { - int diff = opts.fade_delta - get_time_ms() + fade_time; + int diff = opts.fade_delta - get_time_in_milliseconds() + fade_time; if (diff < 0) diff = 0; @@ -989,8 +941,7 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { } if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) { - if (opts.frame_opacity || opts.track_wdata - || opts.detect_client_opacity) + if (opts.frame_opacity || opts.track_wdata) evmask |= PropertyChangeMask; } @@ -1160,36 +1111,6 @@ paint_root(Display *dpy) { root_width, root_height); } -/** - * Get a rectangular region a window occupies, excluding shadow. - */ -static XserverRegion -win_get_region(Display *dpy, win *w) { - XRectangle r; - - r.x = w->a.x; - r.y = w->a.y; - r.width = w->widthb; - r.height = w->heightb; - - return XFixesCreateRegion(dpy, &r, 1); -} - -/** - * Get a rectangular region a window occupies, excluding frame and shadow. - */ -static XserverRegion -win_get_region_noframe(Display *dpy, win *w) { - XRectangle r; - - r.x = w->a.x + w->left_width; - r.y = w->a.y + w->top_width; - r.width = w->a.width; - r.height = w->a.height; - - return XFixesCreateRegion(dpy, &r, 1); -} - /** * Get a rectangular region a window (and possibly its shadow) occupies. * @@ -1315,38 +1236,23 @@ get_frame_extents(Display *dpy, win *w, Window client) { } } -static inline Picture -get_alpha_pict_d(double o) { - assert((lround(normalize_d(o) / opts.alpha_step)) <= lround(1.0 / opts.alpha_step)); - return alpha_picts[lround(normalize_d(o) / opts.alpha_step)]; -} - -static inline Picture -get_alpha_pict_o(opacity_t o) { - return get_alpha_pict_d((double) o / OPAQUE); -} - static win * paint_preprocess(Display *dpy, win *list) { win *w; win *t = NULL, *next = NULL; + // Sounds like the timeout in poll() frequently does not work + // accurately, asking it to wait to 20ms, and often it would wait for + // 19ms, so the step value has to be rounded. + unsigned steps = roundl((double) (get_time_in_milliseconds() - fade_time) / opts.fade_delta); - // Fading step calculation - unsigned steps = (sub_unslong(get_time_ms(), fade_time) - + FADE_DELTA_TOLERANCE * opts.fade_delta) / opts.fade_delta; - fade_time += steps * opts.fade_delta; - - XserverRegion last_reg_ignore = None; + // Reset fade_time + fade_time = get_time_in_milliseconds(); for (w = list; w; w = next) { // In case calling the fade callback function destroys this window next = w->next; opacity_t opacity_old = w->opacity; - // Destroy reg_ignore on all windows if they should expire - if (reg_ignore_expire) - free_region(dpy, &w->reg_ignore); - #if CAN_DO_USABLE if (!w->usable) continue; #endif @@ -1368,10 +1274,7 @@ paint_preprocess(Display *dpy, win *list) { add_damage_win(dpy, w); } - w->alpha_pict = get_alpha_pict_o(w->opacity); - - // End the game if we are using the 0 opacity alpha_pict - if (w->alpha_pict == alpha_picts[0]) { + if (!w->opacity) { check_fade_fin(dpy, w); continue; } @@ -1382,11 +1285,13 @@ paint_preprocess(Display *dpy, win *list) { 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; @@ -1407,23 +1312,31 @@ paint_preprocess(Display *dpy, win *list) { add_damage_win(dpy, w); } - // Calculate frame_opacity - { - double frame_opacity_old = w->frame_opacity; + // Rebuild alpha_pict only if necessary + if (OPAQUE != w->opacity + && (!w->alpha_pict || w->opacity != w->opacity_cur)) { + free_picture(dpy, &w->alpha_pict); + w->alpha_pict = solid_picture( + dpy, False, get_opacity_percent(dpy, w), 0, 0, 0); + w->opacity_cur = w->opacity; + } - if (opts.frame_opacity && 1.0 != opts.frame_opacity - && w->top_width) - w->frame_opacity = get_opacity_percent(dpy, w) * - opts.frame_opacity; - else - w->frame_opacity = 0.0; + // Calculate frame_opacity + if (opts.frame_opacity && 1.0 != opts.frame_opacity && w->top_width) + w->frame_opacity = get_opacity_percent(dpy, w) * opts.frame_opacity; + else + w->frame_opacity = 0.0; - if ((0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) - reg_ignore_expire = True; + // Rebuild frame_alpha_pict only if necessary + if (w->frame_opacity + && (!w->frame_alpha_pict + || w->frame_opacity != w->frame_opacity_cur)) { + free_picture(dpy, &w->frame_alpha_pict); + w->frame_alpha_pict = solid_picture( + dpy, False, w->frame_opacity, 0, 0, 0); + w->frame_opacity_cur = w->frame_opacity; } - w->frame_alpha_pict = get_alpha_pict_d(w->frame_opacity); - // Calculate shadow opacity if (w->frame_opacity) w->shadow_opacity = opts.shadow_opacity * w->frame_opacity; @@ -1439,36 +1352,16 @@ paint_preprocess(Display *dpy, win *list) { w->widthb, w->heightb); } - w->shadow_alpha_pict = get_alpha_pict_d(w->shadow_opacity); - - // Generate ignore region for painting to reduce GPU load - if (reg_ignore_expire) { - free_region(dpy, &w->reg_ignore); - - // If the window is solid, we add the window region to the - // ignored region - if (WINDOW_SOLID == w->mode) { - if (!w->frame_opacity) - w->reg_ignore = win_get_region(dpy, w); - else - w->reg_ignore = win_get_region_noframe(dpy, w); - - XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, - w->border_size); - - if (last_reg_ignore) - XFixesUnionRegion(dpy, w->reg_ignore, w->reg_ignore, - last_reg_ignore); - } - // Otherwise we copy the last region over - else if (last_reg_ignore) - w->reg_ignore = copy_region(dpy, last_reg_ignore); - else - w->reg_ignore = None; + // Rebuild shadow_alpha_pict if necessary + if (w->shadow + && (!w->shadow_alpha_pict + || w->shadow_opacity != w->shadow_opacity_cur)) { + free_picture(dpy, &w->shadow_alpha_pict); + w->shadow_alpha_pict = solid_picture( + dpy, False, w->shadow_opacity, 0, 0, 0); + w->shadow_opacity_cur = w->shadow_opacity; } - last_reg_ignore = w->reg_ignore; - // Reset flags w->flags = 0; @@ -1479,82 +1372,13 @@ paint_preprocess(Display *dpy, win *list) { return t; } -/** - * Paint the shadow of a window. - */ -static inline void -win_paint_shadow(Display *dpy, win *w, Picture root_buffer) { - XRenderComposite( - dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, - root_buffer, 0, 0, 0, 0, - w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, - w->shadow_width, w->shadow_height); -} - -/** - * Paint a window itself and dim it if asked. - */ -static inline void -win_paint_win(Display *dpy, win *w, Picture root_buffer) { - int x = w->a.x; - int y = w->a.y; - int wid = w->widthb; - int hei = w->heightb; - - Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); - int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); - - if (!w->frame_opacity) { - XRenderComposite(dpy, op, w->picture, alpha_mask, - 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->frame_alpha_pict, - root_buffer, 0, 0, 0, 0, x, y, wid, t); - - // left - XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - root_buffer, 0, t, 0, t, x, y + t, l, hei - t); - - // bottom - XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_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->frame_alpha_pict, - root_buffer, wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); - - // body - XRenderComposite(dpy, op, w->picture, alpha_mask, root_buffer, - l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); - - } - - // Dimming the window if needed - if (w->dim) { - XRenderComposite(dpy, PictOpOver, dim_picture, None, - root_buffer, 0, 0, 0, 0, x, y, wid, hei); - } -} - static void paint_all(Display *dpy, XserverRegion region, win *t) { win *w; - XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; if (!region) { region = get_screen_region(dpy); } - else { - // Remove the damaged area out of screen - XFixesIntersectRegion(dpy, region, region, get_screen_region(dpy)); - } #ifdef MONITOR_REPAINT root_buffer = root_picture; @@ -1581,86 +1405,97 @@ paint_all(Display *dpy, XserverRegion region, win *t) { root_width, root_height); #endif + paint_root(dpy); + #ifdef DEBUG_REPAINT - print_timestamp(); printf("paint:"); #endif - if (t && t->reg_ignore) { - // Calculate the region upon which the root window is to be painted - // based on the ignore region of the lowest window, if available - reg_paint = reg_tmp = XFixesCreateRegion(dpy, NULL, 0); - XFixesSubtractRegion(dpy, reg_paint, region, t->reg_ignore); - } - else { - reg_paint = region; - } - - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); - - paint_root(dpy); + for (w = t; w; w = w->prev_trans) { + int x, y, wid, hei; - // Create temporary regions for use during painting - if (!reg_tmp) - reg_tmp = XFixesCreateRegion(dpy, NULL, 0); - reg_tmp2 = XFixesCreateRegion(dpy, NULL, 0); +#if HAS_NAME_WINDOW_PIXMAP + x = w->a.x; + y = w->a.y; + wid = w->widthb; + hei = w->heightb; +#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 - for (w = t; w; w = w->prev_trans) { #ifdef DEBUG_REPAINT printf(" %#010lx", w->id); #endif + // Allow shadow to be painted anywhere in the damaged region + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); + // Painting shadow if (w->shadow) { - // Shadow is to be painted based on the ignore region of current - // window - if (w->reg_ignore) { - if (w == t) { - // If it's the first cycle and reg_tmp2 is not ready, calculate - // the paint region here - reg_paint = reg_tmp; - XFixesSubtractRegion(dpy, reg_paint, region, w->reg_ignore); - } - else { - // Otherwise, used the cached region during last cycle - reg_paint = reg_tmp2; - } - XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->extents); - } - else { - reg_paint = region; - } + XRenderComposite( + dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, + root_buffer, 0, 0, 0, 0, + w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, + w->shadow_width, w->shadow_height); + } - // Detect if the region is empty before painting - if (region == reg_paint || !is_region_empty(dpy, reg_paint)) { - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); + // The window only could be painted in its bounding region + XserverRegion paint_reg = XFixesCreateRegion(dpy, NULL, 0); + XFixesIntersectRegion(dpy, paint_reg, region, w->border_size); + XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, paint_reg); - win_paint_shadow(dpy, w, root_buffer); - } - } + Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); + int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); - // Calculate the region based on the reg_ignore of the next (higher) - // window and the bounding region - reg_paint = reg_tmp; - if (w->prev_trans && w->prev_trans->reg_ignore) { - XFixesSubtractRegion(dpy, reg_paint, region, - w->prev_trans->reg_ignore); - // Copy the subtracted region to be used for shadow painting in next - // cycle - XFixesCopyRegion(dpy, reg_tmp2, reg_paint); - XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->border_size); + // Painting the window + if (!w->frame_opacity) { + XRenderComposite(dpy, op, w->picture, alpha_mask, + root_buffer, 0, 0, 0, 0, x, y, wid, hei); } else { - XFixesIntersectRegion(dpy, reg_paint, region, w->border_size); - } + 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->frame_alpha_pict, root_buffer, + 0, 0, 0, 0, x, y, wid, t); + + /* left */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->frame_alpha_pict, root_buffer, + 0, t, 0, t, x, y + t, l, hei - t); + + /* bottom */ + XRenderComposite( + dpy, PictOpOver, w->picture, w->frame_alpha_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->frame_alpha_pict, root_buffer, + wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); + + /* body */ + XRenderComposite( + dpy, op, w->picture, alpha_mask, root_buffer, + l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); - if (!is_region_empty(dpy, reg_paint)) { - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); + } - // Painting the window - win_paint_win(dpy, w, root_buffer); + // Dimming the window if needed + if (w->dim) { + XRenderComposite(dpy, PictOpOver, dim_picture, None, + root_buffer, 0, 0, 0, 0, x, y, wid, hei); } + XFixesDestroyRegion(dpy, paint_reg); + check_fade_fin(dpy, w); } @@ -1669,10 +1504,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { fflush(stdout); #endif - // Free up all temporary regions XFixesDestroyRegion(dpy, region); - XFixesDestroyRegion(dpy, reg_tmp); - XFixesDestroyRegion(dpy, reg_tmp2); if (root_buffer != root_picture) { XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, None); @@ -1710,10 +1542,6 @@ repair_win(Display *dpy, win *w) { w->a.y + w->a.border_width); } - // Remove the part in the damage area that could be ignored - if (!reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) - XFixesSubtractRegion(dpy, parts, parts, w->prev_trans->reg_ignore); - add_damage(dpy, parts); w->damaged = 1; } @@ -1750,6 +1578,30 @@ get_wintype_prop(Display *dpy, Window wid) { return WINTYPE_UNKNOWN; } +static wintype +determine_wintype(Display *dpy, Window w) { + Window *children = NULL; + unsigned int nchildren, i; + wintype type; + + type = get_wintype_prop(dpy, w); + if (type != WINTYPE_UNKNOWN) return type; + + if (!wid_get_children(dpy, w, &children, &nchildren)) + return WINTYPE_UNKNOWN; + + for (i = 0; i < nchildren; i++) { + type = determine_wintype(dpy, children[i]); + if (type != WINTYPE_UNKNOWN) return type; + } + + if (children) { + XFree((void *)children); + } + + return WINTYPE_UNKNOWN; +} + static void map_win(Display *dpy, Window id, unsigned long sequence, Bool fade, @@ -1758,8 +1610,6 @@ map_win(Display *dpy, Window id, if (!w) return; - reg_ignore_expire = True; - w->focused = False; w->a.map_state = IsViewable; @@ -1775,21 +1625,10 @@ map_win(Display *dpy, Window id, // Detect client window here instead of in add_win() as the client // window should have been prepared at this point if (!w->client_win) { - Window cw = 0; - // Always recursively look for a window with WM_STATE, as Fluxbox - // sets override-redirect flags on all frame windows. - cw = find_client_win(dpy, w->id); + Window cw = find_client_win(dpy, w->id); #ifdef DEBUG_CLIENTWIN printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); #endif - // Set a window's client window to itself only if we didn't find a - // client window and the window has override-redirect flag - if (!cw && w->a.override_redirect) { - cw = w->id; -#ifdef DEBUG_CLIENTWIN - printf("find_client_win(%#010lx): client self (override-redirected)\n", w->id); -#endif - } if (cw) { mark_client_win(dpy, w, cw); } @@ -1800,10 +1639,8 @@ map_win(Display *dpy, Window id, get_frame_extents(dpy, w, w->client_win); } - // Workaround for _NET_WM_WINDOW_TYPE for Openbox menus, which is - // set on a non-override-redirect window with no WM_STATE either - if (!w->client_win && WINTYPE_UNKNOWN == w->window_type) - w->window_type = get_wintype_prop(dpy, w->id); + if (WINTYPE_UNKNOWN == w->window_type) + w->window_type = determine_wintype(dpy, w->id); #ifdef DEBUG_WINTYPE printf("map_win(%#010lx): type %s\n", @@ -1897,7 +1734,9 @@ finish_unmap_win(Display *dpy, win *w) { w->extents = None; } +#if HAS_NAME_WINDOW_PIXMAP free_pixmap(dpy, &w->pixmap); +#endif free_picture(dpy, &w->picture); free_region(dpy, &w->border_size); @@ -1915,8 +1754,6 @@ unmap_win(Display *dpy, Window id, Bool fade) { if (!w) return; - reg_ignore_expire = True; - w->a.map_state = IsUnmapped; // Fading out @@ -1937,14 +1774,14 @@ unmap_win(Display *dpy, Window id, Bool fade) { } static opacity_t -wid_get_opacity_prop(Display *dpy, Window wid, opacity_t def) { +get_opacity_prop(Display *dpy, win *w, opacity_t def) { Atom actual; int format; unsigned long n, left; unsigned char *data; int result = XGetWindowProperty( - dpy, wid, opacity_atom, 0L, 1L, False, + dpy, w->id, opacity_atom, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL) { @@ -1983,11 +1820,6 @@ determine_mode(Display *dpy, win *w) { mode = WINDOW_SOLID; } - // Expire reg_ignore if the window mode changes from solid to not, or - // vice versa - if ((WINDOW_SOLID == mode) != (WINDOW_SOLID == w->mode)) - reg_ignore_expire = True; - w->mode = mode; } @@ -2023,18 +1855,13 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { // Do not refetch the opacity window attribute unless necessary, this // is probably an expensive operation in some cases if (refetch_prop) { - w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); - if (!opts.detect_client_opacity || !w->client_win - || w->id == w->client_win) - w->opacity_prop_client = OPAQUE; - else - w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, - OPAQUE); + w->opacity_prop = get_opacity_prop(dpy, w, OPAQUE); } - if (OPAQUE == (opacity = w->opacity_prop) - && OPAQUE == (opacity = w->opacity_prop_client)) { - opacity = opts.wintype_opacity[w->window_type] * OPAQUE; + if (OPAQUE == (opacity = w->opacity_prop)) { + if (1.0 != opts.wintype_opacity[w->window_type]) { + opacity = opts.wintype_opacity[w->window_type] * OPAQUE; + } } // Respect inactive_opacity in some cases @@ -2135,28 +1962,14 @@ static void mark_client_win(Display *dpy, win *w, Window client) { w->client_win = client; - XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); - // Get the frame width and monitor further frame width changes on client // window if necessary if (opts.frame_opacity) { get_frame_extents(dpy, w, client); } - - // Detect window type here + XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); if (WINTYPE_UNKNOWN == w->window_type) w->window_type = get_wintype_prop(dpy, w->client_win); - - // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take - // override-redirect windows or windows without WM_TRANSIENT_FOR as - // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. - if (WINTYPE_UNKNOWN == w->window_type) { - if (w->a.override_redirect - || !wid_has_attr(dpy, client, transient_atom)) - w->window_type = WINTYPE_NORMAL; - else - w->window_type = WINTYPE_DIALOG; - } } static void @@ -2191,7 +2004,9 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { #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) { @@ -2212,10 +2027,10 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->rounded_corners = False; new->border_size = None; - new->reg_ignore = None; new->extents = None; new->shadow = False; new->shadow_opacity = 0.0; + new->shadow_opacity_cur = 0.0; new->shadow_pict = None; new->shadow_alpha_pict = None; new->shadow_dx = 0; @@ -2224,13 +2039,14 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_height = 0; new->opacity = 0; new->opacity_tgt = 0; + new->opacity_cur = OPAQUE; new->opacity_prop = OPAQUE; - new->opacity_prop_client = OPAQUE; new->fade = False; new->fade_callback = NULL; new->fade_fin = False; new->alpha_pict = None; new->frame_opacity = 1.0; + new->frame_opacity_cur = 1.0; new->frame_alpha_pict = None; new->dim = False; new->focused = False; @@ -2238,7 +2054,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->need_configure = False; new->window_type = WINTYPE_UNKNOWN; - new->prev_trans = NULL; + new->prev_trans = 0; new->left_width = 0; new->right_width = 0; @@ -2349,8 +2165,6 @@ configure_win(Display *dpy, XConfigureEvent *ce) { restack_win(dpy, w, ce->above); } - reg_ignore_expire = True; - w->need_configure = False; #if CAN_DO_USABLE @@ -2367,8 +2181,10 @@ configure_win(Display *dpy, XConfigureEvent *ce) { w->a.y = ce->y; if (w->a.width != ce->width || w->a.height != ce->height) { +#if HAS_NAME_WINDOW_PIXMAP free_pixmap(dpy, &w->pixmap); free_picture(dpy, &w->picture); +#endif } if (w->a.width != ce->width || w->a.height != ce->height @@ -2419,9 +2235,10 @@ finish_destroy_win(Display *dpy, Window id) { finish_unmap_win(dpy, w); *prev = w->next; + free_picture(dpy, &w->alpha_pict); + free_picture(dpy, &w->frame_alpha_pict); free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); - free_region(dpy, &w->reg_ignore); free(w->name); free(w->class_instance); free(w->class_general); @@ -2432,10 +2249,12 @@ finish_destroy_win(Display *dpy, Window id) { } } +#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) { @@ -2962,17 +2781,12 @@ ev_property_notify(XPropertyEvent *ev) { } } - // If _NET_WM_OPACITY changes + /* check if Trans property was changed */ if (ev->atom == opacity_atom) { - win *w = NULL; - if ((w = find_win(dpy, ev->window))) - w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); - else if (opts.detect_client_opacity - && (w = find_toplevel(dpy, ev->window))) - w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, - OPAQUE); + /* reset mode and redraw window */ + win *w = find_win(dpy, ev->window); if (w) { - calc_opacity(dpy, w, False); + calc_opacity(dpy, w, True); } } @@ -3041,22 +2855,7 @@ ev_shape_notify(XShapeEvent *ev) { } } -/** - * Handle ScreenChangeNotify events from X RandR extension. - */ -static void -ev_screen_change_notify(XRRScreenChangeNotifyEvent *ev) { - if (!opts.refresh_rate) { - update_refresh_rate(dpy); - if (!refresh_rate) { - fprintf(stderr, "ev_screen_change_notify(): Refresh rate detection " - "failed, software VSync disabled."); - opts.vsync = VSYNC_NONE; - } - } -} - -static void +inline static void ev_handle(XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) { discard_ignore(dpy, ev->xany.serial); @@ -3130,10 +2929,6 @@ ev_handle(XEvent *ev) { ev_shape_notify((XShapeEvent *) ev); break; } - if (randr_exists && ev->type == (randr_event + RRScreenChangeNotify)) { - ev_screen_change_notify((XRRScreenChangeNotifyEvent *) ev); - break; - } if (ev->type == damage_event + XDamageNotify) { ev_damage_notify((XDamageNotifyEvent *)ev); } @@ -3145,14 +2940,11 @@ ev_handle(XEvent *ev) { * Main */ -/** - * Print usage text and exit. - */ static void usage(void) { - fputs( - "compton (v0.0.1)\n" - "usage: compton [options]\n" + fprintf(stderr, "compton (development version)\n"); + fprintf(stderr, "usage: compton [options]\n"); + fprintf(stderr, "Options:\n" "\n" "-d display\n" @@ -3219,27 +3011,6 @@ usage(void) { "--detect-rounded-corners\n" " Try to detect windows with rounded corners and don't consider\n" " them shaped windows.\n" - "--detect-client-opacity\n" - " Detect _NET_WM_OPACITY on client windows, useful for window\n" - " managers not passing _NET_WM_OPACITY of client windows to frame\n" - " windows.\n" - "\n" - "--refresh-rate val\n" - " Specify refresh rate of the screen. If not specified or 0, compton\n" - " will try detecting this with X RandR extension.\n" - "--vsync vsync-method\n" - " Set VSync method. There are 4 VSync methods currently available:\n" - " none = No VSync\n" - " sw = software VSync, basically limits compton to send a request\n" - " every 1 / refresh_rate second. Experimental.\n" - " drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n" - " drivers. Experimental.\n" - " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" - " work on some drivers. Experimental.\n" - " (Note some VSync methods may not be enabled at compile time.)\n" - "--alpha-step val\n" - " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" - " 0.03.\n" "\n" "Format of a condition:\n" "\n" @@ -3256,77 +3027,26 @@ usage(void) { " flag is \"i\" (ignore case).\n" "\n" " is the actual pattern string.\n" - , stderr); + ); exit(1); } -/** - * Register a window as symbol, and initialize GLX context if wanted. - */ static void -register_cm(Bool want_glxct) { +register_cm(int scr) { + Window w; Atom a; char *buf; int len, s; -#ifdef CONFIG_VSYNC_OPENGL - // Create a window with the wanted GLX visual - if (want_glxct) { - XVisualInfo *pvi = NULL; - Bool ret = False; - // Get visual for the window - int attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; - pvi = glXChooseVisual(dpy, scr, attribs); - - if (!pvi) { - fprintf(stderr, "register_cm(): Failed to choose visual required " - "by fake OpenGL VSync window. OpenGL VSync turned off.\n"); - } - else { - // Create the window - XSetWindowAttributes swa = { - .colormap = XCreateColormap(dpy, root, pvi->visual, AllocNone), - .border_pixel = 0, - }; - - pvi->screen = scr; - reg_win = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, pvi->depth, - InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa); - - if (!reg_win) - fprintf(stderr, "register_cm(): Failed to create window required " - "by fake OpenGL VSync. OpenGL VSync turned off.\n"); - else { - // Get GLX context - glx_context = glXCreateContext(dpy, pvi, None, GL_TRUE); - if (!glx_context) { - fprintf(stderr, "register_cm(): Failed to get GLX context. " - "OpenGL VSync turned off.\n"); - opts.vsync = VSYNC_NONE; - } - else { - // Attach GLX context - if (!(ret = glXMakeCurrent(dpy, reg_win, glx_context))) - fprintf(stderr, "register_cm(): Failed to attach GLX context." - " OpenGL VSync turned off.\n"); - } - } - } - if (pvi) - XFree(pvi); + if (scr < 0) return; - if (!ret) - opts.vsync = VSYNC_NONE; - } -#endif - - if (!reg_win) - reg_win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, - None, None); + w = XCreateSimpleWindow( + dpy, RootWindow(dpy, 0), + 0, 0, 1, 1, 0, None, None); Xutf8SetWMProperties( - dpy, reg_win, "xcompmgr", "xcompmgr", + dpy, w, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL, NULL); len = strlen(REGISTER_PROP) + 2; @@ -3343,7 +3063,7 @@ register_cm(Bool want_glxct) { a = XInternAtom(dpy, buf, False); free(buf); - XSetSelectionOwner(dpy, a, reg_win, 0); + XSetSelectionOwner(dpy, a, w, 0); } static void @@ -3551,13 +3271,6 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { // --detect-rounded-corners lcfg_lookup_bool(&cfg, "detect-rounded-corners", &opts.detect_rounded_corners); - // --detect-client-opacity - lcfg_lookup_bool(&cfg, "detect-client-opacity", - &opts.detect_client_opacity); - // --refresh-rate - lcfg_lookup_int(&cfg, "refresh-rate", &opts.refresh_rate); - // --alpha-step - config_lookup_float(&cfg, "alpha-step", &opts.alpha_step); // --shadow-exclude { config_setting_t *setting = @@ -3620,19 +3333,9 @@ get_cfg(int argc, char *const *argv) { { "no-fading-openclose", no_argument, NULL, 265 }, { "shadow-ignore-shaped", no_argument, NULL, 266 }, { "detect-rounded-corners", no_argument, NULL, 267 }, - { "detect-client-opacity", no_argument, NULL, 268 }, - { "refresh-rate", required_argument, NULL, 269 }, - { "vsync", required_argument, NULL, 270 }, - { "alpha-step", required_argument, NULL, 271 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; - const static char * const vsync_str[] = { - "none", // VSYNC_NONE - "sw", // VSYNC_SW - "drm", // VSYNC_DRM - "opengl", // VSYNC_OPENGL - }; struct options_tmp cfgtmp = { .no_dock_shadow = False, @@ -3784,32 +3487,6 @@ get_cfg(int argc, char *const *argv) { // --detect-rounded-corners opts.detect_rounded_corners = True; break; - case 268: - // --detect-client-opacity - opts.detect_client_opacity = True; - break; - case 269: - // --refresh-rate - opts.refresh_rate = atoi(optarg); - break; - case 270: - // --vsync - { - vsync_t i; - for (i = 0; i < (sizeof(vsync_str) / sizeof(vsync_str[0])); ++i) - if (!strcasecmp(optarg, vsync_str[i])) { - opts.vsync = i; - break; - } - if ((sizeof(vsync_str) / sizeof(vsync_str[0])) == i) { - fputs("Invalid --vsync argument. Ignored.\n", stderr); - } - } - break; - case 271: - // --alpha-step - opts.alpha_step = atof(optarg); - break; default: usage(); break; @@ -3830,8 +3507,6 @@ get_cfg(int argc, char *const *argv) { opts.frame_opacity = normalize_d(opts.frame_opacity); opts.shadow_opacity = normalize_d(opts.shadow_opacity); cfgtmp.menu_opacity = normalize_d(cfgtmp.menu_opacity); - opts.refresh_rate = normalize_i_range(opts.refresh_rate, 0, 300); - opts.alpha_step = normalize_d_range(opts.alpha_step, 0.01, 1.0); if (OPAQUE == opts.inactive_opacity) { opts.inactive_opacity = 0; } @@ -3866,11 +3541,10 @@ get_atoms(void) { extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); - client_atom = XInternAtom(dpy, "WM_STATE", False); + client_atom = XA_WM_CLASS; name_atom = XA_WM_NAME; name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); class_atom = XA_WM_CLASS; - transient_atom = XA_WM_TRANSIENT_FOR; win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); @@ -3905,291 +3579,6 @@ get_atoms(void) { "_NET_WM_WINDOW_TYPE_DND", False); } -/** - * Update refresh rate info with X Randr extension. - */ -static void -update_refresh_rate(Display *dpy) { - XRRScreenConfiguration* randr_info; - - if (!(randr_info = XRRGetScreenInfo(dpy, root))) - return; - refresh_rate = XRRConfigCurrentRate(randr_info); - - XRRFreeScreenConfigInfo(randr_info); - - if (refresh_rate) - refresh_intv = NS_PER_SEC / refresh_rate; - else - refresh_intv = 0; -} - -/** - * Initialize software VSync. - * - * @return True for success, False otherwise - */ -static Bool -vsync_sw_init(void) { - // Prepare refresh rate - // Check if user provides one - refresh_rate = opts.refresh_rate; - if (refresh_rate) - refresh_intv = NS_PER_SEC / refresh_rate; - - // Auto-detect refresh rate otherwise - if (!refresh_rate && randr_exists) { - update_refresh_rate(dpy); - } - - // Turn off vsync_sw if we can't get the refresh rate - if (!refresh_rate) - return False; - - // Monitor screen changes only if vsync_sw is enabled and we are using - // an auto-detected refresh rate - if (randr_exists && !opts.refresh_rate) - XRRSelectInput(dpy, root, RRScreenChangeNotify); - - return True; -} - -/** - * Get current time in struct timespec. - * - * Note its starting time is unspecified. - */ -static inline struct timespec -get_time_timespec(void) { - struct timespec tm = { 0 }; - - clock_gettime(CLOCK_MONOTONIC, &tm); - - // Return a time of all 0 if the call fails - return tm; -} - -/** - * Get the smaller number that is bigger than dividend and is - * N times of divisor. - */ -static inline long -lceil_ntimes(long dividend, long divisor) { - // It's possible to use the more beautiful expression here: - // ret = ((dividend - 1) / divisor + 1) * divisor; - // But it does not work well for negative values. - long ret = dividend / divisor * divisor; - if (ret < dividend) - ret += divisor; - - return ret; -} - -/** - * Calculate time for which the program should wait for events if vsync_sw is - * enabled. - * - * @param timeout old timeout value, never negative! - * @return time to wait, in struct timespec - */ -static struct timespec -vsync_sw_ntimeout(int timeout) { - // Convert the old timeout to struct timespec - struct timespec next_paint_tmout = { - .tv_sec = timeout / MS_PER_SEC, - .tv_nsec = timeout % MS_PER_SEC * (NS_PER_SEC / MS_PER_SEC) - }; - // Get the nanosecond offset of the time when the we reach the timeout - // I don't think a 32-bit long could overflow here. - long target_relative_offset = (next_paint_tmout.tv_nsec + get_time_timespec().tv_nsec - paint_tm_offset) % NS_PER_SEC; - if (target_relative_offset < 0) - target_relative_offset += NS_PER_SEC; - - assert(target_relative_offset >= 0); - - // If the target time is sufficiently close to a VSync time, don't add - // an offset, to avoid certain blocking conditions. - if ((target_relative_offset % NS_PER_SEC) < VSYNC_SW_TOLERANCE) - return next_paint_tmout; - - // Add an offset so we wait until the next VSync after timeout - next_paint_tmout.tv_nsec += lceil_ntimes(target_relative_offset, refresh_intv) - target_relative_offset; - if (next_paint_tmout.tv_nsec > NS_PER_SEC) { - next_paint_tmout.tv_nsec -= NS_PER_SEC; - ++next_paint_tmout.tv_sec; - } - - return next_paint_tmout; -} - -/** - * Initialize DRM VSync. - * - * @return True for success, False otherwise - */ -static Bool -vsync_drm_init(void) { -#ifdef CONFIG_VSYNC_DRM - // Should we always open card0? - if ((drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { - fprintf(stderr, "vsync_drm_init(): Failed to open device.\n"); - return False; - } - - if (vsync_drm_wait()) - return False; - - return True; -#else - fprintf(stderr, "Program not compiled with DRM VSync support.\n"); - return False; -#endif -} - -#ifdef CONFIG_VSYNC_DRM -/** - * Wait for next VSync, DRM method. - * - * Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp - */ -static int -vsync_drm_wait(void) { - int ret = -1; - drm_wait_vblank_t vbl; - - vbl.request.type = _DRM_VBLANK_RELATIVE, - vbl.request.sequence = 1; - - do { - ret = ioctl(drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl); - vbl.request.type &= ~_DRM_VBLANK_RELATIVE; - } while (ret && errno == EINTR); - - if (ret) - fprintf(stderr, "vsync_drm_wait(): VBlank ioctl did not work, " - "unimplemented in this drmver?\n"); - - return ret; - -} -#endif - -/** - * Initialize OpenGL VSync. - * - * Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e - * Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html - * - * @return True for success, False otherwise - */ -static Bool -vsync_opengl_init(void) { -#ifdef CONFIG_VSYNC_OPENGL - // Get video sync functions - glx_get_video_sync = (f_GetVideoSync) - glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI"); - glx_wait_video_sync = (f_WaitVideoSync) - glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI"); - if (!glx_wait_video_sync || !glx_get_video_sync) { - fprintf(stderr, "vsync_opengl_init(): " - "Failed to get glXWait/GetVideoSyncSGI function.\n"); - return False; - } - - return True; -#else - fprintf(stderr, "Program not compiled with OpenGL VSync support.\n"); - return False; -#endif -} - -#ifdef CONFIG_VSYNC_OPENGL -/** - * Wait for next VSync, OpenGL method. - */ -static void -vsync_opengl_wait(void) { - unsigned vblank_count; - - glx_get_video_sync(&vblank_count); - glx_wait_video_sync(2, (vblank_count + 1) % 2, &vblank_count); - // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required? -} -#endif - -/** - * Wait for next vsync and timeout unless new events appear. - * - * @param fd struct pollfd used for poll() - * @param timeout second timeout (fading timeout) - * @return > 0 if we get some events, 0 if timeout is reached, < 0 on - * problems - */ -static Bool -vsync_wait(Display *dpy, struct pollfd *fd, int timeout) { - // Always wait infinitely if asked so, to minimize CPU usage - if (timeout < 0) { - int ret = poll(fd, 1, timeout); - // Reset fade_time so the fading steps during idling are not counted - fade_time = get_time_ms(); - return ret; - } - - if (VSYNC_NONE == opts.vsync) - return poll(fd, 1, timeout); - - // vsync_sw: Wait until the next sync right after next fading timeout - if (VSYNC_SW == opts.vsync) { - struct timespec new_tmout = vsync_sw_ntimeout(timeout); - // printf("ppoll(): %3ld:%09ld\n", new_tmout.tv_sec, new_tmout.tv_nsec); - return ppoll(fd, 1, &new_tmout, NULL); - } - -#ifdef CONFIG_VSYNC_DRM - // vsync_drm: We are not accepting events when waiting for next sync, - // so I guess this would generate a latency of at most one frame. I'm - // not sure if it's possible to add some smart logic in vsync_drm_wait() - // to avoid this problem, unless I could find more documentation... - if (VSYNC_DRM == opts.vsync) { - vsync_drm_wait(); - return 0; - } -#endif - -#ifdef CONFIG_VSYNC_OPENGL - // vsync_opengl: Same one-frame-latency issue, well, not sure how to deal it - // here. - if (VSYNC_OPENGL == opts.vsync) { - vsync_opengl_wait(); - return 0; - } -#endif - - // This place should not reached! - assert(0); - - return 0; -} - -/** - * Pregenerate alpha pictures. - */ -static void -init_alpha_picts(Display *dpy) { - int i; - int num = lround(1.0 / opts.alpha_step) + 1; - - alpha_picts = malloc(sizeof(Picture) * num); - - for (i = 0; i < num; ++i) { - double o = i * opts.alpha_step; - if ((1.0 - o) > opts.alpha_step) - alpha_picts[i] = solid_picture(dpy, False, o, 0, 0, 0); - else - alpha_picts[i] = None; - } -} - int main(int argc, char **argv) { XEvent ev; @@ -4210,7 +3599,7 @@ main(int argc, char **argv) { get_cfg(argc, argv); - fade_time = get_time_ms(); + fade_time = get_time_in_milliseconds(); dpy = XOpenDisplay(opts.display); if (!dpy) { @@ -4239,9 +3628,11 @@ main(int argc, char **argv) { 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"); @@ -4253,44 +3644,15 @@ main(int argc, char **argv) { exit(1); } - // Query X Shape - if (XShapeQueryExtension(dpy, &shape_event, &shape_error)) { - shape_exists = True; - } - - // Query X RandR - if (VSYNC_SW == opts.vsync && !opts.refresh_rate) { - if (XRRQueryExtension(dpy, &randr_event, &randr_error)) - randr_exists = True; - else - fprintf(stderr, "No XRandR extension, automatic refresh rate " - "detection impossible.\n"); - } - -#ifdef CONFIG_VSYNC_OPENGL - // Query X GLX extension - if (VSYNC_OPENGL == opts.vsync) { - if (glXQueryExtension(dpy, &glx_event, &glx_error)) - glx_exists = True; - else { - fprintf(stderr, "No GLX extension, OpenGL VSync impossible.\n"); - opts.vsync = VSYNC_NONE; - } + if (!XShapeQueryExtension(dpy, &shape_event, &shape_error)) { + shape_exists = False; } -#endif - - register_cm((VSYNC_OPENGL == opts.vsync)); - // Initialize software/DRM/OpenGL VSync - if ((VSYNC_SW == opts.vsync && !vsync_sw_init()) - || (VSYNC_DRM == opts.vsync && !vsync_drm_init()) - || (VSYNC_OPENGL == opts.vsync && !vsync_opengl_init())) - opts.vsync = VSYNC_NONE; + register_cm(scr); if (opts.fork_after_register) fork_after(); get_atoms(); - init_alpha_picts(dpy); pa.subwindow_mode = IncludeInferiors; @@ -4350,51 +3712,31 @@ main(int argc, char **argv) { ufd.fd = ConnectionNumber(dpy); ufd.events = POLLIN; -#ifdef DEBUG_REPAINT - struct timespec last_paint = get_time_timespec(); -#endif - - if (VSYNC_SW == opts.vsync) - paint_tm_offset = get_time_timespec().tv_nsec; - - reg_ignore_expire = True; - t = paint_preprocess(dpy, list); - paint_all(dpy, None, t); // Initialize idling idling = False; - // Main loop - while (1) { - Bool ev_received = False; + for (;;) { + do { + if (!QLength(dpy)) { + if (poll(&ufd, 1, (idling ? -1: fade_timeout())) == 0) { + break; + } + } - while (QLength(dpy) - || (vsync_wait(dpy, &ufd, - (ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) { XNextEvent(dpy, &ev); - ev_handle((XEvent *) &ev); - ev_received = True; - } + ev_handle((XEvent *)&ev); + } while (QLength(dpy)); // idling will be turned off during paint_preprocess() if needed idling = True; t = paint_preprocess(dpy, list); - - if (all_damage && !is_region_empty(dpy, all_damage)) { -#ifdef DEBUG_REPAINT - struct timespec now = get_time_timespec(); - struct timespec diff = { 0 }; - timespec_subtract(&diff, &now, &last_paint); - printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); - last_paint = now; -#endif - + if (all_damage) { static int paint; paint_all(dpy, all_damage, t); - reg_ignore_expire = False; paint++; XSync(dpy, False); all_damage = None; diff --git a/compton.h b/compton.h index 6aac338e9..83b78769e 100644 --- a/compton.h +++ b/compton.h @@ -26,10 +26,6 @@ // #define CONFIG_REGEX_PCRE_JIT 1 // Whether to enable parsing of configuration files using libconfig // #define CONFIG_LIBCONFIG 1 -// Whether to enable DRM VSync support -// #define CONFIG_VSYNC_DRM 1 -// Whether to enable OpenGL VSync support -// #define CONFIG_VSYNC_OPENGL 1 // === Includes === @@ -48,7 +44,6 @@ #include #include #include -#include #include @@ -74,25 +69,10 @@ #include #include #include -#include - -#ifdef CONFIG_VSYNC_DRM -#include -// We references some definitions in drm.h, which could also be found in -// /usr/src/linux/include/drm/drm.h, but that path is probably even less -// reliable than libdrm -#include -#include -#include -#endif - -#ifdef CONFIG_VSYNC_OPENGL -#include -#endif // === Constants === -#if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) -#error libXcomposite version unsupported +#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 +#define HAS_NAME_WINDOW_PIXMAP 1 #endif #define ROUNDED_PERCENT 0.05 @@ -109,13 +89,6 @@ extern struct timeval time_start; #define WINDOW_TRANS 1 #define WINDOW_ARGB 2 -#define FADE_DELTA_TOLERANCE 0.2 -#define VSYNC_SW_TOLERANCE 1000 - -#define NS_PER_SEC 1000000000L -#define US_PER_SEC 1000000L -#define MS_PER_SEC 1000 - // Window flags // Window size is changed @@ -183,7 +156,9 @@ 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 */ @@ -219,12 +194,10 @@ typedef struct _win { opacity_t opacity; /// Target window opacity. opacity_t opacity_tgt; + /// Opacity of current alpha_pict. + opacity_t opacity_cur; /// Cached value of opacity window attribute. opacity_t opacity_prop; - /// Cached value of opacity window attribute on client window. For - /// broken window managers not transferring client window's - /// _NET_WM_OPACITY value - opacity_t opacity_prop_client; /// Alpha mask Picture to render window with opacity. Picture alpha_pict; @@ -240,6 +213,8 @@ typedef struct _win { // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. double frame_opacity; + /// Opacity of current frame_alpha_pict. + opacity_t frame_opacity_cur; /// Alpha mask Picture to render window frame with opacity. Picture frame_alpha_pict; /// Frame widths. Determined by client window attributes. @@ -250,6 +225,8 @@ typedef struct _win { Bool shadow; /// Opacity of the shadow. Affected by window opacity and frame opacity. double shadow_opacity; + /// Opacity of current shadow_pict. + double shadow_opacity_cur; /// X offset of shadow. Affected by commandline argument. int shadow_dx; /// Y offset of shadow. Affected by commandline argument. @@ -274,27 +251,10 @@ typedef struct _win { Bool need_configure; XConfigureEvent queue_configure; - /// Region to be ignored when painting. Basically the region where - /// higher opaque windows will paint upon. Depends on window frame - /// opacity state, window geometry, window mapped/unmapped state, - /// window mode, of this and all higher windows. - XserverRegion reg_ignore; struct _win *prev_trans; } win; -typedef enum _vsync_t { - VSYNC_NONE, - VSYNC_SW, - VSYNC_DRM, - VSYNC_OPENGL, -} vsync_t; - -#ifdef CONFIG_VSYNC_OPENGL -typedef int (*f_WaitVideoSync) (int, int, unsigned *); -typedef int (*f_GetVideoSync) (unsigned *); -#endif - typedef struct _options { // General char *display; @@ -309,12 +269,6 @@ typedef struct _options { /// Whether to work under synchronized mode for debugging. Bool synchronize; - // VSync - /// User-specified refresh rate. - int refresh_rate; - /// VSync method to use; - vsync_t vsync; - // Shadow Bool wintype_shadow[NUM_WINTYPES]; /// Red, green and blue tone of the shadow. @@ -348,16 +302,9 @@ typedef struct _options { /// Whether inactive_opacity overrides the opacity set by window /// attributes. Bool inactive_opacity_override; - /// Frame opacity. Relative to window opacity, also affects shadow - /// opacity. double frame_opacity; - /// Whether to detect _NET_WM_OPACITY on client windows. Used on window - /// managers that don't pass _NET_WM_OPACITY to frame windows. - Bool detect_client_opacity; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; - /// Step for pregenerating alpha pictures. 0.01 - 1.0. - double alpha_step; // Calculated /// Whether compton needs to track focus changes. @@ -407,16 +354,6 @@ set_ignore(Display *dpy, unsigned long sequence); static int should_ignore(Display *dpy, unsigned long sequence); -/** - * Subtract two unsigned long values. - * - * Truncate to 0 if the result is negative. - */ -static inline unsigned long -sub_unslong(unsigned long a, unsigned long b) { - return (a > b) ? a - b : 0; -} - /** * Set a Bool array of all wintypes to true. */ @@ -578,41 +515,6 @@ timeval_subtract(struct timeval *result, return x->tv_sec < y->tv_sec; } -/* - * Subtracting two struct timespec values. - * - * Taken from glibc manual. - * - * Subtract the `struct timespec' values X and Y, - * storing the result in RESULT. - * Return 1 if the difference is negative, otherwise 0. - */ -static inline int -timespec_subtract(struct timespec *result, - struct timespec *x, - struct timespec *y) { - /* Perform the carry for the later subtraction by updating y. */ - if (x->tv_nsec < y->tv_nsec) { - int nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; - y->tv_nsec -= NS_PER_SEC * nsec; - y->tv_sec += nsec; - } - - if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { - int nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; - y->tv_nsec += NS_PER_SEC * nsec; - y->tv_sec -= nsec; - } - - /* Compute the time remaining to wait. - tv_nsec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_nsec = x->tv_nsec - y->tv_nsec; - - /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; -} - /** * Print time passed since program starts execution. * @@ -675,7 +577,7 @@ free_damage(Display *dpy, Damage *p) { } static unsigned long -get_time_ms(void); +get_time_in_milliseconds(void); static int fade_timeout(void); @@ -729,7 +631,8 @@ solid_picture(Display *dpy, Bool argb, double a, static inline bool is_normal_win(const win *w) { return (WINTYPE_NORMAL == w->window_type - || WINTYPE_UTILITY == w->window_type); + || WINTYPE_UTILITY == w->window_type + || WINTYPE_UNKNOWN == w->window_type); } /** @@ -858,6 +761,9 @@ repair_win(Display *dpy, win *w); static wintype get_wintype_prop(Display * dpy, Window w); +static wintype +determine_wintype(Display *dpy, Window w); + static void map_win(Display *dpy, Window id, unsigned long sequence, Bool fade, @@ -869,14 +775,16 @@ finish_map_win(Display *dpy, win *w); 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 opacity_t -wid_get_opacity_prop(Display *dpy, Window wid, opacity_t def); +get_opacity_prop(Display *dpy, win *w, opacity_t def); static double get_opacity_percent(Display *dpy, win *w); @@ -927,8 +835,10 @@ 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); @@ -970,7 +880,7 @@ static void usage(void); static void -register_cm(Bool want_glxct); +register_cm(int scr); inline static void ev_focus_in(XFocusChangeEvent *ev); @@ -1037,39 +947,6 @@ copy_region(Display *dpy, XserverRegion oldregion) { return region; } -/** - * Dump a region. - */ -static inline void -dump_region(Display *dpy, XserverRegion region) { - int nrects = 0, i; - XRectangle *rects = XFixesFetchRegion(dpy, region, &nrects); - if (!rects) - return; - - for (i = 0; i < nrects; ++i) - printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, - rects[i].width, rects[i].height); - - XFree(rects); -} - -/** - * Check if a region is empty. - * - * Keith Packard said this is slow: - * http://lists.freedesktop.org/archives/xorg/2007-November/030467.html - */ -static inline Bool -is_region_empty(Display *dpy, XserverRegion region) { - int nrects = 0; - XRectangle *rects = XFixesFetchRegion(dpy, region, &nrects); - - XFree(rects); - - return !nrects; -} - /** * Add a window to damaged area. * @@ -1125,34 +1002,3 @@ get_cfg(int argc, char *const *argv); static void get_atoms(void); - -static void -update_refresh_rate(Display *dpy); - -static Bool -vsync_sw_init(void); - -static struct timespec -vsync_sw_ntimeout(int timeout); - -static Bool -vsync_drm_init(void); - -#ifdef CONFIG_VSYNC_DRM -static int -vsync_drm_wait(void); -#endif - -static Bool -vsync_opengl_init(void); - -#ifdef CONFIG_VSYNC_OPENGL -static void -vsync_opengl_wait(void); -#endif - -static Bool -vsync_wait(Display *dpy, struct pollfd *fd, int timeout); - -static void -init_alpha_picts(Display *dpy); From 09f57a7d6a27afc08ce729dbdc3ba6e03a6569b1 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 22 Oct 2012 08:20:43 -0500 Subject: [PATCH 062/178] fix man page and various other documentation. --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index bb9d7bb8f..fe522f46c 100644 --- a/compton.c +++ b/compton.c @@ -2982,7 +2982,7 @@ usage(void) { " Opacity of window titlebars and borders. (0.1 - 1.0)\n" "-G\n" " Don't draw shadows on DND windows\n" - "-b daemonize\n" + "-b\n" " Daemonize process.\n" "-S\n" " Enable synchronous operation (for debugging).\n" From 590440ac3d78f3810a19055e5e75ed9b241e456d Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 22 Oct 2012 11:56:44 -0500 Subject: [PATCH 063/178] more man page changes --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index fe522f46c..3fa5996d4 100644 --- a/compton.c +++ b/compton.c @@ -2983,7 +2983,7 @@ usage(void) { "-G\n" " Don't draw shadows on DND windows\n" "-b\n" - " Daemonize process.\n" + " Daemonize/background process.\n" "-S\n" " Enable synchronous operation (for debugging).\n" "--config path\n" From 71613f20c2f50b9a9a24547296d6ef81e5a119c8 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 23 Oct 2012 13:42:20 +0800 Subject: [PATCH 064/178] Improvement #7: Add double buffering Add double buffering with X DBE extension in hope to get rid of the tearing issue. Thanks to cairo-compmgr for providing hints. Could be enabled with --dbe. Only very limited tests have been done, I don't know if it actually solves the tearing issue. My estimation is it is harmful for performance, but I found no clear evidence. Experimental, so no configuration file option is available for it. MONITOR_REPAINT is broken if --dbe is turned on, this is intended for testing whether DBE is actually working. --- compton.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++------- compton.h | 3 ++ 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/compton.c b/compton.c index c5243c147..b93d0235f 100755 --- a/compton.c +++ b/compton.c @@ -39,8 +39,14 @@ Display *dpy = NULL; int scr; Window root; -Picture root_picture; -Picture root_buffer; +/// Picture of root window. Destination of painting in no-DBE painting +/// mode. +Picture root_picture = None; +/// Temporary buffer to paint to before sending to display. +Picture root_buffer = None; +/// DBE back buffer for root window. Used in DBE painting mode. +XdbeBackBuffer root_dbe = None; + Picture black_picture; Picture cshadow_picture; /// Picture used for dimming inactive windows. @@ -109,6 +115,8 @@ Bool glx_exists = False; int glx_event, glx_error; #endif +Bool dbe_exists = False; + /* shadows */ conv *gaussian_map; @@ -163,6 +171,7 @@ static options_t opts = { .refresh_rate = 0, .vsync = VSYNC_NONE, + .dbe = False, .wintype_shadow = { False }, .shadow_red = 0.0, @@ -1557,18 +1566,29 @@ paint_all(Display *dpy, XserverRegion region, win *t) { } #ifdef MONITOR_REPAINT + // Note: MONITOR_REPAINT cannot work with DBE right now. root_buffer = root_picture; #else if (!root_buffer) { - Pixmap root_pixmap = XCreatePixmap( - dpy, root, root_width, root_height, - DefaultDepth(dpy, scr)); + // DBE painting mode: Directly paint to a Picture of the back buffer + if (opts.dbe) { + root_buffer = XRenderCreatePicture(dpy, root_dbe, + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + 0, 0); + } + // No-DBE painting mode: Paint to an intermediate Picture then paint + // the Picture to root window + else { + Pixmap root_pixmap = XCreatePixmap( + dpy, root, root_width, root_height, + DefaultDepth(dpy, scr)); - root_buffer = XRenderCreatePicture(dpy, root_pixmap, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), - 0, 0); + root_buffer = XRenderCreatePicture(dpy, root_pixmap, + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + 0, 0); - XFreePixmap(dpy, root_pixmap); + XFreePixmap(dpy, root_pixmap); + } } #endif @@ -1674,7 +1694,16 @@ paint_all(Display *dpy, XserverRegion region, win *t) { XFixesDestroyRegion(dpy, reg_tmp); XFixesDestroyRegion(dpy, reg_tmp2); - if (root_buffer != root_picture) { + // DBE painting mode, only need to swap the buffer + if (opts.dbe) { + XdbeSwapInfo swap_info = { + .swap_window = root, + // Is it safe to use XdbeUndefined? + .swap_action = XdbeCopied + }; + XdbeSwapBuffers(dpy, &swap_info, 1); + } + else if (root_buffer != root_picture) { XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, None); XRenderComposite( dpy, PictOpSrc, root_buffer, None, @@ -3240,6 +3269,9 @@ usage(void) { "--alpha-step val\n" " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" " 0.03.\n" + "--dbe\n" + " Enable DBE painting mode, intended to use with VSync to\n" + " (hopefully) eliminate tearing.\n" "\n" "Format of a condition:\n" "\n" @@ -3624,6 +3656,7 @@ get_cfg(int argc, char *const *argv) { { "refresh-rate", required_argument, NULL, 269 }, { "vsync", required_argument, NULL, 270 }, { "alpha-step", required_argument, NULL, 271 }, + { "dbe", no_argument, NULL, 272 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3810,6 +3843,10 @@ get_cfg(int argc, char *const *argv) { // --alpha-step opts.alpha_step = atof(optarg); break; + case 272: + // --dbe + opts.dbe = True; + break; default: usage(); break; @@ -4190,6 +4227,19 @@ init_alpha_picts(Display *dpy) { } } +/** + * Initialize double buffer. + */ +static void +init_dbe(void) { + if (!(root_dbe = XdbeAllocateBackBufferName(dpy, root, XdbeCopied))) { + fprintf(stderr, "Failed to create double buffer. Double buffering " + "turned off.\n"); + opts.dbe = False; + return; + } +} + int main(int argc, char **argv) { XEvent ev; @@ -4279,6 +4329,22 @@ main(int argc, char **argv) { } #endif + // Query X DBE extension + if (opts.dbe) { + int dbe_ver_major = 0, dbe_ver_minor = 0; + if (XdbeQueryExtension(dpy, &dbe_ver_major, &dbe_ver_minor)) + if (dbe_ver_major >= 1) + dbe_exists = True; + else + fprintf(stderr, "DBE extension version too low. Double buffering " + "impossible.\n"); + else { + fprintf(stderr, "No DBE extension. Double buffering impossible.\n"); + } + if (!dbe_exists) + opts.dbe = False; + } + register_cm((VSYNC_OPENGL == opts.vsync)); // Initialize software/DRM/OpenGL VSync @@ -4287,6 +4353,9 @@ main(int argc, char **argv) { || (VSYNC_OPENGL == opts.vsync && !vsync_opengl_init())) opts.vsync = VSYNC_NONE; + if (opts.dbe) + init_dbe(); + if (opts.fork_after_register) fork_after(); get_atoms(); diff --git a/compton.h b/compton.h index 6aac338e9..ee505985a 100644 --- a/compton.h +++ b/compton.h @@ -75,6 +75,7 @@ #include #include #include +#include #ifdef CONFIG_VSYNC_DRM #include @@ -314,6 +315,8 @@ typedef struct _options { int refresh_rate; /// VSync method to use; vsync_t vsync; + /// Whether to enable double buffer. + Bool dbe; // Shadow Bool wintype_shadow[NUM_WINTYPES]; From c1471ff16a88f5383450fa6715124b465f379727 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 24 Oct 2012 10:09:59 +0800 Subject: [PATCH 065/178] Improvement: Add painting on overlay support - Add support for painting on X Composite overlay window instead of root window (--paint-on-overlay). I intended to use this to fix the conflict between conky (own-window off) and compton, but it's unsuccessful. Will have to ask somebody to figure out how to solve this problem. - Rename a few variables to avoid confusion. - Slightly change how root window content (wallpaper) change is detected. - Slightly improve window name detection in DEBUG_EVENTS. --- compton.c | 192 ++++++++++++++++++++++++++++++++++++++++-------------- compton.h | 9 +++ 2 files changed, 152 insertions(+), 49 deletions(-) diff --git a/compton.c b/compton.c index b93d0235f..833efeaa9 100755 --- a/compton.c +++ b/compton.c @@ -38,12 +38,20 @@ win *list; Display *dpy = NULL; int scr; -Window root; +/// Root window. +Window root = None; +/// Damage of root window. +Damage root_damage = None; +/// X Composite overlay window. Used if --paint-on-overlay. +Window overlay = None; + /// Picture of root window. Destination of painting in no-DBE painting /// mode. Picture root_picture = None; +/// A Picture acting as the painting target. +Picture tgt_picture = None; /// Temporary buffer to paint to before sending to display. -Picture root_buffer = None; +Picture tgt_buffer = None; /// DBE back buffer for root window. Used in DBE painting mode. XdbeBackBuffer root_dbe = None; @@ -168,6 +176,7 @@ static options_t opts = { .fork_after_register = False, .synchronize = False, .detect_rounded_corners = False, + .paint_on_overlay = False, .refresh_rate = 0, .vsync = VSYNC_NONE, @@ -1105,6 +1114,11 @@ recheck_focus(Display *dpy) { static Picture root_tile_f(Display *dpy) { + /* + if (opts.paint_on_overlay) { + return root_picture; + } */ + Picture picture; Atom actual_type; Pixmap pixmap; @@ -1165,7 +1179,7 @@ paint_root(Display *dpy) { XRenderComposite( dpy, PictOpSrc, root_tile, None, - root_buffer, 0, 0, 0, 0, 0, 0, + tgt_buffer, 0, 0, 0, 0, 0, 0, root_width, root_height); } @@ -1492,10 +1506,10 @@ paint_preprocess(Display *dpy, win *list) { * Paint the shadow of a window. */ static inline void -win_paint_shadow(Display *dpy, win *w, Picture root_buffer) { +win_paint_shadow(Display *dpy, win *w, Picture tgt_buffer) { XRenderComposite( dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, - root_buffer, 0, 0, 0, 0, + tgt_buffer, 0, 0, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height); } @@ -1504,7 +1518,7 @@ win_paint_shadow(Display *dpy, win *w, Picture root_buffer) { * Paint a window itself and dim it if asked. */ static inline void -win_paint_win(Display *dpy, win *w, Picture root_buffer) { +win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { int x = w->a.x; int y = w->a.y; int wid = w->widthb; @@ -1515,7 +1529,7 @@ win_paint_win(Display *dpy, win *w, Picture root_buffer) { if (!w->frame_opacity) { XRenderComposite(dpy, op, w->picture, alpha_mask, - root_buffer, 0, 0, 0, 0, x, y, wid, hei); + tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } else { unsigned int t = w->top_width; @@ -1525,22 +1539,22 @@ win_paint_win(Display *dpy, win *w, Picture root_buffer) { // top XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - root_buffer, 0, 0, 0, 0, x, y, wid, t); + tgt_buffer, 0, 0, 0, 0, x, y, wid, t); // left XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - root_buffer, 0, t, 0, t, x, y + t, l, hei - t); + tgt_buffer, 0, t, 0, t, x, y + t, l, hei - t); // bottom XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - root_buffer, l, hei - b, l, hei - b, x + l, y + hei - b, wid - l - r, b); + tgt_buffer, l, hei - b, l, hei - b, x + l, y + hei - b, wid - l - r, b); // right XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - root_buffer, wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); + tgt_buffer, wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); // body - XRenderComposite(dpy, op, w->picture, alpha_mask, root_buffer, + XRenderComposite(dpy, op, w->picture, alpha_mask, tgt_buffer, l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); } @@ -1548,7 +1562,7 @@ win_paint_win(Display *dpy, win *w, Picture root_buffer) { // Dimming the window if needed if (w->dim) { XRenderComposite(dpy, PictOpOver, dim_picture, None, - root_buffer, 0, 0, 0, 0, x, y, wid, hei); + tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } } @@ -1567,12 +1581,12 @@ paint_all(Display *dpy, XserverRegion region, win *t) { #ifdef MONITOR_REPAINT // Note: MONITOR_REPAINT cannot work with DBE right now. - root_buffer = root_picture; + tgt_buffer = tgt_picture; #else - if (!root_buffer) { + if (!tgt_buffer) { // DBE painting mode: Directly paint to a Picture of the back buffer if (opts.dbe) { - root_buffer = XRenderCreatePicture(dpy, root_dbe, + tgt_buffer = XRenderCreatePicture(dpy, root_dbe, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), 0, 0); } @@ -1583,7 +1597,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { dpy, root, root_width, root_height, DefaultDepth(dpy, scr)); - root_buffer = XRenderCreatePicture(dpy, root_pixmap, + tgt_buffer = XRenderCreatePicture(dpy, root_pixmap, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), 0, 0); @@ -1592,12 +1606,12 @@ paint_all(Display *dpy, XserverRegion region, win *t) { } #endif - XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); + XFixesSetPictureClipRegion(dpy, tgt_picture, 0, 0, region); #ifdef MONITOR_REPAINT XRenderComposite( dpy, PictOpSrc, black_picture, None, - root_picture, 0, 0, 0, 0, 0, 0, + tgt_picture, 0, 0, 0, 0, 0, 0, root_width, root_height); #endif @@ -1616,7 +1630,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { reg_paint = region; } - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); + XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, reg_paint); paint_root(dpy); @@ -1653,9 +1667,9 @@ paint_all(Display *dpy, XserverRegion region, win *t) { // Detect if the region is empty before painting if (region == reg_paint || !is_region_empty(dpy, reg_paint)) { - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); + XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, reg_paint); - win_paint_shadow(dpy, w, root_buffer); + win_paint_shadow(dpy, w, tgt_buffer); } } @@ -1675,10 +1689,10 @@ paint_all(Display *dpy, XserverRegion region, win *t) { } if (!is_region_empty(dpy, reg_paint)) { - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, reg_paint); + XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, reg_paint); // Painting the window - win_paint_win(dpy, w, root_buffer); + win_paint_win(dpy, w, tgt_buffer); } check_fade_fin(dpy, w); @@ -1697,17 +1711,18 @@ paint_all(Display *dpy, XserverRegion region, win *t) { // DBE painting mode, only need to swap the buffer if (opts.dbe) { XdbeSwapInfo swap_info = { - .swap_window = root, + .swap_window = (opts.paint_on_overlay ? overlay: root), // Is it safe to use XdbeUndefined? .swap_action = XdbeCopied }; XdbeSwapBuffers(dpy, &swap_info, 1); } - else if (root_buffer != root_picture) { - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, None); + // No-DBE painting mode + else if (tgt_buffer != tgt_picture) { + XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, None); XRenderComposite( - dpy, PictOpSrc, root_buffer, None, - root_picture, 0, 0, 0, 0, + dpy, PictOpSrc, tgt_buffer, None, + tgt_picture, 0, 0, 0, 0, 0, 0, root_width, root_height); } } @@ -2358,9 +2373,9 @@ configure_win(Display *dpy, XConfigureEvent *ce) { if (!w) { if (ce->window == root) { - if (root_buffer) { - XRenderFreePicture(dpy, root_buffer); - root_buffer = None; + if (tgt_buffer) { + XRenderFreePicture(dpy, tgt_buffer); + tgt_buffer = None; } root_width = ce->width; root_height = ce->height; @@ -2479,8 +2494,33 @@ destroy_win(Display *dpy, Window id, Bool fade) { } } +static inline void +root_damaged(void) { + if (root_tile) { + XClearArea(dpy, root, 0, 0, 0, 0, True); + // if (root_picture != root_tile) { + XRenderFreePicture(dpy, root_tile); + root_tile = None; + /* } + if (root_damage) { + XserverRegion parts = XFixesCreateRegion(dpy, 0, 0); + XDamageSubtract(dpy, root_damage, None, parts); + add_damage(dpy, parts); + } */ + } + // Mark screen damaged if we are painting on overlay + if (opts.paint_on_overlay) + add_damage(dpy, get_screen_region(dpy)); +} + static void damage_win(Display *dpy, XDamageNotifyEvent *de) { + /* + if (root == de->drawable) { + root_damaged(); + return; + } */ + win *w = find_win(dpy, de->drawable); if (!w) return; @@ -2979,16 +3019,16 @@ ev_expose(XExposeEvent *ev) { 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; + // Destroy the root "image" if the wallpaper probably changed + if (root == ev->window) { + for (int p = 0; background_props[p]; p++) { + if (ev->atom == XInternAtom(dpy, background_props[p], False)) { + root_damaged(); break; } } + // Unconcerned about any other proprties on root window + return; } // If _NET_WM_OPACITY changes @@ -3093,24 +3133,31 @@ ev_handle(XEvent *ev) { #ifdef DEBUG_EVENTS if (ev->type != damage_event + XDamageNotify) { - Window w; + Window wid; char *window_name; Bool to_free = False; - w = ev_window(ev); + wid = ev_window(ev); window_name = "(Failed to get title)"; - if (w) { - if (root == w) { + if (wid) { + if (root == wid) window_name = "(Root window)"; - } else { - to_free = (Bool) wid_get_name(dpy, w, &window_name); + else { + win *w = find_win(dpy, wid); + if (!w) + w = find_toplevel(dpy, wid); + + if (w->name) + window_name = w->name; + else + to_free = (Bool) wid_get_name(dpy, wid, &window_name); } } print_timestamp(); printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", - ev_name(ev), ev_serial(ev), w, window_name); + ev_name(ev), ev_serial(ev), wid, window_name); if (to_free) { XFree(window_name); @@ -3272,6 +3319,8 @@ usage(void) { "--dbe\n" " Enable DBE painting mode, intended to use with VSync to\n" " (hopefully) eliminate tearing.\n" + "--paint-on-overlay\n" + " Painting on X Composite overlay window.\n" "\n" "Format of a condition:\n" "\n" @@ -3590,6 +3639,8 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { lcfg_lookup_int(&cfg, "refresh-rate", &opts.refresh_rate); // --alpha-step config_lookup_float(&cfg, "alpha-step", &opts.alpha_step); + // --paint-on-overlay + lcfg_lookup_bool(&cfg, "paint-on-overlay", &opts.paint_on_overlay); // --shadow-exclude { config_setting_t *setting = @@ -3657,6 +3708,7 @@ get_cfg(int argc, char *const *argv) { { "vsync", required_argument, NULL, 270 }, { "alpha-step", required_argument, NULL, 271 }, { "dbe", no_argument, NULL, 272 }, + { "paint-on-overlay", no_argument, NULL, 273 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3847,6 +3899,10 @@ get_cfg(int argc, char *const *argv) { // --dbe opts.dbe = True; break; + case 273: + // --paint-on-overlay + opts.paint_on_overlay = True; + break; default: usage(); break; @@ -4232,7 +4288,8 @@ init_alpha_picts(Display *dpy) { */ static void init_dbe(void) { - if (!(root_dbe = XdbeAllocateBackBufferName(dpy, root, XdbeCopied))) { + if (!(root_dbe = XdbeAllocateBackBufferName(dpy, + (opts.paint_on_overlay ? overlay: root), XdbeCopied))) { fprintf(stderr, "Failed to create double buffer. Double buffering " "turned off.\n"); opts.dbe = False; @@ -4240,6 +4297,31 @@ init_dbe(void) { } } +/** + * Initialize X composite overlay window. + */ +static void +init_overlay(void) { + overlay = XCompositeGetOverlayWindow(dpy, root); + if (overlay) { + // Set window region of the overlay window, code stolen from + // compiz-0.8.8 + XserverRegion region = XFixesCreateRegion (dpy, NULL, 0); + XFixesSetWindowShapeRegion(dpy, overlay, ShapeBounding, 0, 0, 0); + XFixesSetWindowShapeRegion(dpy, overlay, ShapeInput, 0, 0, region); + XFixesDestroyRegion (dpy, region); + + // Retrieve DamageNotify on root window if we are painting on an + // overlay + // root_damage = XDamageCreate(dpy, root, XDamageReportNonEmpty); + } + else { + fprintf(stderr, "Cannot get X Composite overlay window. Falling " + "back to painting on root window.\n"); + opts.paint_on_overlay = False; + } +} + int main(int argc, char **argv) { XEvent ev; @@ -4353,6 +4435,10 @@ main(int argc, char **argv) { || (VSYNC_OPENGL == opts.vsync && !vsync_opengl_init())) opts.vsync = VSYNC_NONE; + // Overlay must be initialized before double buffer + if (opts.paint_on_overlay) + init_overlay(); + if (opts.dbe) init_dbe(); @@ -4370,8 +4456,16 @@ main(int argc, char **argv) { root_height = DisplayHeight(dpy, scr); root_picture = XRenderCreatePicture(dpy, root, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), - CPSubwindowMode, &pa); + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + CPSubwindowMode, &pa); + if (opts.paint_on_overlay) { + tgt_picture = XRenderCreatePicture(dpy, overlay, + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + CPSubwindowMode, &pa); + } + else { + tgt_picture = root_picture; + } black_picture = solid_picture(dpy, True, 1, 0, 0, 0); diff --git a/compton.h b/compton.h index ee505985a..77c67ed3e 100644 --- a/compton.h +++ b/compton.h @@ -307,6 +307,9 @@ typedef struct _options { Bool fork_after_register; /// Whether to detect rounded corners. Bool detect_rounded_corners; + /// Whether to paint on X Composite overlay window instead of root + /// window. + Bool paint_on_overlay; /// Whether to work under synchronized mode for debugging. Bool synchronize; @@ -1159,3 +1162,9 @@ vsync_wait(Display *dpy, struct pollfd *fd, int timeout); static void init_alpha_picts(Display *dpy); + +static void +init_dbe(void); + +static void +init_overlay(void); From 399df7aef871f7db8ab3adbe3eee6b0f5af99989 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 26 Oct 2012 11:12:28 +0800 Subject: [PATCH 066/178] Bug fix #7: Correct a possible issue in VSync - I realized I might have fundamentally misunderstood VSync. This commit tries to fix the possible problem, or at least move the tearing line close to the top of the screen. - Software VSync is replaced by --sw-opti (software optimization), as I guess it isn't possible at all to do VSync without driver support. - Add "vsync" and "sw-opti" as configuration file options. --- compton.c | 214 +++++++++++++++++++++++++++--------------------------- compton.h | 32 ++++++-- 2 files changed, 133 insertions(+), 113 deletions(-) diff --git a/compton.c b/compton.c index 833efeaa9..4394e8088 100755 --- a/compton.c +++ b/compton.c @@ -74,12 +74,11 @@ Bool reg_ignore_expire = False; /// Window ID of the window we register as a symbol. Window reg_win = 0; -/// Currently used refresh rate. Used for Software VSync. +/// Currently used refresh rate. Used for sw_opti. short refresh_rate = 0; -/// Interval between refresh in nanoseconds. Used for Software VSync. +/// Interval between refresh in nanoseconds. Used for sw_opti. unsigned long refresh_intv = 0; -/// Nanosecond-level offset of the first painting. -/// Used for Software VSync. +/// Nanosecond-level offset of the first painting. Used for sw_opti. long paint_tm_offset = 0; #ifdef CONFIG_VSYNC_DRM @@ -179,6 +178,7 @@ static options_t opts = { .paint_on_overlay = False, .refresh_rate = 0, + .sw_opti = False, .vsync = VSYNC_NONE, .dbe = False, @@ -1568,6 +1568,10 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { static void paint_all(Display *dpy, XserverRegion region, win *t) { +#ifdef DEBUG_REPAINT + static struct timespec last_paint = { 0 }; +#endif + win *w; XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; @@ -1616,7 +1620,6 @@ paint_all(Display *dpy, XserverRegion region, win *t) { #endif #ifdef DEBUG_REPAINT - print_timestamp(); printf("paint:"); #endif @@ -1700,7 +1703,6 @@ paint_all(Display *dpy, XserverRegion region, win *t) { #ifdef DEBUG_REPAINT printf("\n"); - fflush(stdout); #endif // Free up all temporary regions @@ -1708,6 +1710,9 @@ paint_all(Display *dpy, XserverRegion region, win *t) { XFixesDestroyRegion(dpy, reg_tmp); XFixesDestroyRegion(dpy, reg_tmp2); + // Wait for VBlank + vsync_wait(); + // DBE painting mode, only need to swap the buffer if (opts.dbe) { XdbeSwapInfo swap_info = { @@ -1725,6 +1730,20 @@ paint_all(Display *dpy, XserverRegion region, win *t) { tgt_picture, 0, 0, 0, 0, 0, 0, root_width, root_height); } + + XFlush(dpy); + +#ifdef DEBUG_REPAINT + // It prints the timestamp in the wrong line, but... + print_timestamp(); + struct timespec now = get_time_timespec(); + struct timespec diff = { 0 }; + timespec_subtract(&diff, &now, &last_paint); + printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); + last_paint = now; + fflush(stdout); +#endif + } static void @@ -3304,10 +3323,8 @@ usage(void) { " Specify refresh rate of the screen. If not specified or 0, compton\n" " will try detecting this with X RandR extension.\n" "--vsync vsync-method\n" - " Set VSync method. There are 4 VSync methods currently available:\n" + " Set VSync method. There are 2 VSync methods currently available:\n" " none = No VSync\n" - " sw = software VSync, basically limits compton to send a request\n" - " every 1 / refresh_rate second. Experimental.\n" " drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n" " drivers. Experimental.\n" " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" @@ -3321,6 +3338,9 @@ usage(void) { " (hopefully) eliminate tearing.\n" "--paint-on-overlay\n" " Painting on X Composite overlay window.\n" + "--sw-opti\n" + " Limit compton to repaint at most once every 1 / refresh_rate\n" + " second to boost performance. Experimental.\n" "\n" "Format of a condition:\n" "\n" @@ -3533,6 +3553,29 @@ open_config_file(char *cpath, char **ppath) { return NULL; } +/** + * Parse a VSync option argument. + */ +static inline void +parse_vsync(const char *optarg) { + const static char * const vsync_str[] = { + "none", // VSYNC_NONE + "drm", // VSYNC_DRM + "opengl", // VSYNC_OPENGL + }; + + vsync_t i; + + for (i = 0; i < (sizeof(vsync_str) / sizeof(vsync_str[0])); ++i) + if (!strcasecmp(optarg, vsync_str[i])) { + opts.vsync = i; + break; + } + if ((sizeof(vsync_str) / sizeof(vsync_str[0])) == i) { + fputs("Invalid --vsync argument. Ignored.\n", stderr); + } +} + /** * Parse a configuration file from default location. */ @@ -3543,6 +3586,7 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { config_t cfg; int ival = 0; double dval = 0.0; + const char *sval = NULL; f = open_config_file(cpath, &path); if (!f) { @@ -3637,10 +3681,15 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { &opts.detect_client_opacity); // --refresh-rate lcfg_lookup_int(&cfg, "refresh-rate", &opts.refresh_rate); + // --vsync + if (config_lookup_string(&cfg, "vsync", &sval)) + parse_vsync(sval); // --alpha-step config_lookup_float(&cfg, "alpha-step", &opts.alpha_step); // --paint-on-overlay lcfg_lookup_bool(&cfg, "paint-on-overlay", &opts.paint_on_overlay); + // --sw-opti + lcfg_lookup_bool(&cfg, "sw-opti", &opts.sw_opti); // --shadow-exclude { config_setting_t *setting = @@ -3709,15 +3758,10 @@ get_cfg(int argc, char *const *argv) { { "alpha-step", required_argument, NULL, 271 }, { "dbe", no_argument, NULL, 272 }, { "paint-on-overlay", no_argument, NULL, 273 }, + { "sw-opti", no_argument, NULL, 274 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; - const static char * const vsync_str[] = { - "none", // VSYNC_NONE - "sw", // VSYNC_SW - "drm", // VSYNC_DRM - "opengl", // VSYNC_OPENGL - }; struct options_tmp cfgtmp = { .no_dock_shadow = False, @@ -3879,17 +3923,7 @@ get_cfg(int argc, char *const *argv) { break; case 270: // --vsync - { - vsync_t i; - for (i = 0; i < (sizeof(vsync_str) / sizeof(vsync_str[0])); ++i) - if (!strcasecmp(optarg, vsync_str[i])) { - opts.vsync = i; - break; - } - if ((sizeof(vsync_str) / sizeof(vsync_str[0])) == i) { - fputs("Invalid --vsync argument. Ignored.\n", stderr); - } - } + parse_vsync(optarg); break; case 271: // --alpha-step @@ -3903,6 +3937,10 @@ get_cfg(int argc, char *const *argv) { // --paint-on-overlay opts.paint_on_overlay = True; break; + case 274: + // --sw-opti + opts.sw_opti = True; + break; default: usage(); break; @@ -4018,12 +4056,12 @@ update_refresh_rate(Display *dpy) { } /** - * Initialize software VSync. + * Initialize refresh-rated based software optimization. * * @return True for success, False otherwise */ static Bool -vsync_sw_init(void) { +sw_opti_init(void) { // Prepare refresh rate // Check if user provides one refresh_rate = opts.refresh_rate; @@ -4047,21 +4085,6 @@ vsync_sw_init(void) { return True; } -/** - * Get current time in struct timespec. - * - * Note its starting time is unspecified. - */ -static inline struct timespec -get_time_timespec(void) { - struct timespec tm = { 0 }; - - clock_gettime(CLOCK_MONOTONIC, &tm); - - // Return a time of all 0 if the call fails - return tm; -} - /** * Get the smaller number that is bigger than dividend and is * N times of divisor. @@ -4079,19 +4102,35 @@ lceil_ntimes(long dividend, long divisor) { } /** - * Calculate time for which the program should wait for events if vsync_sw is - * enabled. + * Wait for events until next paint. + * + * Optionally use refresh-rate based optimization to reduce painting. * - * @param timeout old timeout value, never negative! - * @return time to wait, in struct timespec + * @param fd struct pollfd used for poll() + * @param timeout second timeout (fading timeout) + * @return > 0 if we get some events, 0 if timeout is reached, < 0 on + * problems */ -static struct timespec -vsync_sw_ntimeout(int timeout) { +static int +evpoll(struct pollfd *fd, int timeout) { + // Always wait infinitely if asked so, to minimize CPU usage + if (timeout < 0) { + int ret = poll(fd, 1, timeout); + // Reset fade_time so the fading steps during idling are not counted + fade_time = get_time_ms(); + return ret; + } + + // Just do a poll() if we are not using optimization + if (!opts.sw_opti) + return poll(fd, 1, timeout); + // Convert the old timeout to struct timespec struct timespec next_paint_tmout = { .tv_sec = timeout / MS_PER_SEC, .tv_nsec = timeout % MS_PER_SEC * (NS_PER_SEC / MS_PER_SEC) }; + // Get the nanosecond offset of the time when the we reach the timeout // I don't think a 32-bit long could overflow here. long target_relative_offset = (next_paint_tmout.tv_nsec + get_time_timespec().tv_nsec - paint_tm_offset) % NS_PER_SEC; @@ -4100,19 +4139,19 @@ vsync_sw_ntimeout(int timeout) { assert(target_relative_offset >= 0); - // If the target time is sufficiently close to a VSync time, don't add + // If the target time is sufficiently close to a refresh time, don't add // an offset, to avoid certain blocking conditions. - if ((target_relative_offset % NS_PER_SEC) < VSYNC_SW_TOLERANCE) - return next_paint_tmout; + if ((target_relative_offset % NS_PER_SEC) < SW_OPTI_TOLERANCE) + return poll(fd, 1, timeout); - // Add an offset so we wait until the next VSync after timeout + // Add an offset so we wait until the next refresh after timeout next_paint_tmout.tv_nsec += lceil_ntimes(target_relative_offset, refresh_intv) - target_relative_offset; if (next_paint_tmout.tv_nsec > NS_PER_SEC) { next_paint_tmout.tv_nsec -= NS_PER_SEC; ++next_paint_tmout.tv_sec; } - return next_paint_tmout; + return ppoll(fd, 1, &next_paint_tmout, NULL); } /** @@ -4211,57 +4250,31 @@ vsync_opengl_wait(void) { #endif /** - * Wait for next vsync and timeout unless new events appear. - * - * @param fd struct pollfd used for poll() - * @param timeout second timeout (fading timeout) - * @return > 0 if we get some events, 0 if timeout is reached, < 0 on - * problems + * Wait for next VSync. */ -static Bool -vsync_wait(Display *dpy, struct pollfd *fd, int timeout) { - // Always wait infinitely if asked so, to minimize CPU usage - if (timeout < 0) { - int ret = poll(fd, 1, timeout); - // Reset fade_time so the fading steps during idling are not counted - fade_time = get_time_ms(); - return ret; - } - +static void +vsync_wait(void) { if (VSYNC_NONE == opts.vsync) - return poll(fd, 1, timeout); - - // vsync_sw: Wait until the next sync right after next fading timeout - if (VSYNC_SW == opts.vsync) { - struct timespec new_tmout = vsync_sw_ntimeout(timeout); - // printf("ppoll(): %3ld:%09ld\n", new_tmout.tv_sec, new_tmout.tv_nsec); - return ppoll(fd, 1, &new_tmout, NULL); - } + return; #ifdef CONFIG_VSYNC_DRM - // vsync_drm: We are not accepting events when waiting for next sync, - // so I guess this would generate a latency of at most one frame. I'm - // not sure if it's possible to add some smart logic in vsync_drm_wait() - // to avoid this problem, unless I could find more documentation... if (VSYNC_DRM == opts.vsync) { vsync_drm_wait(); - return 0; + return; } #endif #ifdef CONFIG_VSYNC_OPENGL - // vsync_opengl: Same one-frame-latency issue, well, not sure how to deal it - // here. if (VSYNC_OPENGL == opts.vsync) { vsync_opengl_wait(); - return 0; + return; } #endif // This place should not reached! assert(0); - return 0; + return; } /** @@ -4391,7 +4404,7 @@ main(int argc, char **argv) { } // Query X RandR - if (VSYNC_SW == opts.vsync && !opts.refresh_rate) { + if (opts.sw_opti && !opts.refresh_rate) { if (XRRQueryExtension(dpy, &randr_event, &randr_error)) randr_exists = True; else @@ -4429,9 +4442,12 @@ main(int argc, char **argv) { register_cm((VSYNC_OPENGL == opts.vsync)); - // Initialize software/DRM/OpenGL VSync - if ((VSYNC_SW == opts.vsync && !vsync_sw_init()) - || (VSYNC_DRM == opts.vsync && !vsync_drm_init()) + // Initialize software optimization + if (opts.sw_opti) + opts.sw_opti = sw_opti_init(); + + // Initialize DRM/OpenGL VSync + if ((VSYNC_DRM == opts.vsync && !vsync_drm_init()) || (VSYNC_OPENGL == opts.vsync && !vsync_opengl_init())) opts.vsync = VSYNC_NONE; @@ -4513,11 +4529,7 @@ main(int argc, char **argv) { ufd.fd = ConnectionNumber(dpy); ufd.events = POLLIN; -#ifdef DEBUG_REPAINT - struct timespec last_paint = get_time_timespec(); -#endif - - if (VSYNC_SW == opts.vsync) + if (opts.sw_opti) paint_tm_offset = get_time_timespec().tv_nsec; reg_ignore_expire = True; @@ -4534,7 +4546,7 @@ main(int argc, char **argv) { Bool ev_received = False; while (QLength(dpy) - || (vsync_wait(dpy, &ufd, + || (evpoll(&ufd, (ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) { XNextEvent(dpy, &ev); ev_handle((XEvent *) &ev); @@ -4547,14 +4559,6 @@ main(int argc, char **argv) { t = paint_preprocess(dpy, list); if (all_damage && !is_region_empty(dpy, all_damage)) { -#ifdef DEBUG_REPAINT - struct timespec now = get_time_timespec(); - struct timespec diff = { 0 }; - timespec_subtract(&diff, &now, &last_paint); - printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); - last_paint = now; -#endif - static int paint; paint_all(dpy, all_damage, t); reg_ignore_expire = False; diff --git a/compton.h b/compton.h index 77c67ed3e..6ae31814b 100644 --- a/compton.h +++ b/compton.h @@ -111,7 +111,7 @@ extern struct timeval time_start; #define WINDOW_ARGB 2 #define FADE_DELTA_TOLERANCE 0.2 -#define VSYNC_SW_TOLERANCE 1000 +#define SW_OPTI_TOLERANCE 1000 #define NS_PER_SEC 1000000000L #define US_PER_SEC 1000000L @@ -286,7 +286,6 @@ typedef struct _win { typedef enum _vsync_t { VSYNC_NONE, - VSYNC_SW, VSYNC_DRM, VSYNC_OPENGL, } vsync_t; @@ -313,9 +312,11 @@ typedef struct _options { /// Whether to work under synchronized mode for debugging. Bool synchronize; - // VSync + // VSync and software optimization /// User-specified refresh rate. int refresh_rate; + /// Whether to enable refresh-rate-based software optimization. + Bool sw_opti; /// VSync method to use; vsync_t vsync; /// Whether to enable double buffer. @@ -619,6 +620,21 @@ timespec_subtract(struct timespec *result, return x->tv_sec < y->tv_sec; } +/** + * Get current time in struct timespec. + * + * Note its starting time is unspecified. + */ +static inline struct timespec +get_time_timespec(void) { + struct timespec tm = { 0 }; + + clock_gettime(CLOCK_MONOTONIC, &tm); + + // Return a time of all 0 if the call fails + return tm; +} + /** * Print time passed since program starts execution. * @@ -1136,10 +1152,10 @@ static void update_refresh_rate(Display *dpy); static Bool -vsync_sw_init(void); +sw_opti_init(void); -static struct timespec -vsync_sw_ntimeout(int timeout); +static int +evpoll(struct pollfd *fd, int timeout); static Bool vsync_drm_init(void); @@ -1157,8 +1173,8 @@ static void vsync_opengl_wait(void); #endif -static Bool -vsync_wait(Display *dpy, struct pollfd *fd, int timeout); +static void +vsync_wait(void); static void init_alpha_picts(Display *dpy); From 00ce7d0de030b42958e80766534c9220d57873d3 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 27 Oct 2012 21:46:01 +0800 Subject: [PATCH 067/178] Bug fix #57: Fix incorrect handling of InputOnly windows - Stop rendering InputOnly windows. In the past I've misunderstood the whole thing, I guess, sorry. Thanks to garfilth and funeral1988 for reporting and providing valuable information. - Fix a possible segfault in DDEBUG_EVENTS. - Add "dbe" as a configuration file option. - Attempt to slightly reduce the rendering delay after VSync in non-DBE mode. I don't think, however, that this would be greatly helpful for the tearing issue. --- compton.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/compton.c b/compton.c index 4394e8088..090bae83e 100755 --- a/compton.c +++ b/compton.c @@ -784,6 +784,13 @@ win_match_once(win *w, const wincond *cond) { cond); #endif + if (InputOnly == w->a.class) { +#ifdef DEBUG_WINMATCH + printf(": InputOnly\n"); +#endif + return false; + } + // Determine the target target = NULL; switch (cond->target) { @@ -1710,6 +1717,10 @@ paint_all(Display *dpy, XserverRegion region, win *t) { XFixesDestroyRegion(dpy, reg_tmp); XFixesDestroyRegion(dpy, reg_tmp2); + // Do this as early as possible + if (!opts.dbe) + XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, None); + // Wait for VBlank vsync_wait(); @@ -1724,7 +1735,6 @@ paint_all(Display *dpy, XserverRegion region, win *t) { } // No-DBE painting mode else if (tgt_buffer != tgt_picture) { - XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, None); XRenderComposite( dpy, PictOpSrc, tgt_buffer, None, tgt_picture, 0, 0, 0, 0, @@ -1819,7 +1829,8 @@ map_win(Display *dpy, Window id, Bool override_redirect) { win *w = find_win(dpy, id); - if (!w) return; + // Don't care about window mapping if it's an InputOnly window + if (!w || InputOnly == w->a.class) return; reg_ignore_expire = True; @@ -3167,7 +3178,7 @@ ev_handle(XEvent *ev) { if (!w) w = find_toplevel(dpy, wid); - if (w->name) + if (w && w->name) window_name = w->name; else to_free = (Bool) wid_get_name(dpy, wid, &window_name); @@ -3686,6 +3697,8 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { parse_vsync(sval); // --alpha-step config_lookup_float(&cfg, "alpha-step", &opts.alpha_step); + // --dbe + lcfg_lookup_bool(&cfg, "dbe", &opts.dbe); // --paint-on-overlay lcfg_lookup_bool(&cfg, "paint-on-overlay", &opts.paint_on_overlay); // --sw-opti From b04321083b171e4592bb2f3d0263058a752d801c Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 28 Oct 2012 17:02:07 +0800 Subject: [PATCH 068/178] Improvement: Change clear_shadow implementation - Implement clear_shadow with painting region limitation instead of calculation in shadow image, to make it work correctly on windows with rounded corners, requested by funeral1988. This might cause more load on CPU, but could mean less load for GPU. The original implementation is kept but commented out. - Code cleanup. --- compton.c | 66 ++++++++++++++++++++++++++++++++++++------------------- compton.h | 8 +++++-- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/compton.c b/compton.c index 090bae83e..289cd0422 100755 --- a/compton.c +++ b/compton.c @@ -463,7 +463,7 @@ presum_gaussian(conv *map) { static XImage * make_shadow(Display *dpy, double opacity, - int width, int height) { + int width, int height, Bool clear_shadow) { XImage *ximage; unsigned char *data; int ylimit, xlimit; @@ -500,8 +500,8 @@ make_shadow(Display *dpy, double opacity, // later will be filled) could entirely cover the area of the shadow // that will be displayed, do not bother filling other pixels. If it // can't, we must fill the other pixels here. - if (!(opts.clear_shadow && opts.shadow_offset_x <= 0 && opts.shadow_offset_x >= -cgsize - && opts.shadow_offset_y <= 0 && opts.shadow_offset_y >= -cgsize)) { + /* if (!(clear_shadow && opts.shadow_offset_x <= 0 && opts.shadow_offset_x >= -cgsize + && opts.shadow_offset_y <= 0 && opts.shadow_offset_y >= -cgsize)) { */ if (cgsize > 0) { d = shadow_top[opacity_int * (cgsize + 1) + cgsize]; } else { @@ -509,7 +509,7 @@ make_shadow(Display *dpy, double opacity, opacity, center, center, width, height); } memset(data, d, sheight * swidth); - } + // } /* * corners @@ -572,7 +572,9 @@ make_shadow(Display *dpy, double opacity, } } - if (opts.clear_shadow) { + assert(!clear_shadow); + /* + if (clear_shadow) { // Clear the region in the shadow that the window would cover based // on shadow_offset_{x,y} user provides int xstart = normalize_i_range(- (int) opts.shadow_offset_x, 0, swidth); @@ -587,18 +589,20 @@ make_shadow(Display *dpy, double opacity, memset(&data[y * swidth + xstart], 0, xrange); } } + */ return ximage; } static Picture -shadow_picture(Display *dpy, double opacity, int width, int height) { +shadow_picture(Display *dpy, double opacity, int width, int height, + Bool clear_shadow) { XImage *shadow_image = NULL; Pixmap shadow_pixmap = None, shadow_pixmap_argb = None; Picture shadow_picture = None, shadow_picture_argb = None; GC gc = None; - shadow_image = make_shadow(dpy, opacity, width, height); + shadow_image = make_shadow(dpy, opacity, width, height, clear_shadow); if (!shadow_image) return None; @@ -1466,7 +1470,7 @@ paint_preprocess(Display *dpy, win *list) { if (w->shadow && !w->shadow_pict) { w->shadow_pict = shadow_picture(dpy, 1, - w->widthb, w->heightb); + w->widthb, w->heightb, False); } w->shadow_alpha_pict = get_alpha_pict_d(w->shadow_opacity); @@ -1672,8 +1676,13 @@ paint_all(Display *dpy, XserverRegion region, win *t) { XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->extents); } else { - reg_paint = region; + reg_paint = reg_tmp; + XFixesIntersectRegion(dpy, reg_paint, region, w->extents); } + // Clear the shadow here instead of in make_shadow() for saving GPU + // power and handling shaped windows + if (opts.clear_shadow) + XFixesSubtractRegion(dpy, reg_paint, reg_paint, w->border_size); // Detect if the region is empty before painting if (region == reg_paint || !is_region_empty(dpy, reg_paint)) { @@ -1885,11 +1894,7 @@ map_win(Display *dpy, Window id, #endif // Detect if the window is shaped or has rounded corners - if (opts.shadow_ignore_shaped) { - w->bounding_shaped = wid_bounding_shaped(dpy, w->id); - if (w->bounding_shaped && opts.detect_rounded_corners) - win_rounded_corners(dpy, w); - } + win_update_shape(dpy, w); // Get window name and class if we are tracking them if (opts.track_wdata) { @@ -2144,6 +2149,30 @@ determine_fade(Display *dpy, win *w) { w->fade = opts.wintype_fade[w->window_type]; } +/** + * Update window-shape related information. + */ +static void +win_update_shape(Display *dpy, win *w) { + if (shape_exists && (opts.shadow_ignore_shaped /* || opts.clear_shadow */)) { + // Bool bounding_shaped_old = w->bounding_shaped; + + w->bounding_shaped = wid_bounding_shaped(dpy, w->id); + if (w->bounding_shaped && opts.detect_rounded_corners) + win_rounded_corners(dpy, w); + + // Shadow state could be changed + determine_shadow(dpy, w); + + /* + // If clear_shadow state on the window possibly changed, destroy the old + // shadow_pict + if (opts.clear_shadow && w->bounding_shaped != bounding_shaped_old) + free_picture(dpy, &w->shadow_pict); + */ + } +} + /** * Determine if a window should have shadow, and update things depending * on shadow state. @@ -3130,14 +3159,7 @@ ev_shape_notify(XShapeEvent *ev) { } // Redo bounding shape detection and rounded corner detection - if (opts.shadow_ignore_shaped) { - w->bounding_shaped = wid_bounding_shaped(dpy, w->id); - if (w->bounding_shaped && opts.detect_rounded_corners) - win_rounded_corners(dpy, w); - - // Shadow state could be changed - determine_shadow(dpy, w); - } + win_update_shape(dpy, w); } /** diff --git a/compton.h b/compton.h index 6ae31814b..a987da235 100644 --- a/compton.h +++ b/compton.h @@ -740,10 +740,11 @@ presum_gaussian(conv *map); static XImage * make_shadow(Display *dpy, double opacity, - int width, int height); + int width, int height, Bool clear_shadow); static Picture -shadow_picture(Display *dpy, double opacity, int width, int height); +shadow_picture(Display *dpy, double opacity, int width, int height, + Bool clear_shadow); static Picture solid_picture(Display *dpy, Bool argb, double a, @@ -922,6 +923,9 @@ set_focused(Display *dpy, win *w, Bool focused) { static void determine_fade(Display *dpy, win *w); +static void +win_update_shape(Display *dpy, win *w); + static void determine_shadow(Display *dpy, win *w); From 6bcc871ef4603ffcfbe81ed019ad566287233975 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 29 Oct 2012 22:00:11 +0800 Subject: [PATCH 069/178] Bug fix: Fading blocks in rare circumstances - In very rare circumstances, poll() to the X connection returns 1 but no events are read out, causing XNextEvent() in the main loop to wait infinitely until another event comes, typically affecting fading process only, causing fading to appear somehow stopped. This commit adds a (possible) fix. - Listen to Expose events of the X Composite overlay window if we are painting to it, to avoid making some parts of the screen blank when switching out of X screen in --paint-on-overlay mode. - Drop "fade_fin" member of struct _win, because it's pretty useless. - Drop unused "root" parameter of expose_root(), move get_time_ms() to compton.h, etc. --- compton.c | 52 +++++++++++++++++++--------------------------------- compton.h | 21 +++++++++++++++------ 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/compton.c b/compton.c index 77b5478ee..012e9d39a 100644 --- a/compton.c +++ b/compton.c @@ -219,21 +219,6 @@ static options_t opts = { unsigned long fade_time = 0; -/** - * Get current system clock in milliseconds. - * - * The return type must be unsigned long because so many milliseconds have - * passed since the epoch. - */ -static unsigned long -get_time_ms() { - struct timeval tv; - - gettimeofday(&tv, NULL); - - return tv.tv_sec * 1000 + tv.tv_usec / 1000; -} - /** * Get the time left before next fading point. * @@ -256,10 +241,8 @@ fade_timeout(void) { */ static void run_fade(Display *dpy, win *w, unsigned steps) { - // If we reach target opacity, set fade_fin so the callback gets - // executed + // If we have reached target opacity, return if (w->opacity == w->opacity_tgt) { - w->fade_fin = True; return; } @@ -278,15 +261,9 @@ run_fade(Display *dpy, win *w, unsigned steps) { w->opacity_tgt, OPAQUE); } - if (w->opacity == w->opacity_tgt) { - w->fade_fin = True; - return; - } - else { + if (w->opacity != w->opacity_tgt) { idling = False; } - - w->fade_fin = False; } /** @@ -2331,7 +2308,6 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->opacity_prop_client = OPAQUE; new->fade = False; new->fade_callback = NULL; - new->fade_fin = False; new->alpha_pict = None; new->frame_opacity = 1.0; new->frame_alpha_pict = None; @@ -2743,7 +2719,7 @@ error(Display *dpy, XErrorEvent *ev) { } static void -expose_root(Display *dpy, Window root, XRectangle *rects, int nrects) { +expose_root(Display *dpy, XRectangle *rects, int nrects) { XserverRegion region = XFixesCreateRegion(dpy, rects, nrects); add_damage(dpy, region); } @@ -3050,7 +3026,7 @@ ev_circulate_notify(XCirculateEvent *ev) { inline static void ev_expose(XExposeEvent *ev) { - if (ev->window == root) { + if (ev->window == root || (overlay && ev->window == overlay)) { int more = ev->count + 1; if (n_expose == size_expose) { if (expose_rects) { @@ -3070,7 +3046,7 @@ ev_expose(XExposeEvent *ev) { n_expose++; if (ev->count == 0) { - expose_root(dpy, root, expose_rects, n_expose); + expose_root(dpy, expose_rects, n_expose); n_expose = 0; } } @@ -3195,6 +3171,8 @@ ev_handle(XEvent *ev) { if (wid) { if (root == wid) window_name = "(Root window)"; + else if (overlay == wid) + window_name = "(Overlay)"; else { win *w = find_win(dpy, wid); if (!w) @@ -4359,6 +4337,9 @@ init_overlay(void) { XFixesSetWindowShapeRegion(dpy, overlay, ShapeInput, 0, 0, region); XFixesDestroyRegion (dpy, region); + // Listen to Expose events on the overlay + XSelectInput(dpy, overlay, ExposureMask); + // Retrieve DamageNotify on root window if we are painting on an // overlay // root_damage = XDamageCreate(dpy, root, XDamageReportNonEmpty); @@ -4580,12 +4561,17 @@ main(int argc, char **argv) { while (1) { Bool ev_received = False; - while (QLength(dpy) + while (XEventsQueued(dpy, QueuedAfterReading) || (evpoll(&ufd, (ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) { - XNextEvent(dpy, &ev); - ev_handle((XEvent *) &ev); - ev_received = True; + // Sometimes poll() returns 1 but no events are actually read, causing + // XNextEvent() to block, I have no idea what's wrong, so we check for the + // number of events here + if (XEventsQueued(dpy, QueuedAfterReading)) { + XNextEvent(dpy, &ev); + ev_handle((XEvent *) &ev); + ev_received = True; + } } // idling will be turned off during paint_preprocess() if needed diff --git a/compton.h b/compton.h index a987da235..c70e28779 100644 --- a/compton.h +++ b/compton.h @@ -235,8 +235,6 @@ typedef struct _win { Bool fade; /// Callback to be called after fading completed. void (*fade_callback) (Display *dpy, struct _win *w); - /// Whether fading is finished. - Bool fade_fin; // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. @@ -696,8 +694,20 @@ free_damage(Display *dpy, Damage *p) { } } +/** + * Get current system clock in milliseconds. + * + * The return type must be unsigned long because so many milliseconds have + * passed since the epoch. + */ static unsigned long -get_time_ms(void); +get_time_ms(void) { + struct timeval tv; + + gettimeofday(&tv, NULL); + + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} static int fade_timeout(void); @@ -714,8 +724,7 @@ set_fade_callback(Display *dpy, win *w, */ static inline void check_fade_fin(Display *dpy, win *w) { - if (w->fade_fin) { - w->fade_fin = False; + if (w->fade_callback && w->opacity == w->opacity_tgt) { // Must be the last line as the callback could destroy w! set_fade_callback(dpy, w, NULL, True); } @@ -966,7 +975,7 @@ static int error(Display *dpy, XErrorEvent *ev); static void -expose_root(Display *dpy, Window root, XRectangle *rects, int nrects); +expose_root(Display *dpy, XRectangle *rects, int nrects); static Bool wid_get_text_prop(Display *dpy, Window wid, Atom prop, From 4a607cea934b66478be02ab88cd7c4f37f1b0ca0 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 30 Oct 2012 18:11:30 +0800 Subject: [PATCH 070/178] Misc: Small adjustments - Minor change of code in configure_win() for a slight performance boost in some cases. - Discovered there are a lot of X errors that I basically cannot fix, caused by the asynchronous nature of Xlib... - Correct the position of the timestamps in DEBUG_REPAINT. This might have negative effects for debugging X errors, though. --- compton.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/compton.c b/compton.c index 012e9d39a..52dd58a83 100644 --- a/compton.c +++ b/compton.c @@ -1261,6 +1261,9 @@ border_size(Display *dpy, win *w) { border = XFixesCreateRegionFromWindow( dpy, w->id, WindowRegionBounding); + if (!border) + return None; + /* translate this */ XFixesTranslateRegion(dpy, border, w->a.x + w->a.border_width, @@ -1607,10 +1610,6 @@ paint_all(Display *dpy, XserverRegion region, win *t) { root_width, root_height); #endif -#ifdef DEBUG_REPAINT - printf("paint:"); -#endif - if (t && t->reg_ignore) { // Calculate the region upon which the root window is to be painted // based on the ignore region of the lowest window, if available @@ -1631,10 +1630,6 @@ paint_all(Display *dpy, XserverRegion region, win *t) { reg_tmp2 = XFixesCreateRegion(dpy, NULL, 0); for (w = t; w; w = w->prev_trans) { -#ifdef DEBUG_REPAINT - printf(" %#010lx", w->id); -#endif - // Painting shadow if (w->shadow) { // Shadow is to be painted based on the ignore region of current @@ -1694,10 +1689,6 @@ paint_all(Display *dpy, XserverRegion region, win *t) { check_fade_fin(dpy, w); } -#ifdef DEBUG_REPAINT - printf("\n"); -#endif - // Free up all temporary regions XFixesDestroyRegion(dpy, region); XFixesDestroyRegion(dpy, reg_tmp); @@ -1730,16 +1721,18 @@ paint_all(Display *dpy, XserverRegion region, win *t) { XFlush(dpy); #ifdef DEBUG_REPAINT - // It prints the timestamp in the wrong line, but... print_timestamp(); struct timespec now = get_time_timespec(); struct timespec diff = { 0 }; timespec_subtract(&diff, &now, &last_paint); printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); last_paint = now; + printf("paint:"); + for (w = t; w; w = w->prev_trans) + printf(" %#010lx", w->id); + putchar('\n'); fflush(stdout); #endif - } static void @@ -2403,20 +2396,22 @@ restack_win(Display *dpy, win *w, Window new_above) { static void configure_win(Display *dpy, XConfigureEvent *ce) { + if (ce->window == root) { + if (tgt_buffer) { + XRenderFreePicture(dpy, tgt_buffer); + tgt_buffer = None; + } + root_width = ce->width; + root_height = ce->height; + + return; + } + win *w = find_win(dpy, ce->window); XserverRegion damage = None; - if (!w) { - if (ce->window == root) { - if (tgt_buffer) { - XRenderFreePicture(dpy, tgt_buffer); - tgt_buffer = None; - } - root_width = ce->width; - root_height = ce->height; - } + if (!w) return; - } if (w->a.map_state == IsUnmapped) { /* save the configure event for when the window maps */ From 47f54a1cb78bb53552ea0a0277b1604d1eaad482 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 31 Oct 2012 08:54:09 +0800 Subject: [PATCH 071/178] Improvement: Try to reduce reg_ignore regenerations - Try to reduce regenerations of reg_ignore. Highly experimental and could lead to very obscure bugs. More testing needed. - Introduce to_paint in struct _win to keep track of whether this window was painted last time. - Drop CAN_DO_USABLE support. Its usage looks pretty limited. - Fix a bug that possibly causes rendering issues on frame width changes if frame_opacity is enabled. - Detect other borders (instead of only top border) when determining frame opacity. - Change the type of w->mode from int to an enumeration type. - Ignore ShapeNotify if the window is not mapped, to avoid loss of w->border_size in some cases, which breaks the fading out process of shaped windows. - Stop rendering a window if its picture is lost and it's unmapped, to avoid a series of X errors and possible rendering problems. --- compton.c | 296 +++++++++++++++++++++++------------------------------- compton.h | 33 ++++-- 2 files changed, 150 insertions(+), 179 deletions(-) diff --git a/compton.c b/compton.c index 52dd58a83..3a1810f8f 100644 --- a/compton.c +++ b/compton.c @@ -1324,6 +1324,9 @@ get_frame_extents(Display *dpy, win *w, Window client) { w->right_width = extents[1]; w->top_width = extents[2]; w->bottom_width = extents[3]; + + if (opts.frame_opacity) + update_reg_ignore_expire(w); } XFree(data); } @@ -1353,6 +1356,9 @@ paint_preprocess(Display *dpy, win *list) { XserverRegion last_reg_ignore = None; for (w = list; w; w = next) { + Bool to_paint = True; + const winmode mode_old = w->mode; + // In case calling the fade callback function destroys this window next = w->next; opacity_t opacity_old = w->opacity; @@ -1361,133 +1367,148 @@ paint_preprocess(Display *dpy, win *list) { if (reg_ignore_expire) free_region(dpy, &w->reg_ignore); -#if CAN_DO_USABLE - if (!w->usable) continue; -#endif - // Run fading run_fade(dpy, w, steps); - // Give up if it's not damaged or invisible + // Give up if it's not damaged or invisible, or it's unmapped and its + // picture is gone (for example due to a ConfigureNotify) if (!w->damaged || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 - || w->a.x >= root_width || w->a.y >= root_height) { - check_fade_fin(dpy, w); - continue; + || w->a.x >= root_width || w->a.y >= root_height + || (IsUnmapped == w->a.map_state && !w->picture)) { + to_paint = False; } - // If opacity changes - if (w->opacity != opacity_old) { - determine_mode(dpy, w); - add_damage_win(dpy, w); - } + if (to_paint) { + // If opacity changes + if (w->opacity != opacity_old) { + determine_mode(dpy, w); + add_damage_win(dpy, w); + } - w->alpha_pict = get_alpha_pict_o(w->opacity); + w->alpha_pict = get_alpha_pict_o(w->opacity); - // End the game if we are using the 0 opacity alpha_pict - if (w->alpha_pict == alpha_picts[0]) { - check_fade_fin(dpy, w); - continue; + // End the game if we are using the 0 opacity alpha_pict + if (w->alpha_pict == alpha_picts[0]) { + to_paint = False; + } } - // Fetch the picture and pixmap if needed - if (!w->picture) { - XRenderPictureAttributes pa; - XRenderPictFormat *format; - Drawable draw = w->id; + if (to_paint) { + // Fetch the picture and pixmap if needed + if (!w->picture) { + XRenderPictureAttributes pa; + XRenderPictFormat *format; + Drawable draw = w->id; - if (has_name_pixmap && !w->pixmap) { - set_ignore(dpy, NextRequest(dpy)); - w->pixmap = XCompositeNameWindowPixmap(dpy, w->id); + if (has_name_pixmap && !w->pixmap) { + set_ignore(dpy, NextRequest(dpy)); + w->pixmap = XCompositeNameWindowPixmap(dpy, w->id); + } + if (w->pixmap) draw = w->pixmap; + + format = XRenderFindVisualFormat(dpy, w->a.visual); + pa.subwindow_mode = IncludeInferiors; + w->picture = XRenderCreatePicture( + dpy, draw, format, CPSubwindowMode, &pa); } - if (w->pixmap) draw = w->pixmap; - format = XRenderFindVisualFormat(dpy, w->a.visual); - pa.subwindow_mode = IncludeInferiors; - w->picture = XRenderCreatePicture( - dpy, draw, format, CPSubwindowMode, &pa); - } + // Fetch bounding region and extents if needed + if (!w->border_size) { + w->border_size = border_size(dpy, w); + } - // Fetch bounding region and extents if needed - if (!w->border_size) { - w->border_size = border_size(dpy, w); - } + if (!w->extents) { + w->extents = win_extents(dpy, w); + // If w->extents does not exist, the previous add_damage_win() + // call when opacity changes has no effect, so redo it here. + if (w->opacity != opacity_old) + add_damage_win(dpy, w); + } - if (!w->extents) { - w->extents = win_extents(dpy, w); - // If w->extents does not exist, the previous add_damage_win() - // call when opacity changes has no effect, so redo it here. - if (w->opacity != opacity_old) - add_damage_win(dpy, w); - } + // Calculate frame_opacity + { + double frame_opacity_old = w->frame_opacity; - // Calculate frame_opacity - { - double frame_opacity_old = w->frame_opacity; + if (opts.frame_opacity && 1.0 != opts.frame_opacity + && win_has_frame(w)) + w->frame_opacity = get_opacity_percent(dpy, w) * + opts.frame_opacity; + else + w->frame_opacity = 0.0; - if (opts.frame_opacity && 1.0 != opts.frame_opacity - && w->top_width) - w->frame_opacity = get_opacity_percent(dpy, w) * - opts.frame_opacity; - else - w->frame_opacity = 0.0; + if (w->to_paint && WINDOW_SOLID == mode_old + && (0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) + reg_ignore_expire = True; + } - if ((0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) - reg_ignore_expire = True; - } + w->frame_alpha_pict = get_alpha_pict_d(w->frame_opacity); - w->frame_alpha_pict = get_alpha_pict_d(w->frame_opacity); + // Calculate shadow opacity + if (w->frame_opacity) + w->shadow_opacity = opts.shadow_opacity * w->frame_opacity; + else + w->shadow_opacity = opts.shadow_opacity * get_opacity_percent(dpy, w); - // Calculate shadow opacity - if (w->frame_opacity) - w->shadow_opacity = opts.shadow_opacity * w->frame_opacity; - else - w->shadow_opacity = opts.shadow_opacity * get_opacity_percent(dpy, w); + // Rebuild shadow_pict if necessary + if (w->flags & WFLAG_SIZE_CHANGE) + free_picture(dpy, &w->shadow_pict); - // Rebuild shadow_pict if necessary - if (w->flags & WFLAG_SIZE_CHANGE) - free_picture(dpy, &w->shadow_pict); + if (w->shadow && !w->shadow_pict) { + w->shadow_pict = shadow_picture(dpy, 1, + w->widthb, w->heightb, False); + } - if (w->shadow && !w->shadow_pict) { - w->shadow_pict = shadow_picture(dpy, 1, - w->widthb, w->heightb, False); + w->shadow_alpha_pict = get_alpha_pict_d(w->shadow_opacity); } - w->shadow_alpha_pict = get_alpha_pict_d(w->shadow_opacity); - - // Generate ignore region for painting to reduce GPU load - if (reg_ignore_expire) { - free_region(dpy, &w->reg_ignore); - - // If the window is solid, we add the window region to the - // ignored region - if (WINDOW_SOLID == w->mode) { - if (!w->frame_opacity) - w->reg_ignore = win_get_region(dpy, w); + if ((to_paint && WINDOW_SOLID == w->mode) + != (w->to_paint && WINDOW_SOLID == mode_old)) + reg_ignore_expire = True; + + if (to_paint) { + // Generate ignore region for painting to reduce GPU load + if (reg_ignore_expire || !w->to_paint) { + free_region(dpy, &w->reg_ignore); + + // If the window is solid, we add the window region to the + // ignored region + if (WINDOW_SOLID == w->mode) { + if (!w->frame_opacity) + w->reg_ignore = win_get_region(dpy, w); + else + w->reg_ignore = win_get_region_noframe(dpy, w); + + XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, + w->border_size); + + if (last_reg_ignore) + XFixesUnionRegion(dpy, w->reg_ignore, w->reg_ignore, + last_reg_ignore); + } + // Otherwise we copy the last region over + else if (last_reg_ignore) + w->reg_ignore = copy_region(dpy, last_reg_ignore); else - w->reg_ignore = win_get_region_noframe(dpy, w); + w->reg_ignore = None; + } - XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, - w->border_size); + last_reg_ignore = w->reg_ignore; - if (last_reg_ignore) - XFixesUnionRegion(dpy, w->reg_ignore, w->reg_ignore, - last_reg_ignore); - } - // Otherwise we copy the last region over - else if (last_reg_ignore) - w->reg_ignore = copy_region(dpy, last_reg_ignore); - else - w->reg_ignore = None; + // Reset flags + w->flags = 0; } - last_reg_ignore = w->reg_ignore; - // Reset flags - w->flags = 0; + if (to_paint) { + w->prev_trans = t; + t = w; + } + else { + check_fade_fin(dpy, w); + } - w->prev_trans = t; - t = w; + w->to_paint = to_paint; } return t; @@ -1811,8 +1832,6 @@ map_win(Display *dpy, Window id, // Don't care about window mapping if it's an InputOnly window if (!w || InputOnly == w->a.class) return; - reg_ignore_expire = True; - w->focused = False; w->a.map_state = IsViewable; @@ -1892,9 +1911,6 @@ map_win(Display *dpy, Window id, // shadow determine_shadow(dpy, w); - // Determine mode here just in case the colormap changes - determine_mode(dpy, w); - // Fading in calc_opacity(dpy, w, True); @@ -1912,10 +1928,6 @@ map_win(Display *dpy, Window id, calc_dim(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 = 1; @@ -1936,9 +1948,8 @@ finish_map_win(Display *dpy, win *w) { static void finish_unmap_win(Display *dpy, win *w) { w->damaged = 0; -#if CAN_DO_USABLE - w->usable = False; -#endif + + update_reg_ignore_expire(w); if (w->extents != None) { /* destroys region */ @@ -1947,7 +1958,6 @@ finish_unmap_win(Display *dpy, win *w) { } free_pixmap(dpy, &w->pixmap); - free_picture(dpy, &w->picture); free_region(dpy, &w->border_size); free_picture(dpy, &w->shadow_pict); @@ -1964,8 +1974,6 @@ unmap_win(Display *dpy, Window id, Bool fade) { if (!w) return; - reg_ignore_expire = True; - w->a.map_state = IsUnmapped; // Fading out @@ -2012,7 +2020,7 @@ get_opacity_percent(Display *dpy, win *w) { static void determine_mode(Display *dpy, win *w) { - int mode; + winmode mode; XRenderPictFormat *format; /* if trans prop == -1 fall back on previous tests */ @@ -2032,11 +2040,6 @@ determine_mode(Display *dpy, win *w) { mode = WINDOW_SOLID; } - // Expire reg_ignore if the window mode changes from solid to not, or - // vice versa - if ((WINDOW_SOLID == mode) != (WINDOW_SOLID == w->mode)) - reg_ignore_expire = True; - w->mode = mode; } @@ -2261,9 +2264,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { } new->damaged = 0; -#if CAN_DO_USABLE - new->usable = False; -#endif + new->to_paint = False; new->pixmap = None; new->picture = None; @@ -2309,6 +2310,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->destroyed = False; new->need_configure = False; new->window_type = WINTYPE_UNKNOWN; + new->mode = WINDOW_TRANS; new->prev_trans = NULL; @@ -2423,18 +2425,15 @@ configure_win(Display *dpy, XConfigureEvent *ce) { restack_win(dpy, w, ce->above); } + // Windows restack (including window restacks happened when this + // window is not mapped) could mess up all reg_ignore reg_ignore_expire = True; 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); - } + damage = XFixesCreateRegion(dpy, 0, 0); + if (w->extents != None) { + XFixesCopyRegion(dpy, damage, w->extents); } w->a.x = ce->x; @@ -2555,46 +2554,7 @@ damage_win(Display *dpy, XDamageNotifyEvent *de) { 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) { - if (opts.wintype_fade[w->window_type]) { - set_fade(dpy, w, 0, get_opacity_percent(dpy, w), - opts.fade_in_step, 0, True, True); - } - w->usable = True; - } - } - - if (w->usable) -#endif - repair_win(dpy, w); + repair_win(dpy, w); } static int @@ -3111,7 +3071,7 @@ ev_damage_notify(XDamageNotifyEvent *ev) { inline static void ev_shape_notify(XShapeEvent *ev) { win *w = find_win(dpy, ev->window); - if (!w) return; + if (!w || IsUnmapped == w->a.map_state) return; /* * Empty border_size may indicated an diff --git a/compton.h b/compton.h index c70e28779..8d17dbacf 100644 --- a/compton.h +++ b/compton.h @@ -6,8 +6,6 @@ // === Options === -#define CAN_DO_USABLE 0 - // Debug options, enable them using -D in CFLAGS // #define DEBUG_REPAINT 1 // #define DEBUG_EVENTS 1 @@ -106,10 +104,6 @@ extern struct timeval time_start; #define OPAQUE 0xffffffff #define REGISTER_PROP "_NET_WM_CM_S" -#define WINDOW_SOLID 0 -#define WINDOW_TRANS 1 -#define WINDOW_ARGB 2 - #define FADE_DELTA_TOLERANCE 0.2 #define SW_OPTI_TOLERANCE 1000 @@ -147,6 +141,12 @@ typedef enum { NUM_WINTYPES } wintype; +typedef enum { + WINDOW_SOLID, + WINDOW_TRANS, + WINDOW_ARGB +} winmode; + typedef struct _ignore { struct _ignore *next; unsigned long sequence; @@ -186,11 +186,7 @@ typedef struct _win { Window client_win; Pixmap pixmap; XWindowAttributes a; -#if CAN_DO_USABLE - Bool usable; /* mapped and all damaged at one point */ - XRectangle damage_bounds; /* bounds of damage */ -#endif - int mode; + winmode mode; int damaged; Damage damage; Picture picture; @@ -207,6 +203,8 @@ typedef struct _win { Bool bounding_shaped; /// Whether the window just have rounded corners. Bool rounded_corners; + /// Whether this window is to be painted + Bool to_paint; // Blacklist related members char *name; @@ -393,6 +391,7 @@ extern int root_height, root_width; extern Atom atom_client_attr; extern Bool idling; extern Bool shape_exists; +extern Bool reg_ignore_expire; /** * Functions @@ -830,6 +829,18 @@ wid_bounding_shaped(Display *dpy, Window wid) { return False; } +static inline void +update_reg_ignore_expire(const win *w) { + if (w->to_paint && WINDOW_SOLID == w->mode) + reg_ignore_expire = True; +} + +static inline bool +win_has_frame(const win *w) { + return w->top_width || w->left_width || w->right_width + || w->bottom_width; +} + static void win_rounded_corners(Display *dpy, win *w); From 987c43bbd37cd654bb183807c2ee2dde6eed018e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 1 Nov 2012 10:43:15 +0800 Subject: [PATCH 072/178] Improvement: border_size & ConfigureNotify & VSync changes - Run XSync() before the final paint to catch VBlank better. Stolen from Xfwm4 VSync patch. - Add --vsync-aggressive that sends out the final painting request earlier, simulating xfwm4 VSync patch. But this thing does have the possibility of breaking VSync, I think... - Change handling of ConfigureNotify to avoid freeing w->extents and w->border_size if possible. - Change logic in paint_prepreprocess() to use win_get_region() for border_size generation instead of border_size() if the window is not shaped to try to avoid some BadRegion error messages when a window loses its border_size then is unmapped, about which Adys complained in #25. - Detect if w->border_size is None before using it in various places. Practically the effect is pretty limited because XFixesCreateRegionFromWindow() usually returns an invalid X ID instead of None on error. - Fix a bug that rounded corner detection could fail if the window size is changed by a ConfigureNotify immediately. --- compton.c | 72 ++++++++++++++++++++++++++++++++++++++++++++----------- compton.h | 6 ++++- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/compton.c b/compton.c index 3a1810f8f..2e9ce2ef4 100644 --- a/compton.c +++ b/compton.c @@ -181,6 +181,7 @@ static options_t opts = { .sw_opti = False, .vsync = VSYNC_NONE, .dbe = False, + .vsync_aggressive = False, .wintype_shadow = { False }, .shadow_red = 0.0, @@ -1413,11 +1414,20 @@ paint_preprocess(Display *dpy, win *list) { dpy, draw, format, CPSubwindowMode, &pa); } - // Fetch bounding region and extents if needed + // Fetch bounding region if (!w->border_size) { - w->border_size = border_size(dpy, w); + // Build a border_size ourselves if window is not shaped, to avoid + // getting an invalid border_size region from X if the window is + // unmapped/destroyed + if (!w->bounding_shaped) { + w->border_size = win_get_region(dpy, w); + } + else if (IsUnmapped != w->a.map_state) { + w->border_size = border_size(dpy, w); + } } + // Fetch window extents if (!w->extents) { w->extents = win_extents(dpy, w); // If w->extents does not exist, the previous add_damage_win() @@ -1479,8 +1489,9 @@ paint_preprocess(Display *dpy, win *list) { else w->reg_ignore = win_get_region_noframe(dpy, w); - XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, - w->border_size); + if (w->border_size) + XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, + w->border_size); if (last_reg_ignore) XFixesUnionRegion(dpy, w->reg_ignore, w->reg_ignore, @@ -1674,7 +1685,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { } // Clear the shadow here instead of in make_shadow() for saving GPU // power and handling shaped windows - if (opts.clear_shadow) + if (opts.clear_shadow && w->border_size) XFixesSubtractRegion(dpy, reg_paint, reg_paint, w->border_size); // Detect if the region is empty before painting @@ -1694,10 +1705,15 @@ paint_all(Display *dpy, XserverRegion region, win *t) { // Copy the subtracted region to be used for shadow painting in next // cycle XFixesCopyRegion(dpy, reg_tmp2, reg_paint); - XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->border_size); + + if (w->border_size) + XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->border_size); } else { - XFixesIntersectRegion(dpy, reg_paint, region, w->border_size); + if (w->border_size) + XFixesIntersectRegion(dpy, reg_paint, region, w->border_size); + else + reg_paint = region; } if (!is_region_empty(dpy, reg_paint)) { @@ -1719,8 +1735,17 @@ paint_all(Display *dpy, XserverRegion region, win *t) { if (!opts.dbe) XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, None); - // Wait for VBlank - vsync_wait(); + if (VSYNC_NONE != opts.vsync) { + // Make sure all previous requests are processed to achieve best + // effect + XSync(dpy, False); + } + + // Wait for VBlank. We could do it aggressively (send the painting + // request and XFlush() on VBlank) or conservatively (send the request + // only on VBlank). + if (!opts.vsync_aggressive) + vsync_wait(); // DBE painting mode, only need to swap the buffer if (opts.dbe) { @@ -1739,6 +1764,9 @@ paint_all(Display *dpy, XserverRegion region, win *t) { 0, 0, root_width, root_height); } + if (opts.vsync_aggressive) + vsync_wait(); + XFlush(dpy); #ifdef DEBUG_REPAINT @@ -2127,7 +2155,7 @@ determine_fade(Display *dpy, win *w) { */ static void win_update_shape(Display *dpy, win *w) { - if (shape_exists && (opts.shadow_ignore_shaped /* || opts.clear_shadow */)) { + if (shape_exists) { // Bool bounding_shaped_old = w->bounding_shaped; w->bounding_shaped = wid_bounding_shaped(dpy, w->id); @@ -2436,6 +2464,13 @@ configure_win(Display *dpy, XConfigureEvent *ce) { XFixesCopyRegion(dpy, damage, w->extents); } + // If window geometry did not change, don't free extents here + if (w->a.x != ce->x || w->a.y != ce->y + || w->a.width != ce->width || w->a.height != ce->height) { + free_region(dpy, &w->extents); + free_region(dpy, &w->border_size); + } + w->a.x = ce->x; w->a.y = ce->y; @@ -2450,6 +2485,11 @@ configure_win(Display *dpy, XConfigureEvent *ce) { w->a.height = ce->height; w->a.border_width = ce->border_width; calc_win_size(dpy, w); + + // Rounded corner detection is affected by window size + if (shape_exists && opts.shadow_ignore_shaped + && opts.detect_rounded_corners && w->bounding_shaped) + win_update_shape(dpy, w); } if (w->a.map_state != IsUnmapped && damage) { @@ -2458,10 +2498,6 @@ configure_win(Display *dpy, XConfigureEvent *ce) { XFixesDestroyRegion(dpy, extents); add_damage(dpy, damage); } - - // Window extents and border_size may have changed - free_region(dpy, &w->extents); - free_region(dpy, &w->border_size); } w->a.override_redirect = ce->override_redirect; @@ -3307,6 +3343,9 @@ usage(void) { "--sw-opti\n" " Limit compton to repaint at most once every 1 / refresh_rate\n" " second to boost performance. Experimental.\n" + "--vsync-aggressive\n" + " Attempt to send painting request before VBlank and do XFlush()\n" + " during VBlank. This switch may be lifted out at any moment.\n" "\n" "Format of a condition:\n" "\n" @@ -3727,6 +3766,7 @@ get_cfg(int argc, char *const *argv) { { "dbe", no_argument, NULL, 272 }, { "paint-on-overlay", no_argument, NULL, 273 }, { "sw-opti", no_argument, NULL, 274 }, + { "vsync-aggressive", no_argument, NULL, 275 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -3909,6 +3949,10 @@ get_cfg(int argc, char *const *argv) { // --sw-opti opts.sw_opti = True; break; + case 275: + // --vsync-aggressive + opts.vsync_aggressive = True; + break; default: usage(); break; diff --git a/compton.h b/compton.h index 8d17dbacf..9ec61fcbb 100644 --- a/compton.h +++ b/compton.h @@ -115,6 +115,8 @@ extern struct timeval time_start; // Window size is changed #define WFLAG_SIZE_CHANGE 0x0001 +// Window size/position is changed +#define WFLAG_POS_CHANGE 0x0002 /** * Types @@ -280,7 +282,7 @@ typedef struct _win { struct _win *prev_trans; } win; -typedef enum _vsync_t { +typedef enum { VSYNC_NONE, VSYNC_DRM, VSYNC_OPENGL, @@ -317,6 +319,8 @@ typedef struct _options { vsync_t vsync; /// Whether to enable double buffer. Bool dbe; + /// Whether to do VSync aggressively. + Bool vsync_aggressive; // Shadow Bool wintype_shadow[NUM_WINTYPES]; From d3e75057a28fd56ea911d90bb9e332db7c91d864 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 1 Nov 2012 19:03:56 +0800 Subject: [PATCH 073/178] Bug fix #17: Try to fix artifacts after animating/shading shaped wins in Openbox - Correct design mistakes in win_get_region_noframe(). I must be sleepwalking when I wrote that thing! - Intersect border_size with window region rectangle because Openbox is offering wrong window shapes larger than the window rectangle for shaped windows. Shame on you, Openbox. - Change logic in reg_ignore calculation as border_size is now intersected with window rectangle and we don't need to do this here again. - Rewrite window painting with frame opacity part in win_paint_win() to deal with absurd frame widths WM offers. Again, Openbox, this is your fault. - As I'm in a pretty bad state (continuously working on compton for 10+ hours without rest...), all these changes are not well tested, and bugs are to be expected. --- compton.c | 145 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 94 insertions(+), 51 deletions(-) diff --git a/compton.c b/compton.c index 2e9ce2ef4..4190c140d 100644 --- a/compton.c +++ b/compton.c @@ -1194,12 +1194,15 @@ static XserverRegion win_get_region_noframe(Display *dpy, win *w) { XRectangle r; - r.x = w->a.x + w->left_width; - r.y = w->a.y + w->top_width; - r.width = w->a.width; - r.height = w->a.height; + r.x = w->a.x + w->a.border_width + w->left_width; + r.y = w->a.y + w->a.border_width + w->top_width; + r.width = max_i(w->a.width - w->left_width - w->right_width, 0); + r.height = max_i(w->a.height - w->top_width - w->bottom_width, 0); - return XFixesCreateRegion(dpy, &r, 1); + if (r.width > 0 && r.height > 0) + return XFixesCreateRegion(dpy, &r, 1); + else + return XFixesCreateRegion(dpy, NULL, 0); } /** @@ -1249,28 +1252,38 @@ win_extents(Display *dpy, win *w) { static XserverRegion border_size(Display *dpy, win *w) { - XserverRegion border; + // Start with the window rectangular region + XserverRegion fin = win_get_region(dpy, w); - /* - * 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. - */ + // Only request for a bounding region if the window is shaped + if (w->bounding_shaped) { + /* + * 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. + */ - border = XFixesCreateRegionFromWindow( - dpy, w->id, WindowRegionBounding); + XserverRegion border = XFixesCreateRegionFromWindow( + dpy, w->id, WindowRegionBounding); - if (!border) - return None; + if (!border) + return fin; - /* translate this */ - XFixesTranslateRegion(dpy, border, - w->a.x + w->a.border_width, - w->a.y + w->a.border_width); + // Translate the region to the correct place + XFixesTranslateRegion(dpy, border, + w->a.x + w->a.border_width, + w->a.y + w->a.border_width); + + // Intersect the bounding region we got with the window rectangle, to + // make sure the bounding region is not bigger than the window + // rectangle + XFixesIntersectRegion(dpy, fin, fin, border); + XFixesDestroyRegion(dpy, border); + } - return border; + return fin; } static Window @@ -1484,14 +1497,18 @@ paint_preprocess(Display *dpy, win *list) { // If the window is solid, we add the window region to the // ignored region if (WINDOW_SOLID == w->mode) { - if (!w->frame_opacity) - w->reg_ignore = win_get_region(dpy, w); - else + if (!w->frame_opacity) { + if (w->border_size) + w->reg_ignore = copy_region(dpy, w->border_size); + else + w->reg_ignore = win_get_region(dpy, w); + } + else { w->reg_ignore = win_get_region_noframe(dpy, w); - - if (w->border_size) - XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, - w->border_size); + if (w->border_size) + XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, + w->border_size); + } if (last_reg_ignore) XFixesUnionRegion(dpy, w->reg_ignore, w->reg_ignore, @@ -1555,33 +1572,59 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { tgt_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->frame_alpha_pict, - tgt_buffer, 0, 0, 0, 0, x, y, wid, t); - - // left - XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - tgt_buffer, 0, t, 0, t, x, y + t, l, hei - t); - - // bottom - XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - tgt_buffer, l, hei - b, l, hei - b, x + l, y + hei - b, wid - l - r, b); + int t = w->top_width; + int l = w->left_width; + int b = w->bottom_width; + int r = w->right_width; - // right - XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, - tgt_buffer, wid - r, t, wid - r, t, x + wid - r, y + t, r, hei - t); +#define COMP_BDR(cx, cy, cwid, chei) \ + XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, \ + tgt_buffer, (cx), (cy), 0, 0, x + (cx), y + (cy), (cwid), (chei)) - // body - XRenderComposite(dpy, op, w->picture, alpha_mask, tgt_buffer, - l, t, l, t, x + l, y + t, wid - l - r, hei - t - b); + // The following complicated logic is requried because some broken + // window managers (I'm talking about you, Openbox!) that makes + // top_width + bottom_width > height in some cases. + // top + COMP_BDR(0, 0, wid, min_i(t, hei)); + + if (hei > t) { + int phei = min_i(hei - t, b); + + // bottom + if (phei) { + assert(phei > 0); + COMP_BDR(0, hei - phei, wid, phei); + + phei = hei - t - phei; + if (phei) { + assert(phei > 0); + // left + COMP_BDR(0, t, min_i(l, wid), phei); + + if (wid > l) { + int pwid = min_i(wid - l, r); + + if (pwid) { + assert(pwid > 0); + // right + COMP_BDR(wid - pwid, t, pwid, phei); + + pwid = wid - l - pwid; + if (pwid) + assert(pwid > 0); + // body + XRenderComposite(dpy, op, w->picture, alpha_mask, + tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei); + } + } + } + } + } } +#undef COMP_BDR + // Dimming the window if needed if (w->dim) { XRenderComposite(dpy, PictOpOver, dim_picture, None, From b7aee48ccae37dfdf5bb7025fac6f394a9abb335 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 3 Nov 2012 05:51:40 +0800 Subject: [PATCH 074/178] Misc: Code cleanup & debug code changes - Merge @daBrado's focus event debugging code. Thanks! - Use macro to reduce code redundancy in various functions. - Move focus event validation from ev_focus_out() to a separate function. - Add logic in ev_handle() to increase the chance of successful window name detection if compton is not reading window names normally (i.e. if there's no --shadow-exclude), when DEBUG_EVENTS is on. --- compton.c | 219 ++++++++++++++++++++++++++---------------------------- compton.h | 2 +- 2 files changed, 106 insertions(+), 115 deletions(-) diff --git a/compton.c b/compton.c index 4190c140d..b98e6a60f 100644 --- a/compton.c +++ b/compton.c @@ -10,6 +10,12 @@ #include "compton.h" +#define MSTR_(s) #s +#define MSTR(s) MSTR_(s) + +// Use #s here to prevent macro expansion +#define CASESTRRET(s) case s: return #s + /** * Shared */ @@ -2651,98 +2657,48 @@ error(Display *dpy, XErrorEvent *ev) { exit(1); } +#define CASESTRRET2(s) case s: name = #s; break + o = ev->error_code - xfixes_error; switch (o) { - case BadRegion: - name = "BadRegion"; - break; - default: - break; + CASESTRRET2(BadRegion); } o = ev->error_code - damage_error; switch (o) { - case BadDamage: - name = "BadDamage"; - break; - default: - break; + CASESTRRET2(BadDamage); } 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; + CASESTRRET2(BadPictFormat); + CASESTRRET2(BadPicture); + CASESTRRET2(BadPictOp); + CASESTRRET2(BadGlyphSet); + CASESTRRET2(BadGlyph); } switch (ev->error_code) { - case BadAccess: - name = "BadAccess"; - break; - case BadAlloc: - name = "BadAlloc"; - break; - case BadAtom: - name = "BadAtom"; - break; - case BadColor: - name = "BadColor"; - break; - case BadCursor: - name = "BadCursor"; - break; - case BadDrawable: - name = "BadDrawable"; - break; - case BadFont: - name = "BadFont"; - break; - case BadGC: - name = "BadGC"; - break; - case BadIDChoice: - name = "BadIDChoice"; - break; - case BadImplementation: - name = "BadImplementation"; - break; - case BadLength: - name = "BadLength"; - break; - case BadMatch: - name = "BadMatch"; - break; - case BadName: - name = "BadName"; - break; - case BadPixmap: - name = "BadPixmap"; - break; - case BadRequest: - name = "BadRequest"; - break; - case BadValue: - name = "BadValue"; - break; - case BadWindow: - name = "BadWindow"; - break; - } + CASESTRRET2(BadAccess); + CASESTRRET2(BadAlloc); + CASESTRRET2(BadAtom); + CASESTRRET2(BadColor); + CASESTRRET2(BadCursor); + CASESTRRET2(BadDrawable); + CASESTRRET2(BadFont); + CASESTRRET2(BadGC); + CASESTRRET2(BadIDChoice); + CASESTRRET2(BadImplementation); + CASESTRRET2(BadLength); + CASESTRRET2(BadMatch); + CASESTRRET2(BadName); + CASESTRRET2(BadPixmap); + CASESTRRET2(BadRequest); + CASESTRRET2(BadValue); + CASESTRRET2(BadWindow); + } + +#undef CASESTRRET2 print_timestamp(); printf("error %d (%s) request %d minor %d serial %lu\n", @@ -2888,34 +2844,22 @@ ev_serial(XEvent *ev) { return NextRequest(ev->xany.display); } -static char * +static const char * ev_name(XEvent *ev) { static char buf[128]; switch (ev->type & 0x7f) { - case FocusIn: - return "FocusIn"; - case FocusOut: - return "FocusOut"; - case CreateNotify: - return "CreateNotify"; - case ConfigureNotify: - return "ConfigureNotify"; - case DestroyNotify: - return "DestroyNotify"; - case MapNotify: - return "Map"; - case UnmapNotify: - return "Unmap"; - case ReparentNotify: - return "Reparent"; - case CirculateNotify: - return "Circulate"; - case Expose: - return "Expose"; - case PropertyNotify: - return "PropertyNotify"; - case ClientMessage: - return "ClientMessage"; + CASESTRRET(FocusIn); + CASESTRRET(FocusOut); + CASESTRRET(CreateNotify); + CASESTRRET(ConfigureNotify); + CASESTRRET(DestroyNotify); + CASESTRRET(MapNotify); + CASESTRRET(UnmapNotify); + CASESTRRET(ReparentNotify); + CASESTRRET(CirculateNotify); + CASESTRRET(Expose); + CASESTRRET(PropertyNotify); + CASESTRRET(ClientMessage); default: if (ev->type == damage_event + XDamageNotify) { return "Damage"; @@ -2969,14 +2913,61 @@ ev_window(XEvent *ev) { return 0; } } + +static inline const char * +ev_focus_mode_name(XFocusChangeEvent* ev) { + switch (ev->mode) { + CASESTRRET(NotifyNormal); + CASESTRRET(NotifyWhileGrabbed); + CASESTRRET(NotifyGrab); + CASESTRRET(NotifyUngrab); + } + + return "Unknown"; +} + +static inline const char * +ev_focus_detail_name(XFocusChangeEvent* ev) { + switch (ev->detail) { + CASESTRRET(NotifyAncestor); + CASESTRRET(NotifyVirtual); + CASESTRRET(NotifyInferior); + CASESTRRET(NotifyNonlinear); + CASESTRRET(NotifyNonlinearVirtual); + CASESTRRET(NotifyPointer); + CASESTRRET(NotifyPointerRoot); + CASESTRRET(NotifyDetailNone); + } + + return "Unknown"; +} + +static inline void +ev_focus_report(XFocusChangeEvent* ev) { + printf(" { mode: %s, detail: %s }\n", ev_focus_mode_name(ev), + ev_focus_detail_name(ev)); +} + #endif /** * Events */ +inline static bool +ev_focus_accept(XFocusChangeEvent *ev) { + return ev->mode == NotifyGrab + || (ev->mode == NotifyNormal + && (ev->detail == NotifyNonlinear + || ev->detail == NotifyNonlinearVirtual)); +} + inline static void ev_focus_in(XFocusChangeEvent *ev) { +#ifdef DEBUG_EVENTS + ev_focus_report(ev); +#endif + win *w = find_win(dpy, ev->window); // To deal with events sent from windows just destroyed @@ -2987,14 +2978,12 @@ ev_focus_in(XFocusChangeEvent *ev) { inline static void ev_focus_out(XFocusChangeEvent *ev) { - if (ev->mode == NotifyGrab - || (ev->mode == NotifyNormal - && (ev->detail == NotifyNonlinear - || ev->detail == NotifyNonlinearVirtual))) { - ; - } else { +#ifdef DEBUG_EVENTS + ev_focus_report(ev); +#endif + + if (!ev_focus_accept(ev)) return; - } win *w = find_win(dpy, ev->window); @@ -3012,7 +3001,7 @@ ev_create_notify(XCreateWindowEvent *ev) { inline static void ev_configure_notify(XConfigureEvent *ev) { #ifdef DEBUG_EVENTS - printf("{ send_event: %d, " + printf(" { send_event: %d, " " above: %#010lx, " " override_redirect: %d }\n", ev->send_event, ev->above, ev->override_redirect); @@ -3214,8 +3203,10 @@ ev_handle(XEvent *ev) { if (w && w->name) window_name = w->name; - else - to_free = (Bool) wid_get_name(dpy, wid, &window_name); + else if (!(w && w->client_win + && (to_free = (Bool) wid_get_name(dpy, w->client_win, + &window_name)))) + to_free = (Bool) wid_get_name(dpy, wid, &window_name); } } diff --git a/compton.h b/compton.h index 9ec61fcbb..d78e64fe1 100644 --- a/compton.h +++ b/compton.h @@ -1009,7 +1009,7 @@ win_get_class(Display *dpy, win *w); static int ev_serial(XEvent *ev); -static char * +static const char * ev_name(XEvent *ev); static Window From 1dd73826f941d2034d763e47280e527e5c731309 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 3 Nov 2012 10:30:32 +0800 Subject: [PATCH 075/178] Bug fix: A XserverRegion leak - Fix a XserverRegion leak introduced in b78ab316fd. I hope this is the reason of the slowdowns many users reported. Thanks to xrestop! - Cache the screen region as a variable. - Add debugging code for region allocation. --- compton.c | 19 ++++++++++++++++++- compton.h | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index b98e6a60f..235ef6ed5 100644 --- a/compton.c +++ b/compton.c @@ -69,6 +69,8 @@ Picture root_tile; XserverRegion all_damage; Bool has_name_pixmap; int root_height, root_width; +/// A region of the size of the screen. +XserverRegion screen_reg = None; /// Pregenerated alpha pictures. Picture *alpha_picts = NULL; @@ -1638,6 +1640,16 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { } } +/** + * Rebuild cached screen_reg. + */ +static void +rebuild_screen_reg(Display *dpy) { + if (screen_reg) + XFixesDestroyRegion(dpy, screen_reg); + screen_reg = get_screen_region(dpy); +} + static void paint_all(Display *dpy, XserverRegion region, win *t) { #ifdef DEBUG_REPAINT @@ -1652,7 +1664,7 @@ paint_all(Display *dpy, XserverRegion region, win *t) { } else { // Remove the damaged area out of screen - XFixesIntersectRegion(dpy, region, region, get_screen_region(dpy)); + XFixesIntersectRegion(dpy, region, region, screen_reg); } #ifdef MONITOR_REPAINT @@ -2483,6 +2495,8 @@ configure_win(Display *dpy, XConfigureEvent *ce) { root_width = ce->width; root_height = ce->height; + rebuild_screen_reg(dpy); + return; } @@ -2995,6 +3009,7 @@ ev_focus_out(XFocusChangeEvent *ev) { inline static void ev_create_notify(XCreateWindowEvent *ev) { + assert(ev->parent == root); add_win(dpy, ev->window, 0, ev->override_redirect); } @@ -4520,6 +4535,8 @@ main(int argc, char **argv) { root_width = DisplayWidth(dpy, scr); root_height = DisplayHeight(dpy, scr); + rebuild_screen_reg(dpy); + root_picture = XRenderCreatePicture(dpy, root, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), CPSubwindowMode, &pa); diff --git a/compton.h b/compton.h index d78e64fe1..3bed918a4 100644 --- a/compton.h +++ b/compton.h @@ -89,6 +89,26 @@ #include #endif +#ifdef DEBUG_ALLOC_REG +static inline XserverRegion +XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, + const char *func, int line) { + XserverRegion reg = XFixesCreateRegion(dpy, p, n); + printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); + return reg; +} + +static inline void +XFixesDestroyRegion_(Display *dpy, XserverRegion reg, + const char *func, int line) { + XFixesDestroyRegion(dpy, reg); + printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); +} + +#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) +#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) +#endif + // === Constants === #if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) #error libXcomposite version unsupported From 53aeb801d4a22fc3e80cc1953a52dd492e77165f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 3 Nov 2012 22:02:07 +0800 Subject: [PATCH 076/178] Improvement: Focus detection & --mark-ovredir-focused changes - Change focus detection to filter FocusIn/Out events in an alternative way. Could break things, and may be revoked in the future. - Change --mark-ovredir-focused's semantics. Now it determines whether a window is to be unconditionally focused by checking if its client window is the window itself. This resolves a problem in window managers that set frame windows to be override-redirected (Fluxbox and i3?). Yes, I know it's bad to change semantics in this way. --- compton.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/compton.c b/compton.c index 235ef6ed5..8e7500cb2 100644 --- a/compton.c +++ b/compton.c @@ -1992,7 +1992,7 @@ map_win(Display *dpy, Window id, // focused if mark_wmwin_focused is on, or it's over-redirected and // mark_ovredir_focused is on if ((opts.mark_wmwin_focused && !w->client_win) - || (opts.mark_ovredir_focused && w->a.override_redirect)) + || (opts.mark_ovredir_focused && w->id == w->client_win)) w->focused = True; } @@ -2968,12 +2968,14 @@ ev_focus_report(XFocusChangeEvent* ev) { * Events */ +/** + * Determine whether we should respond to a FocusIn/Out + * event. + */ inline static bool ev_focus_accept(XFocusChangeEvent *ev) { - return ev->mode == NotifyGrab - || (ev->mode == NotifyNormal - && (ev->detail == NotifyNonlinear - || ev->detail == NotifyNonlinearVirtual)); + return ev->detail == NotifyNonlinear + || ev->detail == NotifyNonlinearVirtual; } inline static void @@ -2982,6 +2984,9 @@ ev_focus_in(XFocusChangeEvent *ev) { ev_focus_report(ev); #endif + if (!ev_focus_accept(ev)) + return; + win *w = find_win(dpy, ev->window); // To deal with events sent from windows just destroyed @@ -3357,7 +3362,7 @@ usage(void) { "--shadow-exclude condition\n" " Exclude conditions for shadows.\n" "--mark-ovredir-focused\n" - " Mark over-redirect windows as active.\n" + " Mark windows that have no WM frame as active.\n" "--no-fading-openclose\n" " Do not fade on window open/close.\n" "--shadow-ignore-shaped\n" From 86103c5cebcd962156edebe7f58fec8786d84277 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 4 Nov 2012 18:11:08 +0800 Subject: [PATCH 077/178] Improvement: Add EWMH _NET_WM_ACTIVE_WINDOW support - Add support for using _NET_WM_ACTIVE_WINDOW to track focus (rather than using FocusIn/Out events) as --use-ewmh-active-win. - Add wid_get_attr() and use it in place of XGetWindowProperty() in various places. - Remove dpy parameter from find_win() and find_toplevel() as it's unused. - Fix a writing-to-freed-memory issue in paint_preprocess(). --- compton.c | 238 +++++++++++++++++++++++++++++++++--------------------- compton.h | 71 +++++++++++++++- 2 files changed, 215 insertions(+), 94 deletions(-) diff --git a/compton.c b/compton.c index 8e7500cb2..032f2d3ea 100644 --- a/compton.c +++ b/compton.c @@ -71,6 +71,9 @@ Bool has_name_pixmap; int root_height, root_width; /// A region of the size of the screen. XserverRegion screen_reg = None; +/// Current active window. Used by EWMH _NET_ACTIVE_WINDOW focus +/// detection. +win *active_win = NULL; /// Pregenerated alpha pictures. Picture *alpha_picts = NULL; @@ -153,14 +156,14 @@ int size_expose = 0; int n_expose = 0; // atoms -Atom extents_atom; -Atom opacity_atom; -Atom frame_extents_atom; -Atom client_atom; -Atom name_atom; -Atom name_ewmh_atom; -Atom class_atom; -Atom transient_atom; +Atom opacity_atom = None; +Atom frame_extents_atom = None; +Atom client_atom = None; +Atom name_atom = None; +Atom name_ewmh_atom = None; +Atom class_atom = None; +Atom transient_atom = None; +Atom ewmh_active_win_atom = None;; Atom win_type_atom; Atom win_type[NUM_WINTYPES]; @@ -217,6 +220,7 @@ static options_t opts = { .detect_client_opacity = False, .inactive_dim = 0.0, .alpha_step = 0.03, + .use_ewmh_active_win = False, .track_focus = False, .track_wdata = False, @@ -998,12 +1002,13 @@ static long determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { long evmask = NoEventMask; - if (WIN_EVMODE_FRAME == mode || find_win(dpy, wid)) { + if (WIN_EVMODE_FRAME == mode || find_win(wid)) { evmask |= PropertyChangeMask; - if (opts.track_focus) evmask |= FocusChangeMask; + if (opts.track_focus && !opts.use_ewmh_active_win) + evmask |= FocusChangeMask; } - if (WIN_EVMODE_CLIENT == mode || find_toplevel(dpy, wid)) { + if (WIN_EVMODE_CLIENT == mode || find_toplevel(wid)) { if (opts.frame_opacity || opts.track_wdata || opts.detect_client_opacity) evmask |= PropertyChangeMask; @@ -1013,7 +1018,7 @@ determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { } static win * -find_win(Display *dpy, Window id) { +find_win(Window id) { win *w; for (w = list; w; w = w->next) { @@ -1027,12 +1032,11 @@ find_win(Display *dpy, Window id) { /** * Find out the WM frame of a client window using existing data. * - * @param dpy display to use * @param w window ID * @return struct _win object of the found window, NULL if not found */ static win * -find_toplevel(Display *dpy, Window id) { +find_toplevel(Window id) { win *w; for (w = list; w; w = w->next) { @@ -1055,7 +1059,7 @@ find_toplevel2(Display *dpy, Window wid) { win *w = NULL; // We traverse through its ancestors to find out the frame - while (wid && wid != root && !(w = find_win(dpy, wid))) { + while (wid && wid != root && !(w = find_win(wid))) { Window troot; Window parent; Window *tchildren; @@ -1087,6 +1091,12 @@ find_toplevel2(Display *dpy, Window wid) { */ static win * recheck_focus(Display *dpy) { + // Use EWMH _NET_ACTIVE_WINDOW if enabled + if (opts.use_ewmh_active_win) { + update_ewmh_active_win(dpy); + return active_win; + } + // Determine the currently focused window so we can apply appropriate // opacity on it Window wid = 0; @@ -1096,7 +1106,7 @@ recheck_focus(Display *dpy) { XGetInputFocus(dpy, &wid, &revert_to); // Fallback to the old method if find_toplevel() fails - if (!(w = find_toplevel(dpy, wid))) { + if (!(w = find_toplevel(wid))) { w = find_toplevel2(dpy, wid); } @@ -1117,33 +1127,24 @@ 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; + // Get the values of background attributes for (p = 0; background_props[p]; p++) { - prop = NULL; - 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); + winattr_t attr = wid_get_attr(dpy, root, + XInternAtom(dpy, background_props[p], False), 1L, XA_PIXMAP, 32); + if (attr.nitems) { + pixmap = *((long *) attr.data); fill = False; + free_winattr(&attr); break; - } else if (prop) - XFree(prop); + } + free_winattr(&attr); } if (!pixmap) { @@ -1321,37 +1322,26 @@ find_client_win(Display *dpy, Window w) { static void get_frame_extents(Display *dpy, win *w, Window client) { - long *extents; - Atom type; - int format; - unsigned long nitems, after; - unsigned char *data = NULL; - int result; - w->left_width = 0; w->right_width = 0; w->top_width = 0; w->bottom_width = 0; - result = XGetWindowProperty( - dpy, client, frame_extents_atom, - 0L, 4L, False, AnyPropertyType, - &type, &format, &nitems, &after, - &data); - - if (result == Success) { - if (nitems == 4 && after == 0) { - extents = (long *) data; - w->left_width = extents[0]; - w->right_width = extents[1]; - w->top_width = extents[2]; - w->bottom_width = extents[3]; - - if (opts.frame_opacity) - update_reg_ignore_expire(w); - } - XFree(data); + winattr_t attr = wid_get_attr(dpy, client, frame_extents_atom, + 4L, XA_CARDINAL, 32); + + if (4 == attr.nitems) { + long *extents = (long *) attr.data; + w->left_width = extents[0]; + w->right_width = extents[1]; + w->top_width = extents[2]; + w->bottom_width = extents[3]; + + if (opts.frame_opacity) + update_reg_ignore_expire(w); } + + free_winattr(&attr); } static inline Picture @@ -1539,12 +1529,18 @@ paint_preprocess(Display *dpy, win *list) { if (to_paint) { w->prev_trans = t; t = w; + w->to_paint = to_paint; } else { - check_fade_fin(dpy, w); + // Avoid setting w->to_paint if w is to be freed + if (!(w->opacity_tgt == w->opacity && w->destroyed)) { + check_fade_fin(dpy, w); + w->to_paint = to_paint; + } + else { + check_fade_fin(dpy, w); + } } - - w->to_paint = to_paint; } return t; @@ -1916,7 +1912,7 @@ static void map_win(Display *dpy, Window id, unsigned long sequence, Bool fade, Bool override_redirect) { - win *w = find_win(dpy, id); + win *w = find_win(id); // Don't care about window mapping if it's an InputOnly window if (!w || InputOnly == w->a.class) return; @@ -1980,14 +1976,15 @@ map_win(Display *dpy, Window id, win_get_class(dpy, w); } - /* - * Occasionally compton does not seem able to get a FocusIn event from a - * window just mapped. I suspect it's a timing issue again when the - * XSelectInput() is called too late. We have to recheck the focused - * window here. - */ if (opts.track_focus) { - recheck_focus(dpy); + // Occasionally compton does not seem able to get a FocusIn event from + // a window just mapped. I suspect it's a timing issue again when the + // XSelectInput() is called too late. We have to recheck the focused + // window here. It makes no sense if we are using EWMH + // _NET_ACTIVE_WINDOW. + if (!opts.use_ewmh_active_win) + recheck_focus(dpy); + // Consider a window without client window a WM window and mark it // focused if mark_wmwin_focused is on, or it's over-redirected and // mark_ovredir_focused is on @@ -2059,7 +2056,7 @@ unmap_callback(Display *dpy, win *w) { static void unmap_win(Display *dpy, Window id, Bool fade) { - win *w = find_win(dpy, id); + win *w = find_win(id); if (!w) return; @@ -2326,7 +2323,7 @@ mark_client_win(Display *dpy, win *w, Window client) { static void add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { - if (find_win(dpy, id)) { + if (find_win(id)) { return; } @@ -2500,7 +2497,7 @@ configure_win(Display *dpy, XConfigureEvent *ce) { return; } - win *w = find_win(dpy, ce->window); + win *w = find_win(ce->window); XserverRegion damage = None; if (!w) @@ -2568,7 +2565,7 @@ configure_win(Display *dpy, XConfigureEvent *ce) { static void circulate_win(Display *dpy, XCirculateEvent *ce) { - win *w = find_win(dpy, ce->window); + win *w = find_win(ce->window); Window new_above; if (!w) return; @@ -2591,6 +2588,10 @@ finish_destroy_win(Display *dpy, Window id) { finish_unmap_win(dpy, w); *prev = w->next; + // Clear active_win if it's pointing to the destroyed window + if (active_win) + active_win = NULL; + free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); free_region(dpy, &w->reg_ignore); @@ -2611,7 +2612,7 @@ destroy_callback(Display *dpy, win *w) { static void destroy_win(Display *dpy, Window id, Bool fade) { - win *w = find_win(dpy, id); + win *w = find_win(id); if (w) { w->destroyed = True; @@ -2649,7 +2650,7 @@ damage_win(Display *dpy, XDamageNotifyEvent *de) { return; } */ - win *w = find_win(dpy, de->drawable); + win *w = find_win(de->drawable); if (!w) return; @@ -2987,7 +2988,7 @@ ev_focus_in(XFocusChangeEvent *ev) { if (!ev_focus_accept(ev)) return; - win *w = find_win(dpy, ev->window); + win *w = find_win(ev->window); // To deal with events sent from windows just destroyed if (!w) return; @@ -3004,7 +3005,7 @@ ev_focus_out(XFocusChangeEvent *ev) { if (!ev_focus_accept(ev)) return; - win *w = find_win(dpy, ev->window); + win *w = find_win(ev->window); // To deal with events sent from windows just destroyed if (!w) return; @@ -3095,16 +3096,58 @@ ev_expose(XExposeEvent *ev) { } } +/** + * Update current active window based on EWMH _NET_ACTIVE_WIN. + * + * Does not change anything if we fail to get the attribute or the window + * returned could not be found. + */ +static void +update_ewmh_active_win(Display *dpy) { + // Get the attribute firstly + winattr_t attr = wid_get_attr(dpy, root, ewmh_active_win_atom, + 1L, XA_WINDOW, 32); + if (!attr.nitems) { + free_winattr(&attr); + return; + } + + // Search for the window + Window wid = *((long *) attr.data); + win *w = NULL; + free_winattr(&attr); + + if (!(w = find_toplevel(wid))) + if (!(w = find_win(wid))) + w = find_toplevel2(dpy, wid); + + // Mark the window focused + if (w) { + if (!w->focused) + set_focused(dpy, w, True); + if (active_win && w != active_win) + set_focused(dpy, active_win, False); + active_win = w; + } +} + inline static void ev_property_notify(XPropertyEvent *ev) { - // Destroy the root "image" if the wallpaper probably changed if (root == ev->window) { - for (int p = 0; background_props[p]; p++) { - if (ev->atom == XInternAtom(dpy, background_props[p], False)) { - root_damaged(); - break; + if (opts.track_focus && opts.use_ewmh_active_win + && ewmh_active_win_atom == ev->atom) { + update_ewmh_active_win(dpy); + } + else { + // Destroy the root "image" if the wallpaper probably changed + for (int p = 0; background_props[p]; p++) { + if (ev->atom == XInternAtom(dpy, background_props[p], False)) { + root_damaged(); + break; + } } } + // Unconcerned about any other proprties on root window return; } @@ -3112,10 +3155,10 @@ ev_property_notify(XPropertyEvent *ev) { // If _NET_WM_OPACITY changes if (ev->atom == opacity_atom) { win *w = NULL; - if ((w = find_win(dpy, ev->window))) + if ((w = find_win(ev->window))) w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); else if (opts.detect_client_opacity - && (w = find_toplevel(dpy, ev->window))) + && (w = find_toplevel(ev->window))) w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, OPAQUE); if (w) { @@ -3124,8 +3167,8 @@ ev_property_notify(XPropertyEvent *ev) { } // If frame extents property changes - if (opts.frame_opacity && ev->atom == extents_atom) { - win *w = find_toplevel(dpy, ev->window); + if (opts.frame_opacity && ev->atom == frame_extents_atom) { + win *w = find_toplevel(ev->window); if (w) { get_frame_extents(dpy, w, ev->window); // If frame extents change, the window needs repaint @@ -3136,14 +3179,14 @@ ev_property_notify(XPropertyEvent *ev) { // If name changes if (opts.track_wdata && (name_atom == ev->atom || name_ewmh_atom == ev->atom)) { - win *w = find_toplevel(dpy, ev->window); + win *w = find_toplevel(ev->window); if (w && 1 == win_get_name(dpy, w)) determine_shadow(dpy, w); } // If class changes if (opts.track_wdata && class_atom == ev->atom) { - win *w = find_toplevel(dpy, ev->window); + win *w = find_toplevel(ev->window); if (w) { win_get_class(dpy, w); determine_shadow(dpy, w); @@ -3158,7 +3201,7 @@ ev_damage_notify(XDamageNotifyEvent *ev) { inline static void ev_shape_notify(XShapeEvent *ev) { - win *w = find_win(dpy, ev->window); + win *w = find_win(ev->window); if (!w || IsUnmapped == w->a.map_state) return; /* @@ -3217,9 +3260,9 @@ ev_handle(XEvent *ev) { else if (overlay == wid) window_name = "(Overlay)"; else { - win *w = find_win(dpy, wid); + win *w = find_win(wid); if (!w) - w = find_toplevel(dpy, wid); + w = find_toplevel(wid); if (w && w->name) window_name = w->name; @@ -3400,6 +3443,9 @@ usage(void) { "--vsync-aggressive\n" " Attempt to send painting request before VBlank and do XFlush()\n" " during VBlank. This switch may be lifted out at any moment.\n" + "--use-ewmh-active-win\n" + " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n" + " window is focused instead of using FocusIn/Out events.\n" "\n" "Format of a condition:\n" "\n" @@ -3751,6 +3797,9 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { lcfg_lookup_bool(&cfg, "paint-on-overlay", &opts.paint_on_overlay); // --sw-opti lcfg_lookup_bool(&cfg, "sw-opti", &opts.sw_opti); + // --use-ewmh-active-win + lcfg_lookup_bool(&cfg, "use-ewmh-active-win", + &opts.use_ewmh_active_win); // --shadow-exclude { config_setting_t *setting = @@ -3821,6 +3870,7 @@ get_cfg(int argc, char *const *argv) { { "paint-on-overlay", no_argument, NULL, 273 }, { "sw-opti", no_argument, NULL, 274 }, { "vsync-aggressive", no_argument, NULL, 275 }, + { "use-ewmh-active-win", no_argument, NULL, 276 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4007,6 +4057,10 @@ get_cfg(int argc, char *const *argv) { // --vsync-aggressive opts.vsync_aggressive = True; break; + case 276: + // --use-ewmh-active-win + opts.use_ewmh_active_win = True; + break; default: usage(); break; @@ -4060,7 +4114,6 @@ get_cfg(int argc, char *const *argv) { static void get_atoms(void) { - extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); client_atom = XInternAtom(dpy, "WM_STATE", False); @@ -4068,6 +4121,7 @@ get_atoms(void) { name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); class_atom = XA_WM_CLASS; transient_atom = XA_WM_TRANSIENT_FOR; + ewmh_active_win_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); diff --git a/compton.h b/compton.h index 3bed918a4..b3a051630 100644 --- a/compton.h +++ b/compton.h @@ -169,6 +169,11 @@ typedef enum { WINDOW_ARGB } winmode; +typedef struct { + unsigned char *data; + int nitems; +} winattr_t; + typedef struct _ignore { struct _ignore *next; unsigned long sequence; @@ -385,6 +390,8 @@ typedef struct _options { double inactive_dim; /// Step for pregenerating alpha pictures. 0.01 - 1.0. double alpha_step; + /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. + Bool use_ewmh_active_win; // Calculated /// Whether compton needs to track focus changes. @@ -811,6 +818,63 @@ wid_has_attr(Display *dpy, Window w, Atom atom) { return False; } +/** + * Get a specific attribute of a window. + * + * Returns a blank structure if the returned type and format does not + * match the requested type and format. + * + * @param dpy Display to use + * @param w window + * @param atom atom of attribute to fetch + * @param length length to read + * @param rtype atom of the requested type + * @param rformat requested format + * @return a winattr_t structure containing the attribute + * and number of items. A blank one on failure. + */ +static winattr_t +wid_get_attr(Display *dpy, Window w, Atom atom, long length, + Atom rtype, int rformat) { + Atom type = None; + int format = 0; + unsigned long nitems = 0, after = 0; + unsigned char *data = NULL; + + // Use two if statements to deal with the sequence point issue. + if (Success == XGetWindowProperty(dpy, w, atom, 0L, length, False, + rtype, &type, &format, &nitems, &after, &data)) { + if (type == rtype && format == rformat) { + return (winattr_t) { + .data = data, + .nitems = nitems + }; + } + } + + XFree(data); + + return (winattr_t) { + .data = NULL, + .nitems = 0 + }; +} + +/** + * Free a winattr_t. + * + * @param pattr pointer to the winattr_t to free. + */ +static inline void +free_winattr(winattr_t *pattr) { + // Empty the whole structure to avoid possible issues + if (pattr->data) { + XFree(pattr->data); + pattr->data = NULL; + } + pattr->nitems = 0; +} + /** * Get the children of a window. * @@ -881,10 +945,10 @@ static long determine_evmask(Display *dpy, Window wid, win_evmode_t mode); static win * -find_win(Display *dpy, Window id); +find_win(Window id); static win * -find_toplevel(Display *dpy, Window id); +find_toplevel(Window id); static win * find_toplevel2(Display *dpy, Window wid); @@ -1072,6 +1136,9 @@ ev_circulate_notify(XCirculateEvent *ev); inline static void ev_expose(XExposeEvent *ev); +static void +update_ewmh_active_win(Display *dpy); + inline static void ev_property_notify(XPropertyEvent *ev); From fe5537df9e3b3b443a8464c2df4e0a70c7900e2b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 4 Nov 2012 19:14:21 +0800 Subject: [PATCH 078/178] Bug fix #61: Silence a warning - Silence a FORTIFY_SOURCE warning. Thanks to @smlx for reporting. - Add -O2 -D_FORTIFY_SOURCE=2 to default CFLAGS in Makefile. - Use a cleaner way to resolve the writing-to-freed-memory issue mentioned in last commit. --- compton.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/compton.c b/compton.c index 032f2d3ea..9b39fc83e 100644 --- a/compton.c +++ b/compton.c @@ -1526,21 +1526,19 @@ paint_preprocess(Display *dpy, win *list) { } + // Avoid setting w->to_paint if w is to be freed + bool destroyed = (w->opacity_tgt == w->opacity && w->destroyed); + if (to_paint) { w->prev_trans = t; t = w; - w->to_paint = to_paint; } else { - // Avoid setting w->to_paint if w is to be freed - if (!(w->opacity_tgt == w->opacity && w->destroyed)) { - check_fade_fin(dpy, w); - w->to_paint = to_paint; - } - else { - check_fade_fin(dpy, w); - } + check_fade_fin(dpy, w); } + + if (!destroyed) + w->to_paint = to_paint; } return t; @@ -3567,9 +3565,11 @@ fork_after(void) { setsid(); - freopen("/dev/null", "r", stdin); - freopen("/dev/null", "w", stdout); - freopen("/dev/null", "w", stderr); + // Mainly to suppress the _FORTIFY_SOURCE warning + if (!freopen("/dev/null", "r", stdin) + || !freopen("/dev/null", "w", stdout) + || !freopen("/dev/null", "w", stderr)) + fprintf(stderr, "fork_after(): freopen() failed."); } #ifdef CONFIG_LIBCONFIG From 798c631e2b3f08fd3d61b766f71507a896bbbe6f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 6 Nov 2012 11:32:02 +0800 Subject: [PATCH 079/178] Bug fix: Window rendered incorrectly if has no border with -e - Fix a bug that window content and some borders are not rendered if the window has no border on particular sides when -e (frame-opacity) is enabled, introduced in 66d3f30978. Just as what I said in the commit message of the commit, "bugs are to be expected". :-S - Fix a potential segfault in win_match(). - Slightly update CPackConfig.cmake to add dependency to libdrm. --- compton.c | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/compton.c b/compton.c index 9b39fc83e..6b25c0fcc 100644 --- a/compton.c +++ b/compton.c @@ -866,7 +866,8 @@ win_match(win *w, wincond *condlst, wincond **cache) { // Then go through the whole linked list for (; condlst; condlst = condlst->next) { if (win_match_once(w, condlst)) { - *cache = condlst; + if (cache) + *cache = condlst; return true; } } @@ -1583,42 +1584,41 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, \ tgt_buffer, (cx), (cy), 0, 0, x + (cx), y + (cy), (cwid), (chei)) - // The following complicated logic is requried because some broken + // The following complicated logic is required because some broken // window managers (I'm talking about you, Openbox!) that makes // top_width + bottom_width > height in some cases. // top - COMP_BDR(0, 0, wid, min_i(t, hei)); + int phei = min_i(t, hei); + if (phei > 0) + COMP_BDR(0, 0, wid, phei); if (hei > t) { - int phei = min_i(hei - t, b); + phei = min_i(hei - t, b); // bottom - if (phei) { - assert(phei > 0); + if (phei > 0) COMP_BDR(0, hei - phei, wid, phei); - phei = hei - t - phei; - if (phei) { - assert(phei > 0); - // left - COMP_BDR(0, t, min_i(l, wid), phei); - - if (wid > l) { - int pwid = min_i(wid - l, r); - - if (pwid) { - assert(pwid > 0); - // right - COMP_BDR(wid - pwid, t, pwid, phei); - - pwid = wid - l - pwid; - if (pwid) - assert(pwid > 0); - // body - XRenderComposite(dpy, op, w->picture, alpha_mask, - tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei); - } + phei = hei - t - phei; + if (phei > 0) { + int pwid = min_i(l, wid); + // left + if (pwid > 0) + COMP_BDR(0, t, pwid, phei); + + if (wid > l) { + pwid = min_i(wid - l, r); + + // right + if (pwid > 0) + COMP_BDR(wid - pwid, t, pwid, phei); + + pwid = wid - l - pwid; + if (pwid > 0) { + // body + XRenderComposite(dpy, op, w->picture, alpha_mask, + tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei); } } } From baed7f4b0c211ea2ff04bf9e009ab6e334dad3e4 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 8 Nov 2012 19:39:13 +0800 Subject: [PATCH 080/178] Bug fix: Shape update of opaque windows not handled correctly - Fix a bug that causes rendering issues if a opaque window changes shape. - Remove abundant handling code in paint_preprocess() when generating border_size as border_size() is no longer relying on XFixesCreateRegionFromWindow() right now by default. - Add extra code to print backtrace in DEBUG_ALLOC_REG. - Move initialization of fade_time closer to first paint. --- compton.c | 16 +++++----------- compton.h | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/compton.c b/compton.c index 6b25c0fcc..cc0a03c11 100644 --- a/compton.c +++ b/compton.c @@ -1428,15 +1428,7 @@ paint_preprocess(Display *dpy, win *list) { // Fetch bounding region if (!w->border_size) { - // Build a border_size ourselves if window is not shaped, to avoid - // getting an invalid border_size region from X if the window is - // unmapped/destroyed - if (!w->bounding_shaped) { - w->border_size = win_get_region(dpy, w); - } - else if (IsUnmapped != w->a.map_state) { - w->border_size = border_size(dpy, w); - } + w->border_size = border_size(dpy, w); } // Fetch window extents @@ -3220,6 +3212,8 @@ ev_shape_notify(XShapeEvent *ev) { // Redo bounding shape detection and rounded corner detection win_update_shape(dpy, w); + + update_reg_ignore_expire(w); } /** @@ -4478,8 +4472,6 @@ main(int argc, char **argv) { get_cfg(argc, argv); - fade_time = get_time_ms(); - dpy = XOpenDisplay(opts.display); if (!dpy) { fprintf(stderr, "Can't open display\n"); @@ -4659,6 +4651,8 @@ main(int argc, char **argv) { reg_ignore_expire = True; + fade_time = get_time_ms(); + t = paint_preprocess(dpy, list); paint_all(dpy, None, t); diff --git a/compton.h b/compton.h index b3a051630..d7ec6a437 100644 --- a/compton.h +++ b/compton.h @@ -89,12 +89,42 @@ #include #endif +static void +print_timestamp(void); + #ifdef DEBUG_ALLOC_REG + +#include +#define BACKTRACE_SIZE 5 + +/** + * Print current backtrace, excluding the first two items. + * + * Stolen from glibc manual. + */ +static inline void +print_backtrace(void) { + void *array[BACKTRACE_SIZE]; + size_t size; + char **strings; + + size = backtrace(array, BACKTRACE_SIZE); + strings = backtrace_symbols(array, size); + + for (size_t i = 2; i < size; i++) + printf ("%s\n", strings[i]); + + free(strings); +} + static inline XserverRegion XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, const char *func, int line) { XserverRegion reg = XFixesCreateRegion(dpy, p, n); + print_timestamp(); printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); + print_backtrace(); + fflush(stdout); return reg; } @@ -102,7 +132,9 @@ static inline void XFixesDestroyRegion_(Display *dpy, XserverRegion reg, const char *func, int line) { XFixesDestroyRegion(dpy, reg); + print_timestamp(); printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); + fflush(stdout); } #define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) @@ -668,7 +700,7 @@ get_time_timespec(void) { * * Used for debugging. */ -static inline void +static void print_timestamp(void) { struct timeval tm, diff; From 295bbf30c2cca975bcfd774fab61c3158ef460fc Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 9 Nov 2012 11:35:40 +0800 Subject: [PATCH 081/178] Feature #16: _COMPTON_SHADOW window property support - Add support for reading _COMPTON_SHADOW property from windows (--respect-attr-shadow). Presently the only defined behavior is, if _COMPTON_SHADOW is set on the highest ancestor below root window of a window (usually the WM frame), it's format is 32-bit, type is CADINAL, value is 0, the window will not get a shadow. The format and behavior could change in the future without prior notification. - Fix an issue in fork_after() that may cause some streams to remain open. My mistake. - Attempt to reduce determine_shadow() calls from map_win() by separating some raw handler functions out. --- compton.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++------- compton.h | 14 +++++++++ 2 files changed, 94 insertions(+), 10 deletions(-) diff --git a/compton.c b/compton.c index cc0a03c11..448633a6f 100644 --- a/compton.c +++ b/compton.c @@ -164,6 +164,7 @@ Atom name_ewmh_atom = None; Atom class_atom = None; Atom transient_atom = None; Atom ewmh_active_win_atom = None;; +Atom compton_shadow_atom = None; Atom win_type_atom; Atom win_type[NUM_WINTYPES]; @@ -205,6 +206,7 @@ static options_t opts = { .clear_shadow = False, .shadow_blacklist = NULL, .shadow_ignore_shaped = False, + .respect_attr_shadow = False, .wintype_fade = { False }, .fade_in_step = 0.028 * OPAQUE, @@ -1958,7 +1960,7 @@ map_win(Display *dpy, Window id, #endif // Detect if the window is shaped or has rounded corners - win_update_shape(dpy, w); + win_update_shape_raw(dpy, w); // Get window name and class if we are tracking them if (opts.track_wdata) { @@ -1983,8 +1985,11 @@ map_win(Display *dpy, Window id, w->focused = True; } - // Window type change and bounding shape state change could affect - // shadow + // Check for _COMPTON_SHADOW + if (opts.respect_attr_shadow) + win_update_attr_shadow_raw(dpy, w); + + // Many things above could affect shadow determine_shadow(dpy, w); // Fading in @@ -2198,6 +2203,18 @@ determine_fade(Display *dpy, win *w) { w->fade = opts.wintype_fade[w->window_type]; } +/** + * Update window-shape. + */ +static void +win_update_shape_raw(Display *dpy, win *w) { + if (shape_exists) { + w->bounding_shaped = wid_bounding_shaped(dpy, w->id); + if (w->bounding_shaped && opts.detect_rounded_corners) + win_rounded_corners(dpy, w); + } +} + /** * Update window-shape related information. */ @@ -2206,9 +2223,7 @@ win_update_shape(Display *dpy, win *w) { if (shape_exists) { // Bool bounding_shaped_old = w->bounding_shaped; - w->bounding_shaped = wid_bounding_shaped(dpy, w->id); - if (w->bounding_shaped && opts.detect_rounded_corners) - win_rounded_corners(dpy, w); + win_update_shape_raw(dpy, w); // Shadow state could be changed determine_shadow(dpy, w); @@ -2222,6 +2237,40 @@ win_update_shape(Display *dpy, win *w) { } } +/** + * Reread _COMPTON_SHADOW property from a window. + * + * The property must be set on the outermost window, usually the WM frame. + */ +static void +win_update_attr_shadow_raw(Display *dpy, win *w) { + winattr_t attr = wid_get_attr(dpy, w->id, compton_shadow_atom, 1, + XA_CARDINAL, 32); + + if (!attr.nitems) { + free_winattr(&attr); + w->attr_shadow = -1; + + return; + } + + w->attr_shadow = *((long *) attr.data); +} + +/** + * Reread _COMPTON_SHADOW property from a window and update related + * things. + */ +static void +win_update_attr_shadow(Display *dpy, win *w) { + long attr_shadow_old = w->attr_shadow; + + win_update_attr_shadow_raw(dpy, w); + + if (w->attr_shadow != attr_shadow_old) + determine_shadow(dpy, w); +} + /** * Determine if a window should have shadow, and update things depending * on shadow state. @@ -2233,7 +2282,8 @@ determine_shadow(Display *dpy, win *w) { w->shadow = (opts.wintype_shadow[w->window_type] && !win_match(w, opts.shadow_blacklist, &w->cache_sblst) && !(opts.shadow_ignore_shaped && w->bounding_shaped - && !w->rounded_corners)); + && !w->rounded_corners) + && !(opts.respect_attr_shadow && 0 == w->attr_shadow)); // Window extents need update on shadow state change if (w->shadow != shadow_old) { @@ -2387,6 +2437,7 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->need_configure = False; new->window_type = WINTYPE_UNKNOWN; new->mode = WINDOW_TRANS; + new->attr_shadow = -1; new->prev_trans = NULL; @@ -3182,6 +3233,13 @@ ev_property_notify(XPropertyEvent *ev) { determine_shadow(dpy, w); } } + + // If _COMPTON_SHADOW changes + if (opts.respect_attr_shadow && compton_shadow_atom == ev->atom) { + win *w = find_win(ev->window); + if (w) + win_update_attr_shadow(dpy, w); + } } inline static void @@ -3438,6 +3496,9 @@ usage(void) { "--use-ewmh-active-win\n" " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n" " window is focused instead of using FocusIn/Out events.\n" + "--respect-attr-shadow\n" + " Respect _COMPTON_SHADOW. This a prototype-level feature, which\n" + " you must not rely on.\n" "\n" "Format of a condition:\n" "\n" @@ -3560,9 +3621,12 @@ fork_after(void) { setsid(); // Mainly to suppress the _FORTIFY_SOURCE warning - if (!freopen("/dev/null", "r", stdin) - || !freopen("/dev/null", "w", stdout) - || !freopen("/dev/null", "w", stderr)) + bool success = freopen("/dev/null", "r", stdin); + success = freopen("/dev/null", "w", stdout) && success; + if (!success) + fprintf(stderr, "fork_after(): freopen() failed."); + success = freopen("/dev/null", "w", stderr); + if (!success) fprintf(stderr, "fork_after(): freopen() failed."); } @@ -3865,6 +3929,7 @@ get_cfg(int argc, char *const *argv) { { "sw-opti", no_argument, NULL, 274 }, { "vsync-aggressive", no_argument, NULL, 275 }, { "use-ewmh-active-win", no_argument, NULL, 276 }, + { "respect-attr-shadow", no_argument, NULL, 277 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4055,6 +4120,10 @@ get_cfg(int argc, char *const *argv) { // --use-ewmh-active-win opts.use_ewmh_active_win = True; break; + case 277: + // --respect-attr-shadow + opts.respect_attr_shadow = True; + break; default: usage(); break; @@ -4116,6 +4185,7 @@ get_atoms(void) { class_atom = XA_WM_CLASS; transient_atom = XA_WM_TRANSIENT_FOR; ewmh_active_win_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + compton_shadow_atom = XInternAtom(dpy, "_COMPTON_SHADOW", False); win_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); diff --git a/compton.h b/compton.h index d7ec6a437..b7a9d66eb 100644 --- a/compton.h +++ b/compton.h @@ -318,6 +318,9 @@ typedef struct _win { Picture shadow_pict; /// Alpha mask Picture to render shadow. Affected by shadow opacity. Picture shadow_alpha_pict; + /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for + /// none. + long attr_shadow; // Dim-related members /// Whether the window is to be dimmed. @@ -391,6 +394,8 @@ typedef struct _options { wincond *shadow_blacklist; /// Whether bounding-shaped window should be ignored. Bool shadow_ignore_shaped; + /// Whether to respect _COMPTON_SHADOW. + Bool respect_attr_shadow; // Fading Bool wintype_fade[NUM_WINTYPES]; @@ -1063,9 +1068,18 @@ set_focused(Display *dpy, win *w, Bool focused) { static void determine_fade(Display *dpy, win *w); +static void +win_update_shape_raw(Display *dpy, win *w); + static void win_update_shape(Display *dpy, win *w); +static void +win_update_attr_shadow_raw(Display *dpy, win *w); + +static void +win_update_attr_shadow(Display *dpy, win *w); + static void determine_shadow(Display *dpy, win *w); From bbe376ad1b4029f64ac3a89b4b7cc90a99eeaf1f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 9 Nov 2012 21:44:02 +0800 Subject: [PATCH 082/178] Feature: Unredirect windows when there's a fullscreen window - Optionally unredirect windows when there's a fullscreen opaque window on the top of the stack (--unredir-if-possible). Experimental. Known issues: * Screen flickers when redirecting/unredirecting windows. --paint-on-overlay seemingly minimizes it (Thanks for hints from mutter), but still noticeable. * It probably does not play well with vdpau in some cases. - A debug option DEBUG_REDIR is added. - Fix a bug that reg_ignore are not expired when a CirculateNotify is received. - Add extra safe guards in some places, which could be bad for performance. - Remove some abundant code. --- compton.c | 158 +++++++++++++++++++++++++++++++++++++++++++++--------- compton.h | 24 +++++++++ 2 files changed, 156 insertions(+), 26 deletions(-) diff --git a/compton.c b/compton.c index 448633a6f..69aeddd36 100644 --- a/compton.c +++ b/compton.c @@ -74,6 +74,11 @@ XserverRegion screen_reg = None; /// Current active window. Used by EWMH _NET_ACTIVE_WINDOW focus /// detection. win *active_win = NULL; +/// Whether all windows are currently redirected. +Bool redirected = False; +/// Whether there's a highest fullscreen window, and all windows could +/// be unredirected. +Bool unredir_possible = False; /// Pregenerated alpha pictures. Picture *alpha_picts = NULL; @@ -188,6 +193,7 @@ static options_t opts = { .synchronize = False, .detect_rounded_corners = False, .paint_on_overlay = False, + .unredir_if_possible = False, .refresh_rate = 0, .sw_opti = False, @@ -1360,8 +1366,13 @@ get_alpha_pict_o(opacity_t o) { static win * paint_preprocess(Display *dpy, win *list) { + // Initialize unredir_possible + unredir_possible = False; + win *w; win *t = NULL, *next = NULL; + // Trace whether it's the highest window to paint + Bool is_highest = True; // Fading step calculation unsigned steps = (sub_unslong(get_time_ms(), fade_time) @@ -1410,24 +1421,6 @@ paint_preprocess(Display *dpy, win *list) { } if (to_paint) { - // Fetch the picture and pixmap if needed - if (!w->picture) { - XRenderPictureAttributes pa; - XRenderPictFormat *format; - Drawable draw = w->id; - - if (has_name_pixmap && !w->pixmap) { - set_ignore(dpy, NextRequest(dpy)); - w->pixmap = XCompositeNameWindowPixmap(dpy, w->id); - } - if (w->pixmap) draw = w->pixmap; - - format = XRenderFindVisualFormat(dpy, w->a.visual); - pa.subwindow_mode = IncludeInferiors; - w->picture = XRenderCreatePicture( - dpy, draw, format, CPSubwindowMode, &pa); - } - // Fetch bounding region if (!w->border_size) { w->border_size = border_size(dpy, w); @@ -1482,6 +1475,10 @@ paint_preprocess(Display *dpy, win *list) { != (w->to_paint && WINDOW_SOLID == mode_old)) reg_ignore_expire = True; + // Add window to damaged area if its painting status changes + if (to_paint != w->to_paint) + add_damage_win(dpy, w); + if (to_paint) { // Generate ignore region for painting to reduce GPU load if (reg_ignore_expire || !w->to_paint) { @@ -1516,6 +1513,14 @@ paint_preprocess(Display *dpy, win *list) { last_reg_ignore = w->reg_ignore; + if (is_highest && to_paint) { + is_highest = False; + if (WINDOW_SOLID == w->mode + && (!w->frame_opacity || !win_has_frame(w)) + && win_is_fullscreen(w)) + unredir_possible = True; + } + // Reset flags w->flags = 0; } @@ -1536,6 +1541,37 @@ paint_preprocess(Display *dpy, win *list) { w->to_paint = to_paint; } + // If possible, unredirect all windows and stop painting + if (opts.unredir_if_possible && unredir_possible) { + redir_stop(dpy); + } + else { + redir_start(dpy); + } + + // Fetch pictures only if windows are redirected + if (redirected) { + for (w = t; w; w = w->prev_trans) { + // Fetch the picture and pixmap if needed + if (!w->picture) { + XRenderPictureAttributes pa; + XRenderPictFormat *format; + Drawable draw = w->id; + + if (has_name_pixmap && !w->pixmap) { + set_ignore(dpy, NextRequest(dpy)); + w->pixmap = XCompositeNameWindowPixmap(dpy, w->id); + } + if (w->pixmap) draw = w->pixmap; + + format = XRenderFindVisualFormat(dpy, w->a.visual); + pa.subwindow_mode = IncludeInferiors; + w->picture = XRenderCreatePicture( + dpy, draw, format, CPSubwindowMode, &pa); + } + } + } + return t; } @@ -2053,7 +2089,7 @@ static void unmap_win(Display *dpy, Window id, Bool fade) { win *w = find_win(id); - if (!w) return; + if (!w || IsUnmapped == w->a.map_state) return; w->a.map_state = IsUnmapped; @@ -2464,6 +2500,8 @@ static void restack_win(Display *dpy, win *w, Window new_above) { Window old_above; + update_reg_ignore_expire(w); + if (w->next) { old_above = w->next->id; } else { @@ -2593,7 +2631,7 @@ configure_win(Display *dpy, XConfigureEvent *ce) { win_update_shape(dpy, w); } - if (w->a.map_state != IsUnmapped && damage) { + if (damage) { XserverRegion extents = win_extents(dpy, w); XFixesUnionRegion(dpy, damage, damage, extents); XFixesDestroyRegion(dpy, extents); @@ -3499,6 +3537,10 @@ usage(void) { "--respect-attr-shadow\n" " Respect _COMPTON_SHADOW. This a prototype-level feature, which\n" " you must not rely on.\n" + "--unredir-if-possible\n" + " Unredirect all windows if a full-screen opaque window is\n" + " detected, to maximize performance for full-screen windows.\n" + " Experimental.\n" "\n" "Format of a condition:\n" "\n" @@ -3930,6 +3972,7 @@ get_cfg(int argc, char *const *argv) { { "vsync-aggressive", no_argument, NULL, 275 }, { "use-ewmh-active-win", no_argument, NULL, 276 }, { "respect-attr-shadow", no_argument, NULL, 277 }, + { "unredir-if-possible", no_argument, NULL, 278 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4124,6 +4167,10 @@ get_cfg(int argc, char *const *argv) { // --respect-attr-shadow opts.respect_attr_shadow = True; break; + case 278: + // --unredir-if-possible + opts.unredir_if_possible = True; + break; default: usage(); break; @@ -4522,6 +4569,61 @@ init_overlay(void) { } } +/** + * Redirect all windows. + */ +static void +redir_start(Display *dpy) { + if (!redirected) { +#ifdef DEBUG_REDIR + printf("redir_start(): Screen redirected.\n"); +#endif + + // Map overlay window. Done firstly according to this: + // https://bugzilla.gnome.org/show_bug.cgi?id=597014 + if (overlay) + XMapWindow(dpy, overlay); + + XCompositeRedirectSubwindows(dpy, root, CompositeRedirectManual); + + // Must call XSync() here + XSync(dpy, False); + + redirected = True; + } +} + +/** + * Unredirect all windows. + */ +static void +redir_stop(Display *dpy) { + if (redirected) { +#ifdef DEBUG_REDIR + printf("redir_stop(): Screen unredirected.\n"); +#endif + // Destroy all Pictures as they expire once windows are unredirected + // If we don't destroy them here, looks like the resources are just + // kept inaccessible somehow + for (win *w = list; w; w = w->next) { + free_pixmap(dpy, &w->pixmap); + free_picture(dpy, &w->picture); + } + + XCompositeUnredirectSubwindows(dpy, root, CompositeRedirectManual); + + // Unmap overlay window + if (overlay) + XUnmapWindow(dpy, overlay); + + // Must call XSync() here + XSync(dpy, False); + + redirected = False; + + } +} + int main(int argc, char **argv) { XEvent ev; @@ -4689,8 +4791,7 @@ main(int argc, char **argv) { all_damage = None; XGrabServer(dpy); - XCompositeRedirectSubwindows( - dpy, root, CompositeRedirectManual); + redir_start(dpy); XSelectInput(dpy, root, SubstructureNotifyMask @@ -4725,7 +4826,8 @@ main(int argc, char **argv) { t = paint_preprocess(dpy, list); - paint_all(dpy, None, t); + if (redirected) + paint_all(dpy, None, t); // Initialize idling idling = False; @@ -4737,9 +4839,9 @@ main(int argc, char **argv) { while (XEventsQueued(dpy, QueuedAfterReading) || (evpoll(&ufd, (ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) { - // Sometimes poll() returns 1 but no events are actually read, causing - // XNextEvent() to block, I have no idea what's wrong, so we check for the - // number of events here + // Sometimes poll() returns 1 but no events are actually read, + // causing XNextEvent() to block, I have no idea what's wrong, so we + // check for the number of events here if (XEventsQueued(dpy, QueuedAfterReading)) { XNextEvent(dpy, &ev); ev_handle((XEvent *) &ev); @@ -4752,6 +4854,10 @@ main(int argc, char **argv) { t = paint_preprocess(dpy, list); + // If the screen is unredirected, free all_damage to stop painting + if (!redirected) + free_region(dpy, &all_damage); + if (all_damage && !is_region_empty(dpy, all_damage)) { static int paint; paint_all(dpy, all_damage, t); diff --git a/compton.h b/compton.h index b7a9d66eb..b66e77303 100644 --- a/compton.h +++ b/compton.h @@ -14,6 +14,7 @@ // #define DEBUG_CLIENTWIN 1 // #define DEBUG_WINDATA 1 // #define DEBUG_WINMATCH 1 +// #define DEBUG_REDIR 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled @@ -367,6 +368,9 @@ typedef struct _options { /// Whether to paint on X Composite overlay window instead of root /// window. Bool paint_on_overlay; + /// Whether to unredirect all windows if a full-screen opaque window + /// is detected. + Bool unredir_if_possible; /// Whether to work under synchronized mode for debugging. Bool synchronize; @@ -960,12 +964,26 @@ update_reg_ignore_expire(const win *w) { reg_ignore_expire = True; } +/** + * Check whether a window has WM frames. + */ static inline bool win_has_frame(const win *w) { return w->top_width || w->left_width || w->right_width || w->bottom_width; } +/** + * Check if a window is a fullscreen window. + * + * It's not using w->border_size for performance measures. + */ +static inline bool +win_is_fullscreen(const win *w) { + return (w->a.x <= 0 && w->a.y <= 0 && (w->a.x + w->widthb) >= root_width + && (w->a.y + w->heightb) >= root_height && !w->bounding_shaped); +} + static void win_rounded_corners(Display *dpy, win *w); @@ -1345,3 +1363,9 @@ init_dbe(void); static void init_overlay(void); + +static void +redir_start(Display *dpy); + +static void +redir_stop(Display *dpy); From 473cb1f4ed86ec7337f40eea4bdd55298524d9e2 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 10 Nov 2012 11:41:01 +0800 Subject: [PATCH 083/178] Bug fix: Client window event mask not restored in map_win() - Fix a bug that client window event masks are not restored in map_win(), causing further property changes to be ignored, for example. - Misc changes. --- compton.c | 7 +++---- compton.h | 7 ++++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compton.c b/compton.c index 69aeddd36..079f87154 100644 --- a/compton.c +++ b/compton.c @@ -1979,10 +1979,9 @@ map_win(Display *dpy, Window id, mark_client_win(dpy, w, cw); } } - else if (opts.frame_opacity) { - // Refetch frame extents just in case it changes when the window is - // unmapped - get_frame_extents(dpy, w, w->client_win); + else { + // Re-mark client window here + mark_client_win(dpy, w, w->client_win); } // Workaround for _NET_WM_WINDOW_TYPE for Openbox menus, which is diff --git a/compton.h b/compton.h index b66e77303..5d3ad2611 100644 --- a/compton.h +++ b/compton.h @@ -343,6 +343,7 @@ typedef struct _win { struct _win *prev_trans; } win; +/// VSync modes. typedef enum { VSYNC_NONE, VSYNC_DRM, @@ -354,7 +355,7 @@ typedef int (*f_WaitVideoSync) (int, int, unsigned *); typedef int (*f_GetVideoSync) (unsigned *); #endif -typedef struct _options { +typedef struct { // General char *display; /// Whether to try to detect WM windows and mark them as focused. @@ -958,6 +959,10 @@ wid_bounding_shaped(Display *dpy, Window wid) { return False; } +/** + * Determine if a window change affects reg_ignore and set + * reg_ignore_expire accordingly. + */ static inline void update_reg_ignore_expire(const win *w) { if (w->to_paint && WINDOW_SOLID == w->mode) From e6f244196fea9a5d0f936a5cac4eefb060f46013 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 14 Nov 2012 21:34:51 +0800 Subject: [PATCH 084/178] Misc: Code cleanup - Form a function ev_window_name() for all window name detection in debugging code. - Add unredir-if-possible to configuration file options. --- compton.c | 75 +++++++++++++++++++++++++++++++------------------------ compton.h | 5 ++++ 2 files changed, 48 insertions(+), 32 deletions(-) diff --git a/compton.c b/compton.c index 079f87154..080fd0a86 100644 --- a/compton.c +++ b/compton.c @@ -2529,8 +2529,8 @@ restack_win(Display *dpy, win *w, Window new_above) { #ifdef DEBUG_RESTACK { const char *desc; - char *window_name; - Bool to_free; + char *window_name = NULL; + bool to_free; win* c = list; printf("restack_win(%#010lx, %#010lx): " @@ -2539,11 +2539,7 @@ restack_win(Display *dpy, win *w, Window new_above) { for (; c; c = c->next) { window_name = "(Failed to get title)"; - if (root == c->id) { - window_name = "(Root window)"; - } else { - to_free = wid_get_name(dpy, c->id, &window_name); - } + to_free = ev_window_name(dpy, c->id, &window_name); desc = ""; if (c->destroyed) desc = "(D) "; @@ -3326,6 +3322,38 @@ ev_screen_change_notify(XRRScreenChangeNotifyEvent *ev) { } } +#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) +/** + * Get a window's name from window ID. + */ +static bool +ev_window_name(Display *dpy, Window wid, char **name) { + bool to_free = false; + + *name = ""; + if (wid) { + *name = "(Failed to get title)"; + if (root == wid) + *name = "(Root window)"; + else if (overlay == wid) + *name = "(Overlay)"; + else { + win *w = find_win(wid); + if (!w) + w = find_toplevel(wid); + + if (w && w->name) + *name = w->name; + else if (!(w && w->client_win + && (to_free = wid_get_name(dpy, w->client_win, name)))) + to_free = wid_get_name(dpy, wid, name); + } + } + + return to_free; +} +#endif + static void ev_handle(XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) { @@ -3334,31 +3362,11 @@ ev_handle(XEvent *ev) { #ifdef DEBUG_EVENTS if (ev->type != damage_event + XDamageNotify) { - Window wid; - char *window_name; - Bool to_free = False; - - wid = ev_window(ev); - window_name = "(Failed to get title)"; - - if (wid) { - if (root == wid) - window_name = "(Root window)"; - else if (overlay == wid) - window_name = "(Overlay)"; - else { - win *w = find_win(wid); - if (!w) - w = find_toplevel(wid); - - if (w && w->name) - window_name = w->name; - else if (!(w && w->client_win - && (to_free = (Bool) wid_get_name(dpy, w->client_win, - &window_name)))) - to_free = (Bool) wid_get_name(dpy, wid, &window_name); - } - } + Window wid = ev_window(ev); + char *window_name = NULL; + Bool to_free = false; + + to_free = ev_window_name(dpy, wid, &window_name); print_timestamp(); printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", @@ -3899,6 +3907,9 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { // --use-ewmh-active-win lcfg_lookup_bool(&cfg, "use-ewmh-active-win", &opts.use_ewmh_active_win); + // --unredir-if-possible + lcfg_lookup_bool(&cfg, "unredir-if-possible", + &opts.unredir_if_possible); // --shadow-exclude { config_setting_t *setting = diff --git a/compton.h b/compton.h index 5d3ad2611..2b3e08604 100644 --- a/compton.h +++ b/compton.h @@ -1289,6 +1289,11 @@ add_damage_win(Display *dpy, win *w) { } } +#if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) +static bool +ev_window_name(Display *dpy, Window wid, char **name); +#endif + inline static void ev_handle(XEvent *ev); From de41eb8aab4f7acdb3a8c40582f9aa0a3af8b84d Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 19 Nov 2012 09:46:07 +0800 Subject: [PATCH 085/178] Feature #51: Re-initialize when SIGUSR1 is received - Create a session_t structure, to get rid of most global variables and let information pass in/out mostly through parameters. Huge changes, so bugs may very well appear. I'm worried about resource leakages, in particular. - Add gcc attributes on various functions. - Add Doxygen configuration. - Replace much Xlib Bool with C99 bool. - Add and adjust some comments. - Drop unused parameters from some functions. - Cache default Visual and Depth, mainly to shorten code. - Rename some types, variables, and functions. - Add win_ev_stop() and set_ignore_next() for convenience. - Modify wid_get_prop_wintype() and wid_get_opacity_prop() to use wid_get_prop(). - Rename --respect-attr-shadow to --respect-prop-shadow. - Fix a memory leak in --respect-prop-shadow. - Many other small changes. --- compton.c | 2932 ++++++++++++++++++++++++++++------------------------- compton.h | 1093 ++++++++++++-------- 2 files changed, 2259 insertions(+), 1766 deletions(-) diff --git a/compton.c b/compton.c index 080fd0a86..e10c4c2d2 100644 --- a/compton.c +++ b/compton.c @@ -10,16 +10,18 @@ #include "compton.h" -#define MSTR_(s) #s -#define MSTR(s) MSTR_(s) +// === Macros === + +// #define MSTR_(s) #s +// #define MSTR(s) MSTR_(s) // Use #s here to prevent macro expansion +/// Macro used for shortening some debugging code. #define CASESTRRET(s) case s: return #s -/** - * Shared - */ +// === Global constants === +/// Name strings for window types. const char *WINTYPES[NUM_WINTYPES] = { "unknown", "desktop", @@ -38,207 +40,22 @@ const char *WINTYPES[NUM_WINTYPES] = { "dnd", }; -struct timeval time_start = { 0, 0 }; - -win *list; -Display *dpy = NULL; -int scr; - -/// Root window. -Window root = None; -/// Damage of root window. -Damage root_damage = None; -/// X Composite overlay window. Used if --paint-on-overlay. -Window overlay = None; - -/// Picture of root window. Destination of painting in no-DBE painting -/// mode. -Picture root_picture = None; -/// A Picture acting as the painting target. -Picture tgt_picture = None; -/// Temporary buffer to paint to before sending to display. -Picture tgt_buffer = None; -/// DBE back buffer for root window. Used in DBE painting mode. -XdbeBackBuffer root_dbe = None; - -Picture black_picture; -Picture cshadow_picture; -/// Picture used for dimming inactive windows. -Picture dim_picture = 0; -Picture root_tile; -XserverRegion all_damage; -Bool has_name_pixmap; -int root_height, root_width; -/// A region of the size of the screen. -XserverRegion screen_reg = None; -/// Current active window. Used by EWMH _NET_ACTIVE_WINDOW focus -/// detection. -win *active_win = NULL; -/// Whether all windows are currently redirected. -Bool redirected = False; -/// Whether there's a highest fullscreen window, and all windows could -/// be unredirected. -Bool unredir_possible = False; - -/// Pregenerated alpha pictures. -Picture *alpha_picts = NULL; -/// Whether the program is idling. I.e. no fading, no potential window -/// changes. -Bool idling; -/// Whether all reg_ignore of windows should expire in this paint. -Bool reg_ignore_expire = False; -/// Window ID of the window we register as a symbol. -Window reg_win = 0; - -/// Currently used refresh rate. Used for sw_opti. -short refresh_rate = 0; -/// Interval between refresh in nanoseconds. Used for sw_opti. -unsigned long refresh_intv = 0; -/// Nanosecond-level offset of the first painting. Used for sw_opti. -long paint_tm_offset = 0; - -#ifdef CONFIG_VSYNC_DRM -/// File descriptor of DRI device file. Used for DRM VSync. -int drm_fd = 0; -#endif - -#ifdef CONFIG_VSYNC_OPENGL -/// GLX context. -GLXContext glx_context; - -/// Pointer to glXGetVideoSyncSGI function. Used by OpenGL VSync. -f_GetVideoSync glx_get_video_sync = NULL; - -/// Pointer to glXWaitVideoSyncSGI function. Used by OpenGL VSync. -f_WaitVideoSync glx_wait_video_sync = NULL; -#endif - -/* errors */ -ignore *ignore_head = NULL, **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; - -/// Whether X Shape extension exists. -Bool shape_exists = False; -/// Event base number and error base number for X Shape extension. -int shape_event, shape_error; - -/// Whether X RandR extension exists. -Bool randr_exists = False; -/// Event base number and error base number for X RandR extension. -int randr_event, randr_error; - -#ifdef CONFIG_VSYNC_OPENGL -/// Whether X GLX extension exists. -Bool glx_exists = False; -/// Event base number and error base number for X GLX extension. -int glx_event, glx_error; -#endif - -Bool dbe_exists = False; - -/* shadows */ -conv *gaussian_map; - -/* for shadow precomputation */ -int cgsize = -1; -unsigned char *shadow_corner = NULL; -unsigned char *shadow_top = NULL; - -/* for root tile */ -static const char *background_props[] = { +/// Names of root window properties that could point to a pixmap of +/// background. +const char *background_props_str[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", 0, }; -/* for expose events */ -XRectangle *expose_rects = 0; -int size_expose = 0; -int n_expose = 0; - -// atoms -Atom opacity_atom = None; -Atom frame_extents_atom = None; -Atom client_atom = None; -Atom name_atom = None; -Atom name_ewmh_atom = None; -Atom class_atom = None; -Atom transient_atom = None; -Atom ewmh_active_win_atom = None;; -Atom compton_shadow_atom = None; - -Atom win_type_atom; -Atom win_type[NUM_WINTYPES]; +// === Global variables === -/** - * Macros - */ +/// Pointer to current session, as a global variable. Only used by +/// error() and reset_enable(), which could not +/// have a pointer to current session passed in. +session_t *ps_g = NULL; -#define HAS_FRAME_OPACITY(w) \ - (frame_opacity && (w)->top_width) - -/** - * Options - */ - -static options_t opts = { - .display = NULL, - .mark_wmwin_focused = False, - .mark_ovredir_focused = False, - .fork_after_register = False, - .synchronize = False, - .detect_rounded_corners = False, - .paint_on_overlay = False, - .unredir_if_possible = False, - - .refresh_rate = 0, - .sw_opti = False, - .vsync = VSYNC_NONE, - .dbe = False, - .vsync_aggressive = False, - - .wintype_shadow = { False }, - .shadow_red = 0.0, - .shadow_green = 0.0, - .shadow_blue = 0.0, - .shadow_radius = 12, - .shadow_offset_x = -15, - .shadow_offset_y = -15, - .shadow_opacity = .75, - .clear_shadow = False, - .shadow_blacklist = NULL, - .shadow_ignore_shaped = False, - .respect_attr_shadow = False, - - .wintype_fade = { False }, - .fade_in_step = 0.028 * OPAQUE, - .fade_out_step = 0.03 * OPAQUE, - .fade_delta = 10, - .no_fading_openclose = False, - .fade_blacklist = NULL, - - .wintype_opacity = { 0.0 }, - .inactive_opacity = 0, - .inactive_opacity_override = False, - .frame_opacity = 0.0, - .detect_client_opacity = False, - .inactive_dim = 0.0, - .alpha_step = 0.03, - .use_ewmh_active_win = False, - - .track_focus = False, - .track_wdata = False, -}; - -/** - * Fades - */ - -unsigned long fade_time = 0; +// === Fading === /** * Get the time left before next fading point. @@ -246,8 +63,8 @@ unsigned long fade_time = 0; * In milliseconds. */ static int -fade_timeout(void) { - int diff = opts.fade_delta - get_time_ms() + fade_time; +fade_timeout(session_t *ps) { + int diff = ps->o.fade_delta - get_time_ms() + ps->fade_time; if (diff < 0) diff = 0; @@ -261,7 +78,7 @@ fade_timeout(void) { * @param steps steps of fading */ static void -run_fade(Display *dpy, win *w, unsigned steps) { +run_fade(session_t *ps, win *w, unsigned steps) { // If we have reached target opacity, return if (w->opacity == w->opacity_tgt) { return; @@ -274,16 +91,16 @@ run_fade(Display *dpy, win *w, unsigned steps) { // calculations if (w->opacity < w->opacity_tgt) w->opacity = normalize_d_range( - (double) w->opacity + (double) opts.fade_in_step * steps, + (double) w->opacity + (double) ps->o.fade_in_step * steps, 0.0, w->opacity_tgt); else w->opacity = normalize_d_range( - (double) w->opacity - (double) opts.fade_out_step * steps, + (double) w->opacity - (double) ps->o.fade_out_step * steps, w->opacity_tgt, OPAQUE); } if (w->opacity != w->opacity_tgt) { - idling = False; + ps->idling = false; } } @@ -294,32 +111,30 @@ run_fade(Display *dpy, win *w, unsigned steps) { * @param exec_callback whether the previous callback is to be executed */ static void -set_fade_callback(Display *dpy, win *w, - void (*callback) (Display *dpy, win *w), Bool exec_callback) { - void (*old_callback) (Display *dpy, win *w) = w->fade_callback; +set_fade_callback(session_t *ps, win *w, + void (*callback) (session_t *ps, win *w), bool exec_callback) { + void (*old_callback) (session_t *ps, win *w) = w->fade_callback; w->fade_callback = callback; // Must be the last line as the callback could destroy w! if (exec_callback && old_callback) { - old_callback(dpy, w); + old_callback(ps, w); // Although currently no callback function affects window state on // next paint, it could, in the future - idling = False; + ps->idling = false; } } -/** - * Shadows - */ +// === Shadows === -static double +static double __attribute__((const)) 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) { +make_gaussian_map(double r) { conv *c; int size = ((int) ceil((r * 3)) + 1) & ~1; int center = size / 2; @@ -334,7 +149,7 @@ make_gaussian_map(Display *dpy, double r) { for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { - g = gaussian(r, (double) (x - center), (double) (y - center)); + g = gaussian(r, x - center, y - center); t += g; c->data[y * size + x] = g; } @@ -420,54 +235,56 @@ sum_gaussian(conv *map, double opacity, to save time for large windows */ static void -presum_gaussian(conv *map) { +presum_gaussian(session_t *ps, conv *map) { int center = map->size / 2; int opacity, x, y; - cgsize = map->size; + ps->cgsize = map->size; - if (shadow_corner) free((void *)shadow_corner); - if (shadow_top) free((void *)shadow_top); + if (ps->shadow_corner) + free(ps->shadow_corner); + if (ps->shadow_top) + free(ps->shadow_top); - shadow_corner = (unsigned char *)(malloc((cgsize + 1) * (cgsize + 1) * 26)); - shadow_top = (unsigned char *)(malloc((cgsize + 1) * 26)); + ps->shadow_corner = malloc((ps->cgsize + 1) * (ps->cgsize + 1) * 26); + ps->shadow_top = malloc((ps->cgsize + 1) * 26); - for (x = 0; x <= cgsize; x++) { - shadow_top[25 * (cgsize + 1) + x] = - sum_gaussian(map, 1, x - center, center, cgsize * 2, cgsize * 2); + for (x = 0; x <= ps->cgsize; x++) { + ps->shadow_top[25 * (ps->cgsize + 1) + x] = + sum_gaussian(map, 1, x - center, center, ps->cgsize * 2, ps->cgsize * 2); for (opacity = 0; opacity < 25; opacity++) { - shadow_top[opacity * (cgsize + 1) + x] = - shadow_top[25 * (cgsize + 1) + x] * opacity / 25; + ps->shadow_top[opacity * (ps->cgsize + 1) + x] = + ps->shadow_top[25 * (ps->cgsize + 1) + x] * opacity / 25; } for (y = 0; y <= x; y++) { - shadow_corner[25 * (cgsize + 1) * (cgsize + 1) + y * (cgsize + 1) + x] - = sum_gaussian(map, 1, x - center, y - center, cgsize * 2, cgsize * 2); - shadow_corner[25 * (cgsize + 1) * (cgsize + 1) + x * (cgsize + 1) + y] - = shadow_corner[25 * (cgsize + 1) * (cgsize + 1) + y * (cgsize + 1) + x]; + ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x] + = sum_gaussian(map, 1, x - center, y - center, ps->cgsize * 2, ps->cgsize * 2); + ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + x * (ps->cgsize + 1) + y] + = ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + y * (ps->cgsize + 1) + x]; for (opacity = 0; opacity < 25; opacity++) { - shadow_corner[opacity * (cgsize + 1) * (cgsize + 1) - + y * (cgsize + 1) + x] - = shadow_corner[opacity * (cgsize + 1) * (cgsize + 1) - + x * (cgsize + 1) + y] - = shadow_corner[25 * (cgsize + 1) * (cgsize + 1) - + y * (cgsize + 1) + x] * opacity / 25; + ps->shadow_corner[opacity * (ps->cgsize + 1) * (ps->cgsize + 1) + + y * (ps->cgsize + 1) + x] + = ps->shadow_corner[opacity * (ps->cgsize + 1) * (ps->cgsize + 1) + + x * (ps->cgsize + 1) + y] + = ps->shadow_corner[25 * (ps->cgsize + 1) * (ps->cgsize + 1) + + y * (ps->cgsize + 1) + x] * opacity / 25; } } } } static XImage * -make_shadow(Display *dpy, double opacity, - int width, int height, Bool clear_shadow) { +make_shadow(session_t *ps, double opacity, + int width, int height) { XImage *ximage; unsigned char *data; int ylimit, xlimit; - int swidth = width + cgsize; - int sheight = height + cgsize; - int center = cgsize / 2; + int swidth = width + ps->cgsize; + int sheight = height + ps->cgsize; + int center = ps->cgsize / 2; int x, y; unsigned char d; int x_diff; @@ -476,10 +293,8 @@ make_shadow(Display *dpy, double opacity, 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)); + ximage = XCreateImage(ps->dpy, ps->vis, 8, + ZPixmap, 0, (char *) data, swidth, sheight, 8, swidth * sizeof(char)); if (!ximage) { free(data); @@ -498,12 +313,12 @@ make_shadow(Display *dpy, double opacity, // later will be filled) could entirely cover the area of the shadow // that will be displayed, do not bother filling other pixels. If it // can't, we must fill the other pixels here. - /* if (!(clear_shadow && opts.shadow_offset_x <= 0 && opts.shadow_offset_x >= -cgsize - && opts.shadow_offset_y <= 0 && opts.shadow_offset_y >= -cgsize)) { */ - if (cgsize > 0) { - d = shadow_top[opacity_int * (cgsize + 1) + cgsize]; + /* if (!(clear_shadow && ps->o.shadow_offset_x <= 0 && ps->o.shadow_offset_x >= -ps->cgsize + && ps->o.shadow_offset_y <= 0 && ps->o.shadow_offset_y >= -ps->cgsize)) { */ + if (ps->cgsize > 0) { + d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + ps->cgsize]; } else { - d = sum_gaussian(gaussian_map, + d = sum_gaussian(ps->gaussian_map, opacity, center, center, width, height); } memset(data, d, sheight * swidth); @@ -513,19 +328,19 @@ make_shadow(Display *dpy, double opacity, * corners */ - ylimit = cgsize; + ylimit = ps->cgsize; if (ylimit > sheight / 2) ylimit = (sheight + 1) / 2; - xlimit = cgsize; + xlimit = ps->cgsize; if (xlimit > swidth / 2) xlimit = (swidth + 1) / 2; for (y = 0; y < ylimit; y++) { for (x = 0; x < xlimit; x++) { - if (xlimit == cgsize && ylimit == cgsize) { - d = shadow_corner[opacity_int * (cgsize + 1) * (cgsize + 1) - + y * (cgsize + 1) + x]; + if (xlimit == ps->cgsize && ylimit == ps->cgsize) { + d = ps->shadow_corner[opacity_int * (ps->cgsize + 1) * (ps->cgsize + 1) + + y * (ps->cgsize + 1) + x]; } else { - d = sum_gaussian(gaussian_map, + d = sum_gaussian(ps->gaussian_map, opacity, x - center, y - center, width, height); } data[y * swidth + x] = d; @@ -539,17 +354,17 @@ make_shadow(Display *dpy, double opacity, * top/bottom */ - x_diff = swidth - (cgsize * 2); + x_diff = swidth - (ps->cgsize * 2); if (x_diff > 0 && ylimit > 0) { for (y = 0; y < ylimit; y++) { - if (ylimit == cgsize) { - d = shadow_top[opacity_int * (cgsize + 1) + y]; + if (ylimit == ps->cgsize) { + d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + y]; } else { - d = sum_gaussian(gaussian_map, + d = sum_gaussian(ps->gaussian_map, opacity, center, y - center, width, height); } - memset(&data[y * swidth + cgsize], d, x_diff); - memset(&data[(sheight - y - 1) * swidth + cgsize], d, x_diff); + memset(&data[y * swidth + ps->cgsize], d, x_diff); + memset(&data[(sheight - y - 1) * swidth + ps->cgsize], d, x_diff); } } @@ -558,28 +373,27 @@ make_shadow(Display *dpy, double opacity, */ for (x = 0; x < xlimit; x++) { - if (xlimit == cgsize) { - d = shadow_top[opacity_int * (cgsize + 1) + x]; + if (xlimit == ps->cgsize) { + d = ps->shadow_top[opacity_int * (ps->cgsize + 1) + x]; } else { - d = sum_gaussian(gaussian_map, + d = sum_gaussian(ps->gaussian_map, opacity, x - center, center, width, height); } - for (y = cgsize; y < sheight - cgsize; y++) { + for (y = ps->cgsize; y < sheight - ps->cgsize; y++) { data[y * swidth + x] = d; data[y * swidth + (swidth - x - 1)] = d; } } - assert(!clear_shadow); /* if (clear_shadow) { // Clear the region in the shadow that the window would cover based // on shadow_offset_{x,y} user provides - int xstart = normalize_i_range(- (int) opts.shadow_offset_x, 0, swidth); - int xrange = normalize_i_range(width - (int) opts.shadow_offset_x, + int xstart = normalize_i_range(- (int) ps->o.shadow_offset_x, 0, swidth); + int xrange = normalize_i_range(width - (int) ps->o.shadow_offset_x, 0, swidth) - xstart; - int ystart = normalize_i_range(- (int) opts.shadow_offset_y, 0, sheight); - int yend = normalize_i_range(height - (int) opts.shadow_offset_y, + int ystart = normalize_i_range(- (int) ps->o.shadow_offset_y, 0, sheight); + int yend = normalize_i_range(height - (int) ps->o.shadow_offset_y, 0, sheight); int y; @@ -592,48 +406,50 @@ make_shadow(Display *dpy, double opacity, return ximage; } +/** + * Generate shadow Picture for a window. + */ static Picture -shadow_picture(Display *dpy, double opacity, int width, int height, - Bool clear_shadow) { +shadow_picture(session_t *ps, double opacity, int width, int height) { XImage *shadow_image = NULL; Pixmap shadow_pixmap = None, shadow_pixmap_argb = None; Picture shadow_picture = None, shadow_picture_argb = None; GC gc = None; - shadow_image = make_shadow(dpy, opacity, width, height, clear_shadow); + shadow_image = make_shadow(ps, opacity, width, height); if (!shadow_image) return None; - shadow_pixmap = XCreatePixmap(dpy, root, + shadow_pixmap = XCreatePixmap(ps->dpy, ps->root, shadow_image->width, shadow_image->height, 8); - shadow_pixmap_argb = XCreatePixmap(dpy, root, + shadow_pixmap_argb = XCreatePixmap(ps->dpy, ps->root, shadow_image->width, shadow_image->height, 32); if (!shadow_pixmap || !shadow_pixmap_argb) goto shadow_picture_err; - shadow_picture = XRenderCreatePicture(dpy, shadow_pixmap, - XRenderFindStandardFormat(dpy, PictStandardA8), 0, 0); - shadow_picture_argb = XRenderCreatePicture(dpy, shadow_pixmap_argb, - XRenderFindStandardFormat(dpy, PictStandardARGB32), 0, 0); + shadow_picture = XRenderCreatePicture(ps->dpy, shadow_pixmap, + XRenderFindStandardFormat(ps->dpy, PictStandardA8), 0, 0); + shadow_picture_argb = XRenderCreatePicture(ps->dpy, shadow_pixmap_argb, + XRenderFindStandardFormat(ps->dpy, PictStandardARGB32), 0, 0); if (!shadow_picture || !shadow_picture_argb) goto shadow_picture_err; - gc = XCreateGC(dpy, shadow_pixmap, 0, 0); + gc = XCreateGC(ps->dpy, shadow_pixmap, 0, 0); if (!gc) goto shadow_picture_err; - XPutImage(dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, + XPutImage(ps->dpy, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, shadow_image->width, shadow_image->height); - XRenderComposite(dpy, PictOpSrc, cshadow_picture, shadow_picture, + XRenderComposite(ps->dpy, PictOpSrc, ps->cshadow_picture, shadow_picture, shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width, shadow_image->height); - XFreeGC(dpy, gc); + XFreeGC(ps->dpy, gc); XDestroyImage(shadow_image); - XFreePixmap(dpy, shadow_pixmap); - XFreePixmap(dpy, shadow_pixmap_argb); - XRenderFreePicture(dpy, shadow_picture); + XFreePixmap(ps->dpy, shadow_pixmap); + XFreePixmap(ps->dpy, shadow_pixmap_argb); + XRenderFreePicture(ps->dpy, shadow_picture); return shadow_picture_argb; @@ -641,39 +457,42 @@ shadow_picture_err: if (shadow_image) XDestroyImage(shadow_image); if (shadow_pixmap) - XFreePixmap(dpy, shadow_pixmap); + XFreePixmap(ps->dpy, shadow_pixmap); if (shadow_pixmap_argb) - XFreePixmap(dpy, shadow_pixmap_argb); + XFreePixmap(ps->dpy, shadow_pixmap_argb); if (shadow_picture) - XRenderFreePicture(dpy, shadow_picture); + XRenderFreePicture(ps->dpy, shadow_picture); if (shadow_picture_argb) - XRenderFreePicture(dpy, shadow_picture_argb); + XRenderFreePicture(ps->dpy, shadow_picture_argb); if (gc) - XFreeGC(dpy, gc); + XFreeGC(ps->dpy, gc); return None; } +/** + * Generate a 1x1 Picture of a particular color. + */ static Picture -solid_picture(Display *dpy, Bool argb, double a, +solid_picture(session_t *ps, 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); + pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, argb ? 32 : 8); if (!pixmap) return None; pa.repeat = True; - picture = XRenderCreatePicture(dpy, pixmap, - XRenderFindStandardFormat(dpy, argb + picture = XRenderCreatePicture(ps->dpy, pixmap, + XRenderFindStandardFormat(ps->dpy, argb ? PictStandardARGB32 : PictStandardA8), CPRepeat, &pa); if (!picture) { - XFreePixmap(dpy, pixmap); + XFreePixmap(ps->dpy, pixmap); return None; } @@ -682,25 +501,23 @@ solid_picture(Display *dpy, Bool argb, double a, c.green = g * 0xffff; c.blue = b * 0xffff; - XRenderFillRectangle(dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); - XFreePixmap(dpy, pixmap); + XRenderFillRectangle(ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); + XFreePixmap(ps->dpy, pixmap); return picture; } -/** - * Errors - */ +// === Error handling === 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; +discard_ignore(session_t *ps, unsigned long sequence) { + while (ps->ignore_head) { + if ((long) (sequence - ps->ignore_head->sequence) > 0) { + ignore_t *next = ps->ignore_head->next; + free(ps->ignore_head); + ps->ignore_head = next; + if (!ps->ignore_head) { + ps->ignore_tail = &ps->ignore_head; } } else { break; @@ -709,37 +526,35 @@ discard_ignore(Display *dpy, unsigned long sequence) { } static void -set_ignore(Display *dpy, unsigned long sequence) { - ignore *i = malloc(sizeof(ignore)); +set_ignore(session_t *ps, unsigned long sequence) { + ignore_t *i = malloc(sizeof(ignore_t)); if (!i) return; i->sequence = sequence; i->next = 0; - *ignore_tail = i; - ignore_tail = &i->next; + *ps->ignore_tail = i; + ps->ignore_tail = &i->next; } static int -should_ignore(Display *dpy, unsigned long sequence) { - discard_ignore(dpy, sequence); - return ignore_head && ignore_head->sequence == sequence; +should_ignore(session_t *ps, unsigned long sequence) { + discard_ignore(ps, sequence); + return ps->ignore_head && ps->ignore_head->sequence == sequence; } -/** - * Windows - */ +// === Windows === /** * Check if a window has rounded corners. */ static void -win_rounded_corners(Display *dpy, win *w) { +win_rounded_corners(session_t *ps, win *w) { if (!w->bounding_shaped) return; // Fetch its bounding region if (!w->border_size) - w->border_size = border_size(dpy, w); + w->border_size = border_size(ps, w); // Quit if border_size() returns None if (!w->border_size) @@ -754,7 +569,7 @@ win_rounded_corners(Display *dpy, win *w) { // Get the rectangles in the bounding region int nrects = 0, i; - XRectangle *rects = XFixesFetchRegion(dpy, w->border_size, &nrects); + XRectangle *rects = XFixesFetchRegion(ps->dpy, w->border_size, &nrects); if (!rects) return; @@ -762,12 +577,12 @@ win_rounded_corners(Display *dpy, win *w) { // having rounded corners for (i = 0; i < nrects; ++i) if (rects[i].width >= minwidth && rects[i].height >= minheight) { - w->rounded_corners = True; + w->rounded_corners = true; XFree(rects); return; } - w->rounded_corners = False; + w->rounded_corners = false; XFree(rects); } @@ -777,7 +592,7 @@ win_rounded_corners(Display *dpy, win *w) { * @return true if matched, false otherwise. */ static bool -win_match_once(win *w, const wincond *cond) { +win_match_once(win *w, const wincond_t *cond) { const char *target; bool matched = false; @@ -866,7 +681,7 @@ win_match_once(win *w, const wincond *cond) { * @return true if matched, false otherwise. */ static bool -win_match(win *w, wincond *condlst, wincond **cache) { +win_match(win *w, wincond_t *condlst, wincond_t **cache) { // Check if the cached entry matches firstly if (cache && *cache && win_match_once(w, *cache)) return true; @@ -886,22 +701,22 @@ win_match(win *w, wincond *condlst, wincond **cache) { /** * Add a pattern to a condition linked list. */ -static Bool -condlst_add(wincond **pcondlst, const char *pattern) { +static bool +condlst_add(wincond_t **pcondlst, const char *pattern) { if (!pattern) - return False; + return false; unsigned plen = strlen(pattern); - wincond *cond; + wincond_t *cond; const char *pos; if (plen < 4 || ':' != pattern[1] || !strchr(pattern + 2, ':')) { printf("Pattern \"%s\": Format invalid.\n", pattern); - return False; + return false; } // Allocate memory for the new condition - cond = malloc(sizeof(wincond)); + cond = malloc(sizeof(wincond_t)); // Determine the pattern target switch (pattern[0]) { @@ -918,7 +733,7 @@ condlst_add(wincond **pcondlst, const char *pattern) { printf("Pattern \"%s\": Target \"%c\" invalid.\n", pattern, pattern[0]); free(cond); - return False; + return false; } // Determine the pattern type @@ -944,7 +759,7 @@ condlst_add(wincond **pcondlst, const char *pattern) { printf("Pattern \"%s\": Type \"%c\" invalid.\n", pattern, pattern[2]); free(cond); - return False; + return false; } // Determine the pattern flags @@ -985,7 +800,7 @@ condlst_add(wincond **pcondlst, const char *pattern) { printf("Pattern \"%s\": PCRE regular expression parsing failed on " "offset %d: %s\n", pattern, erroffset, error); free(cond); - return False; + return false; } #ifdef CONFIG_REGEX_PCRE_JIT cond->regex_pcre_extra = pcre_study(cond->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error); @@ -1004,33 +819,39 @@ condlst_add(wincond **pcondlst, const char *pattern) { cond->next = *pcondlst; *pcondlst = cond; - return True; + return true; } +/** + * Determine the event mask for a window. + */ static long -determine_evmask(Display *dpy, Window wid, win_evmode_t mode) { +determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { long evmask = NoEventMask; - if (WIN_EVMODE_FRAME == mode || find_win(wid)) { + if (WIN_EVMODE_FRAME == mode || find_win(ps, wid)) { evmask |= PropertyChangeMask; - if (opts.track_focus && !opts.use_ewmh_active_win) + if (ps->o.track_focus && !ps->o.use_ewmh_active_win) evmask |= FocusChangeMask; } - if (WIN_EVMODE_CLIENT == mode || find_toplevel(wid)) { - if (opts.frame_opacity || opts.track_wdata - || opts.detect_client_opacity) + if (WIN_EVMODE_CLIENT == mode || find_toplevel(ps, wid)) { + if (ps->o.frame_opacity || ps->o.track_wdata + || ps->o.detect_client_opacity) evmask |= PropertyChangeMask; } return evmask; } +/** + * Find a window from window id in window linked list of the session. + */ static win * -find_win(Window id) { +find_win(session_t *ps, Window id) { win *w; - for (w = list; w; w = w->next) { + for (w = ps->list; w; w = w->next) { if (w->id == id && !w->destroyed) return w; } @@ -1045,10 +866,10 @@ find_win(Window id) { * @return struct _win object of the found window, NULL if not found */ static win * -find_toplevel(Window id) { +find_toplevel(session_t *ps, Window id) { win *w; - for (w = list; w; w = w->next) { + for (w = ps->list; w; w = w->next) { if (w->client_win == id && !w->destroyed) return w; } @@ -1059,16 +880,16 @@ find_toplevel(Window id) { /** * Find out the WM frame of a client window by querying X. * - * @param dpy display to use + * @param ps current session * @param w window ID * @return struct _win object of the found window, NULL if not found */ static win * -find_toplevel2(Display *dpy, Window wid) { +find_toplevel2(session_t *ps, Window wid) { win *w = NULL; // We traverse through its ancestors to find out the frame - while (wid && wid != root && !(w = find_win(wid))) { + while (wid && wid != ps->root && !(w = find_win(ps, wid))) { Window troot; Window parent; Window *tchildren; @@ -1077,7 +898,7 @@ find_toplevel2(Display *dpy, Window wid) { // XQueryTree probably fails if you run compton when X is somehow // initializing (like add it in .xinitrc). In this case // just leave it alone. - if (!XQueryTree(dpy, wid, &troot, &parent, &tchildren, + if (!XQueryTree(ps->dpy, wid, &troot, &parent, &tchildren, &tnchildren)) { parent = 0; break; @@ -1093,17 +914,17 @@ find_toplevel2(Display *dpy, Window wid) { /** * Recheck currently focused window and set its w->focused - * to True. + * to true. * - * @param dpy display to use + * @param ps current session * @return struct _win of currently focused window, NULL if not found */ static win * -recheck_focus(Display *dpy) { +recheck_focus(session_t *ps) { // Use EWMH _NET_ACTIVE_WINDOW if enabled - if (opts.use_ewmh_active_win) { - update_ewmh_active_win(dpy); - return active_win; + if (ps->o.use_ewmh_active_win) { + update_ewmh_active_win(ps); + return ps->active_win; } // Determine the currently focused window so we can apply appropriate @@ -1112,16 +933,16 @@ recheck_focus(Display *dpy) { int revert_to; win *w = NULL; - XGetInputFocus(dpy, &wid, &revert_to); + XGetInputFocus(ps->dpy, &wid, &revert_to); // Fallback to the old method if find_toplevel() fails - if (!(w = find_toplevel(wid))) { - w = find_toplevel2(dpy, wid); + if (!(w = find_toplevel(ps, wid))) { + w = find_toplevel2(ps, wid); } // And we set the focus state and opacity here if (w) { - set_focused(dpy, w, True); + set_focused(ps, w, true); return w; } @@ -1129,41 +950,42 @@ recheck_focus(Display *dpy) { } static Picture -root_tile_f(Display *dpy) { +root_tile_f(session_t *ps) { /* - if (opts.paint_on_overlay) { - return root_picture; + if (ps->o.paint_on_overlay) { + return ps->root_picture; } */ Picture picture; Pixmap pixmap; - Bool fill; + bool fill = false; XRenderPictureAttributes pa; int p; pixmap = None; // Get the values of background attributes - for (p = 0; background_props[p]; p++) { - winattr_t attr = wid_get_attr(dpy, root, - XInternAtom(dpy, background_props[p], False), 1L, XA_PIXMAP, 32); - if (attr.nitems) { - pixmap = *((long *) attr.data); - fill = False; - free_winattr(&attr); + for (p = 0; background_props_str[p]; p++) { + winprop_t prop = wid_get_prop(ps, ps->root, + XInternAtom(ps->dpy, background_props_str[p], false), + 1L, XA_PIXMAP, 32); + if (prop.nitems) { + pixmap = *((long *) prop.data); + fill = false; + free_winprop(&prop); break; } - free_winattr(&attr); + free_winprop(&prop); } if (!pixmap) { - pixmap = XCreatePixmap(dpy, root, 1, 1, DefaultDepth(dpy, scr)); - fill = True; + pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, ps->depth); + fill = true; } - pa.repeat = True; + pa.repeat = true; picture = XRenderCreatePicture( - dpy, pixmap, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis), CPRepeat, &pa); if (fill) { @@ -1172,29 +994,32 @@ root_tile_f(Display *dpy) { c.red = c.green = c.blue = 0x8080; c.alpha = 0xffff; XRenderFillRectangle( - dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); + ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); } return picture; } +/** + * Paint root window content. + */ static void -paint_root(Display *dpy) { - if (!root_tile) { - root_tile = root_tile_f(dpy); +paint_root(session_t *ps, Picture tgt_buffer) { + if (!ps->root_tile) { + ps->root_tile = root_tile_f(ps); } XRenderComposite( - dpy, PictOpSrc, root_tile, None, + ps->dpy, PictOpSrc, ps->root_tile, None, tgt_buffer, 0, 0, 0, 0, 0, 0, - root_width, root_height); + ps->root_width, ps->root_height); } /** * Get a rectangular region a window occupies, excluding shadow. */ static XserverRegion -win_get_region(Display *dpy, win *w) { +win_get_region(session_t *ps, win *w) { XRectangle r; r.x = w->a.x; @@ -1202,14 +1027,14 @@ win_get_region(Display *dpy, win *w) { r.width = w->widthb; r.height = w->heightb; - return XFixesCreateRegion(dpy, &r, 1); + return XFixesCreateRegion(ps->dpy, &r, 1); } /** * Get a rectangular region a window occupies, excluding frame and shadow. */ static XserverRegion -win_get_region_noframe(Display *dpy, win *w) { +win_get_region_noframe(session_t *ps, win *w) { XRectangle r; r.x = w->a.x + w->a.border_width + w->left_width; @@ -1218,9 +1043,9 @@ win_get_region_noframe(Display *dpy, win *w) { r.height = max_i(w->a.height - w->top_width - w->bottom_width, 0); if (r.width > 0 && r.height > 0) - return XFixesCreateRegion(dpy, &r, 1); + return XFixesCreateRegion(ps->dpy, &r, 1); else - return XFixesCreateRegion(dpy, NULL, 0); + return XFixesCreateRegion(ps->dpy, NULL, 0); } /** @@ -1230,7 +1055,7 @@ win_get_region_noframe(Display *dpy, win *w) { * function. */ static XserverRegion -win_extents(Display *dpy, win *w) { +win_extents(session_t *ps, win *w) { XRectangle r; r.x = w->a.x; @@ -1265,13 +1090,16 @@ win_extents(Display *dpy, win *w) { } } - return XFixesCreateRegion(dpy, &r, 1); + return XFixesCreateRegion(ps->dpy, &r, 1); } +/** + * Retrieve the bounding shape of a window. + */ static XserverRegion -border_size(Display *dpy, win *w) { +border_size(session_t *ps, win *w) { // Start with the window rectangular region - XserverRegion fin = win_get_region(dpy, w); + XserverRegion fin = win_get_region(ps, w); // Only request for a bounding region if the window is shaped if (w->bounding_shaped) { @@ -1284,29 +1112,32 @@ border_size(Display *dpy, win *w) { */ XserverRegion border = XFixesCreateRegionFromWindow( - dpy, w->id, WindowRegionBounding); + ps->dpy, w->id, WindowRegionBounding); if (!border) return fin; // Translate the region to the correct place - XFixesTranslateRegion(dpy, border, + XFixesTranslateRegion(ps->dpy, border, w->a.x + w->a.border_width, w->a.y + w->a.border_width); // Intersect the bounding region we got with the window rectangle, to // make sure the bounding region is not bigger than the window // rectangle - XFixesIntersectRegion(dpy, fin, fin, border); - XFixesDestroyRegion(dpy, border); + XFixesIntersectRegion(ps->dpy, fin, fin, border); + XFixesDestroyRegion(ps->dpy, border); } return fin; } +/** + * Look for the client window of a particular window. + */ static Window -find_client_win(Display *dpy, Window w) { - if (wid_has_attr(dpy, w, client_atom)) { +find_client_win(session_t *ps, Window w) { + if (wid_has_attr(ps, w, ps->atom_client)) { return w; } @@ -1315,12 +1146,12 @@ find_client_win(Display *dpy, Window w) { unsigned int i; Window ret = 0; - if (!wid_get_children(dpy, w, &children, &nchildren)) { + if (!wid_get_children(ps, w, &children, &nchildren)) { return 0; } for (i = 0; i < nchildren; ++i) { - if ((ret = find_client_win(dpy, children[i]))) + if ((ret = find_client_win(ps, children[i]))) break; } @@ -1329,60 +1160,70 @@ find_client_win(Display *dpy, Window w) { return ret; } +/** + * Retrieve frame extents from a window. + */ static void -get_frame_extents(Display *dpy, win *w, Window client) { +get_frame_extents(session_t *ps, win *w, Window client) { w->left_width = 0; w->right_width = 0; w->top_width = 0; w->bottom_width = 0; - winattr_t attr = wid_get_attr(dpy, client, frame_extents_atom, + winprop_t prop = wid_get_prop(ps, client, ps->atom_frame_extents, 4L, XA_CARDINAL, 32); - if (4 == attr.nitems) { - long *extents = (long *) attr.data; + if (4 == prop.nitems) { + long *extents = (long *) prop.data; w->left_width = extents[0]; w->right_width = extents[1]; w->top_width = extents[2]; w->bottom_width = extents[3]; - if (opts.frame_opacity) - update_reg_ignore_expire(w); + if (ps->o.frame_opacity) + update_reg_ignore_expire(ps, w); } - free_winattr(&attr); + free_winprop(&prop); } +/** + * Get alpha Picture for an opacity in double. + */ static inline Picture -get_alpha_pict_d(double o) { - assert((lround(normalize_d(o) / opts.alpha_step)) <= lround(1.0 / opts.alpha_step)); - return alpha_picts[lround(normalize_d(o) / opts.alpha_step)]; +get_alpha_pict_d(session_t *ps, double o) { + assert((lround(normalize_d(o) / ps->o.alpha_step)) <= lround(1.0 / ps->o.alpha_step)); + return ps->alpha_picts[lround(normalize_d(o) / ps->o.alpha_step)]; } +/** + * Get alpha Picture for an opacity in + * opacity_t. + */ static inline Picture -get_alpha_pict_o(opacity_t o) { - return get_alpha_pict_d((double) o / OPAQUE); +get_alpha_pict_o(session_t *ps, opacity_t o) { + return get_alpha_pict_d(ps, (double) o / OPAQUE); } static win * -paint_preprocess(Display *dpy, win *list) { +paint_preprocess(session_t *ps, win *list) { // Initialize unredir_possible - unredir_possible = False; + ps->unredir_possible = false; win *w; win *t = NULL, *next = NULL; // Trace whether it's the highest window to paint - Bool is_highest = True; + bool is_highest = true; // Fading step calculation - unsigned steps = (sub_unslong(get_time_ms(), fade_time) - + FADE_DELTA_TOLERANCE * opts.fade_delta) / opts.fade_delta; - fade_time += steps * opts.fade_delta; + unsigned steps = (sub_unslong(get_time_ms(), ps->fade_time) + + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta; + ps->fade_time += steps * ps->o.fade_delta; XserverRegion last_reg_ignore = None; for (w = list; w; w = next) { - Bool to_paint = True; + bool to_paint = true; const winmode mode_old = w->mode; // In case calling the fade callback function destroys this window @@ -1390,123 +1231,123 @@ paint_preprocess(Display *dpy, win *list) { opacity_t opacity_old = w->opacity; // Destroy reg_ignore on all windows if they should expire - if (reg_ignore_expire) - free_region(dpy, &w->reg_ignore); + if (ps->reg_ignore_expire) + free_region(ps, &w->reg_ignore); // Run fading - run_fade(dpy, w, steps); + run_fade(ps, w, steps); // Give up if it's not damaged or invisible, or it's unmapped and its // picture is gone (for example due to a ConfigureNotify) if (!w->damaged || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 - || w->a.x >= root_width || w->a.y >= root_height - || (IsUnmapped == w->a.map_state && !w->picture)) { - to_paint = False; + || w->a.x >= ps->root_width || w->a.y >= ps->root_height + || ((IsUnmapped == w->a.map_state || w->destroyed) + && !w->picture)) { + to_paint = false; } if (to_paint) { // If opacity changes if (w->opacity != opacity_old) { - determine_mode(dpy, w); - add_damage_win(dpy, w); + determine_mode(ps, w); + add_damage_win(ps, w); } - w->alpha_pict = get_alpha_pict_o(w->opacity); + w->alpha_pict = get_alpha_pict_o(ps, w->opacity); // End the game if we are using the 0 opacity alpha_pict - if (w->alpha_pict == alpha_picts[0]) { - to_paint = False; + if (w->alpha_pict == ps->alpha_picts[0]) { + to_paint = false; } } if (to_paint) { // Fetch bounding region if (!w->border_size) { - w->border_size = border_size(dpy, w); + w->border_size = border_size(ps, w); } // Fetch window extents if (!w->extents) { - w->extents = win_extents(dpy, w); + w->extents = win_extents(ps, w); // If w->extents does not exist, the previous add_damage_win() // call when opacity changes has no effect, so redo it here. if (w->opacity != opacity_old) - add_damage_win(dpy, w); + add_damage_win(ps, w); } // Calculate frame_opacity { double frame_opacity_old = w->frame_opacity; - if (opts.frame_opacity && 1.0 != opts.frame_opacity + if (ps->o.frame_opacity && 1.0 != ps->o.frame_opacity && win_has_frame(w)) - w->frame_opacity = get_opacity_percent(dpy, w) * - opts.frame_opacity; + w->frame_opacity = get_opacity_percent(w) * + ps->o.frame_opacity; else w->frame_opacity = 0.0; if (w->to_paint && WINDOW_SOLID == mode_old && (0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) - reg_ignore_expire = True; + ps->reg_ignore_expire = true; } - w->frame_alpha_pict = get_alpha_pict_d(w->frame_opacity); + w->frame_alpha_pict = get_alpha_pict_d(ps, w->frame_opacity); // Calculate shadow opacity if (w->frame_opacity) - w->shadow_opacity = opts.shadow_opacity * w->frame_opacity; + w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity; else - w->shadow_opacity = opts.shadow_opacity * get_opacity_percent(dpy, w); + w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w); // Rebuild shadow_pict if necessary if (w->flags & WFLAG_SIZE_CHANGE) - free_picture(dpy, &w->shadow_pict); + free_picture(ps, &w->shadow_pict); if (w->shadow && !w->shadow_pict) { - w->shadow_pict = shadow_picture(dpy, 1, - w->widthb, w->heightb, False); + w->shadow_pict = shadow_picture(ps, 1, w->widthb, w->heightb); } - w->shadow_alpha_pict = get_alpha_pict_d(w->shadow_opacity); + w->shadow_alpha_pict = get_alpha_pict_d(ps, w->shadow_opacity); } if ((to_paint && WINDOW_SOLID == w->mode) != (w->to_paint && WINDOW_SOLID == mode_old)) - reg_ignore_expire = True; + ps->reg_ignore_expire = true; // Add window to damaged area if its painting status changes if (to_paint != w->to_paint) - add_damage_win(dpy, w); + add_damage_win(ps, w); if (to_paint) { // Generate ignore region for painting to reduce GPU load - if (reg_ignore_expire || !w->to_paint) { - free_region(dpy, &w->reg_ignore); + if (ps->reg_ignore_expire || !w->to_paint) { + free_region(ps, &w->reg_ignore); // If the window is solid, we add the window region to the // ignored region if (WINDOW_SOLID == w->mode) { if (!w->frame_opacity) { if (w->border_size) - w->reg_ignore = copy_region(dpy, w->border_size); + w->reg_ignore = copy_region(ps, w->border_size); else - w->reg_ignore = win_get_region(dpy, w); + w->reg_ignore = win_get_region(ps, w); } else { - w->reg_ignore = win_get_region_noframe(dpy, w); + w->reg_ignore = win_get_region_noframe(ps, w); if (w->border_size) - XFixesIntersectRegion(dpy, w->reg_ignore, w->reg_ignore, + XFixesIntersectRegion(ps->dpy, w->reg_ignore, w->reg_ignore, w->border_size); } if (last_reg_ignore) - XFixesUnionRegion(dpy, w->reg_ignore, w->reg_ignore, + XFixesUnionRegion(ps->dpy, w->reg_ignore, w->reg_ignore, last_reg_ignore); } // Otherwise we copy the last region over else if (last_reg_ignore) - w->reg_ignore = copy_region(dpy, last_reg_ignore); + w->reg_ignore = copy_region(ps, last_reg_ignore); else w->reg_ignore = None; } @@ -1514,11 +1355,11 @@ paint_preprocess(Display *dpy, win *list) { last_reg_ignore = w->reg_ignore; if (is_highest && to_paint) { - is_highest = False; + is_highest = false; if (WINDOW_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) - && win_is_fullscreen(w)) - unredir_possible = True; + && win_is_fullscreen(ps, w)) + ps->unredir_possible = true; } // Reset flags @@ -1534,7 +1375,7 @@ paint_preprocess(Display *dpy, win *list) { t = w; } else { - check_fade_fin(dpy, w); + check_fade_fin(ps, w); } if (!destroyed) @@ -1542,15 +1383,15 @@ paint_preprocess(Display *dpy, win *list) { } // If possible, unredirect all windows and stop painting - if (opts.unredir_if_possible && unredir_possible) { - redir_stop(dpy); + if (ps->o.unredir_if_possible && ps->unredir_possible) { + redir_stop(ps); } else { - redir_start(dpy); + redir_start(ps); } // Fetch pictures only if windows are redirected - if (redirected) { + if (ps->redirected) { for (w = t; w; w = w->prev_trans) { // Fetch the picture and pixmap if needed if (!w->picture) { @@ -1558,16 +1399,16 @@ paint_preprocess(Display *dpy, win *list) { XRenderPictFormat *format; Drawable draw = w->id; - if (has_name_pixmap && !w->pixmap) { - set_ignore(dpy, NextRequest(dpy)); - w->pixmap = XCompositeNameWindowPixmap(dpy, w->id); + if (ps->has_name_pixmap && !w->pixmap) { + set_ignore_next(ps); + w->pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); } if (w->pixmap) draw = w->pixmap; - format = XRenderFindVisualFormat(dpy, w->a.visual); + format = XRenderFindVisualFormat(ps->dpy, w->a.visual); pa.subwindow_mode = IncludeInferiors; w->picture = XRenderCreatePicture( - dpy, draw, format, CPSubwindowMode, &pa); + ps->dpy, draw, format, CPSubwindowMode, &pa); } } } @@ -1579,9 +1420,9 @@ paint_preprocess(Display *dpy, win *list) { * Paint the shadow of a window. */ static inline void -win_paint_shadow(Display *dpy, win *w, Picture tgt_buffer) { +win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) { XRenderComposite( - dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, + ps->dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, tgt_buffer, 0, 0, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height); @@ -1591,7 +1432,7 @@ win_paint_shadow(Display *dpy, win *w, Picture tgt_buffer) { * Paint a window itself and dim it if asked. */ static inline void -win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { +win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { int x = w->a.x; int y = w->a.y; int wid = w->widthb; @@ -1601,7 +1442,7 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); if (!w->frame_opacity) { - XRenderComposite(dpy, op, w->picture, alpha_mask, + XRenderComposite(ps->dpy, op, w->picture, alpha_mask, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } else { @@ -1611,7 +1452,7 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { int r = w->right_width; #define COMP_BDR(cx, cy, cwid, chei) \ - XRenderComposite(dpy, PictOpOver, w->picture, w->frame_alpha_pict, \ + XRenderComposite(ps->dpy, PictOpOver, w->picture, w->frame_alpha_pict, \ tgt_buffer, (cx), (cy), 0, 0, x + (cx), y + (cy), (cwid), (chei)) // The following complicated logic is required because some broken @@ -1647,7 +1488,7 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { pwid = wid - l - pwid; if (pwid > 0) { // body - XRenderComposite(dpy, op, w->picture, alpha_mask, + XRenderComposite(ps->dpy, op, w->picture, alpha_mask, tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei); } } @@ -1659,7 +1500,7 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { // Dimming the window if needed if (w->dim) { - XRenderComposite(dpy, PictOpOver, dim_picture, None, + XRenderComposite(ps->dpy, PictOpOver, ps->dim_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } } @@ -1668,14 +1509,14 @@ win_paint_win(Display *dpy, win *w, Picture tgt_buffer) { * Rebuild cached screen_reg. */ static void -rebuild_screen_reg(Display *dpy) { - if (screen_reg) - XFixesDestroyRegion(dpy, screen_reg); - screen_reg = get_screen_region(dpy); +rebuild_screen_reg(session_t *ps) { + if (ps->screen_reg) + XFixesDestroyRegion(ps->dpy, ps->screen_reg); + ps->screen_reg = get_screen_region(ps); } static void -paint_all(Display *dpy, XserverRegion region, win *t) { +paint_all(session_t *ps, XserverRegion region, win *t) { #ifdef DEBUG_REPAINT static struct timespec last_paint = { 0 }; #endif @@ -1684,67 +1525,67 @@ paint_all(Display *dpy, XserverRegion region, win *t) { XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; if (!region) { - region = get_screen_region(dpy); + region = get_screen_region(ps); } else { // Remove the damaged area out of screen - XFixesIntersectRegion(dpy, region, region, screen_reg); + XFixesIntersectRegion(ps->dpy, region, region, ps->screen_reg); } #ifdef MONITOR_REPAINT // Note: MONITOR_REPAINT cannot work with DBE right now. - tgt_buffer = tgt_picture; + ps->tgt_buffer = ps->tgt_picture; #else - if (!tgt_buffer) { + if (!ps->tgt_buffer) { // DBE painting mode: Directly paint to a Picture of the back buffer - if (opts.dbe) { - tgt_buffer = XRenderCreatePicture(dpy, root_dbe, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + if (ps->o.dbe) { + ps->tgt_buffer = XRenderCreatePicture(ps->dpy, ps->root_dbe, + XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0); } // No-DBE painting mode: Paint to an intermediate Picture then paint // the Picture to root window else { Pixmap root_pixmap = XCreatePixmap( - dpy, root, root_width, root_height, - DefaultDepth(dpy, scr)); + ps->dpy, ps->root, ps->root_width, ps->root_height, + ps->depth); - tgt_buffer = XRenderCreatePicture(dpy, root_pixmap, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + ps->tgt_buffer = XRenderCreatePicture(ps->dpy, root_pixmap, + XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0); - XFreePixmap(dpy, root_pixmap); + XFreePixmap(ps->dpy, root_pixmap); } } #endif - XFixesSetPictureClipRegion(dpy, tgt_picture, 0, 0, region); + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_picture, 0, 0, region); #ifdef MONITOR_REPAINT XRenderComposite( - dpy, PictOpSrc, black_picture, None, - tgt_picture, 0, 0, 0, 0, 0, 0, - root_width, root_height); + ps->dpy, PictOpSrc, ps->black_picture, None, + ps->tgt_picture, 0, 0, 0, 0, 0, 0, + ps->root_width, ps->root_height); #endif if (t && t->reg_ignore) { // Calculate the region upon which the root window is to be painted // based on the ignore region of the lowest window, if available - reg_paint = reg_tmp = XFixesCreateRegion(dpy, NULL, 0); - XFixesSubtractRegion(dpy, reg_paint, region, t->reg_ignore); + reg_paint = reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); + XFixesSubtractRegion(ps->dpy, reg_paint, region, t->reg_ignore); } else { reg_paint = region; } - XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, reg_paint); + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); - paint_root(dpy); + paint_root(ps, ps->tgt_buffer); // Create temporary regions for use during painting if (!reg_tmp) - reg_tmp = XFixesCreateRegion(dpy, NULL, 0); - reg_tmp2 = XFixesCreateRegion(dpy, NULL, 0); + reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); + reg_tmp2 = XFixesCreateRegion(ps->dpy, NULL, 0); for (w = t; w; w = w->prev_trans) { // Painting shadow @@ -1756,28 +1597,28 @@ paint_all(Display *dpy, XserverRegion region, win *t) { // If it's the first cycle and reg_tmp2 is not ready, calculate // the paint region here reg_paint = reg_tmp; - XFixesSubtractRegion(dpy, reg_paint, region, w->reg_ignore); + XFixesSubtractRegion(ps->dpy, reg_paint, region, w->reg_ignore); } else { // Otherwise, used the cached region during last cycle reg_paint = reg_tmp2; } - XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->extents); + XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, w->extents); } else { reg_paint = reg_tmp; - XFixesIntersectRegion(dpy, reg_paint, region, w->extents); + XFixesIntersectRegion(ps->dpy, reg_paint, region, w->extents); } // Clear the shadow here instead of in make_shadow() for saving GPU // power and handling shaped windows - if (opts.clear_shadow && w->border_size) - XFixesSubtractRegion(dpy, reg_paint, reg_paint, w->border_size); + if (ps->o.clear_shadow && w->border_size) + XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, w->border_size); // Detect if the region is empty before painting - if (region == reg_paint || !is_region_empty(dpy, reg_paint)) { - XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, reg_paint); + if (region == reg_paint || !is_region_empty(ps, reg_paint)) { + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); - win_paint_shadow(dpy, w, tgt_buffer); + win_paint_shadow(ps, w, ps->tgt_buffer); } } @@ -1785,77 +1626,77 @@ paint_all(Display *dpy, XserverRegion region, win *t) { // window and the bounding region reg_paint = reg_tmp; if (w->prev_trans && w->prev_trans->reg_ignore) { - XFixesSubtractRegion(dpy, reg_paint, region, + XFixesSubtractRegion(ps->dpy, reg_paint, region, w->prev_trans->reg_ignore); // Copy the subtracted region to be used for shadow painting in next // cycle - XFixesCopyRegion(dpy, reg_tmp2, reg_paint); + XFixesCopyRegion(ps->dpy, reg_tmp2, reg_paint); if (w->border_size) - XFixesIntersectRegion(dpy, reg_paint, reg_paint, w->border_size); + XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, w->border_size); } else { if (w->border_size) - XFixesIntersectRegion(dpy, reg_paint, region, w->border_size); + XFixesIntersectRegion(ps->dpy, reg_paint, region, w->border_size); else reg_paint = region; } - if (!is_region_empty(dpy, reg_paint)) { - XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, reg_paint); + if (!is_region_empty(ps, reg_paint)) { + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); // Painting the window - win_paint_win(dpy, w, tgt_buffer); + win_paint_win(ps, w, ps->tgt_buffer); } - check_fade_fin(dpy, w); + check_fade_fin(ps, w); } // Free up all temporary regions - XFixesDestroyRegion(dpy, region); - XFixesDestroyRegion(dpy, reg_tmp); - XFixesDestroyRegion(dpy, reg_tmp2); + XFixesDestroyRegion(ps->dpy, region); + XFixesDestroyRegion(ps->dpy, reg_tmp); + XFixesDestroyRegion(ps->dpy, reg_tmp2); // Do this as early as possible - if (!opts.dbe) - XFixesSetPictureClipRegion(dpy, tgt_buffer, 0, 0, None); + if (!ps->o.dbe) + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, None); - if (VSYNC_NONE != opts.vsync) { + if (VSYNC_NONE != ps->o.vsync) { // Make sure all previous requests are processed to achieve best // effect - XSync(dpy, False); + XSync(ps->dpy, False); } // Wait for VBlank. We could do it aggressively (send the painting // request and XFlush() on VBlank) or conservatively (send the request // only on VBlank). - if (!opts.vsync_aggressive) - vsync_wait(); + if (!ps->o.vsync_aggressive) + vsync_wait(ps); // DBE painting mode, only need to swap the buffer - if (opts.dbe) { + if (ps->o.dbe) { XdbeSwapInfo swap_info = { - .swap_window = (opts.paint_on_overlay ? overlay: root), + .swap_window = (ps->o.paint_on_overlay ? ps->overlay: ps->root), // Is it safe to use XdbeUndefined? .swap_action = XdbeCopied }; - XdbeSwapBuffers(dpy, &swap_info, 1); + XdbeSwapBuffers(ps->dpy, &swap_info, 1); } // No-DBE painting mode - else if (tgt_buffer != tgt_picture) { + else if (ps->tgt_buffer != ps->tgt_picture) { XRenderComposite( - dpy, PictOpSrc, tgt_buffer, None, - tgt_picture, 0, 0, 0, 0, - 0, 0, root_width, root_height); + ps->dpy, PictOpSrc, ps->tgt_buffer, None, + ps->tgt_picture, 0, 0, 0, 0, + 0, 0, ps->root_width, ps->root_height); } - if (opts.vsync_aggressive) - vsync_wait(); + if (ps->o.vsync_aggressive) + vsync_wait(ps); - XFlush(dpy); + XFlush(ps->dpy); #ifdef DEBUG_REPAINT - print_timestamp(); + print_timestamp(ps); struct timespec now = get_time_timespec(); struct timespec diff = { 0 }; timespec_subtract(&diff, &now, &last_paint); @@ -1870,91 +1711,78 @@ paint_all(Display *dpy, XserverRegion region, win *t) { } static void -add_damage(Display *dpy, XserverRegion damage) { - if (all_damage) { - XFixesUnionRegion(dpy, all_damage, all_damage, damage); - XFixesDestroyRegion(dpy, damage); +add_damage(session_t *ps, XserverRegion damage) { + if (ps->all_damage) { + XFixesUnionRegion(ps->dpy, ps->all_damage, ps->all_damage, damage); + XFixesDestroyRegion(ps->dpy, damage); } else { - all_damage = damage; + ps->all_damage = damage; } } static void -repair_win(Display *dpy, win *w) { +repair_win(session_t *ps, win *w) { XserverRegion parts; if (!w->damaged) { - parts = win_extents(dpy, w); - set_ignore(dpy, NextRequest(dpy)); - XDamageSubtract(dpy, w->damage, None, None); + parts = win_extents(ps, w); + set_ignore_next(ps); + XDamageSubtract(ps->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, + parts = XFixesCreateRegion(ps->dpy, 0, 0); + set_ignore_next(ps); + XDamageSubtract(ps->dpy, w->damage, None, parts); + XFixesTranslateRegion(ps->dpy, parts, w->a.x + w->a.border_width, w->a.y + w->a.border_width); } // Remove the part in the damage area that could be ignored - if (!reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) - XFixesSubtractRegion(dpy, parts, parts, w->prev_trans->reg_ignore); + if (!ps->reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) + XFixesSubtractRegion(ps->dpy, parts, parts, w->prev_trans->reg_ignore); - add_damage(dpy, parts); + add_damage(ps, parts); w->damaged = 1; } -static wintype -get_wintype_prop(Display *dpy, Window wid) { - Atom actual; - int format; - unsigned long n = 0, left, i; - long *data = NULL; - int j; - - set_ignore(dpy, NextRequest(dpy)); - if (Success != XGetWindowProperty( - dpy, wid, win_type_atom, 0L, 32L, False, XA_ATOM, - &actual, &format, &n, &left, (unsigned char **) &data) - || !data || !n) { - if (data) - XFree(data); - return WINTYPE_UNKNOWN; - } - - for (i = 0; i < n; ++i) { +static wintype_t +wid_get_prop_wintype(session_t *ps, Window wid) { + int i, j; + + set_ignore_next(ps); + winprop_t prop = wid_get_prop(ps, wid, ps->atom_win_type, 32L, XA_ATOM, 32); + + for (i = 0; i < prop.nitems; ++i) { for (j = 1; j < NUM_WINTYPES; ++j) { - if (win_type[j] == (Atom) data[i]) { - XFree(data); + if (ps->atoms_wintypes[j] == (Atom) ((long *) prop.data)[i]) { + free_winprop(&prop); return j; } } } - XFree(data); + free_winprop(&prop); return WINTYPE_UNKNOWN; } static void -map_win(Display *dpy, Window id, - unsigned long sequence, Bool fade, - Bool override_redirect) { - win *w = find_win(id); +map_win(session_t *ps, Window id, bool override_redirect) { + win *w = find_win(ps, id); // Don't care about window mapping if it's an InputOnly window if (!w || InputOnly == w->a.class) return; - w->focused = False; + w->focused = false; w->a.map_state = IsViewable; // Call XSelectInput() before reading properties so that no property // changes are lost - XSelectInput(dpy, id, determine_evmask(dpy, id, WIN_EVMODE_FRAME)); + XSelectInput(ps->dpy, id, determine_evmask(ps, id, WIN_EVMODE_FRAME)); // Notify compton when the shape of a window changes - if (shape_exists) { - XShapeSelectInput(dpy, id, ShapeNotifyMask); + if (ps->shape_exists) { + XShapeSelectInput(ps->dpy, id, ShapeNotifyMask); } // Detect client window here instead of in add_win() as the client @@ -1963,7 +1791,7 @@ map_win(Display *dpy, Window id, Window cw = 0; // Always recursively look for a window with WM_STATE, as Fluxbox // sets override-redirect flags on all frame windows. - cw = find_client_win(dpy, w->id); + cw = find_client_win(ps, w->id); #ifdef DEBUG_CLIENTWIN printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); #endif @@ -1976,18 +1804,18 @@ map_win(Display *dpy, Window id, #endif } if (cw) { - mark_client_win(dpy, w, cw); + mark_client_win(ps, w, cw); } } else { // Re-mark client window here - mark_client_win(dpy, w, w->client_win); + mark_client_win(ps, w, w->client_win); } // Workaround for _NET_WM_WINDOW_TYPE for Openbox menus, which is // set on a non-override-redirect window with no WM_STATE either if (!w->client_win && WINTYPE_UNKNOWN == w->window_type) - w->window_type = get_wintype_prop(dpy, w->id); + w->window_type = wid_get_prop_wintype(ps, w->id); #ifdef DEBUG_WINTYPE printf("map_win(%#010lx): type %s\n", @@ -1995,54 +1823,54 @@ map_win(Display *dpy, Window id, #endif // Detect if the window is shaped or has rounded corners - win_update_shape_raw(dpy, w); + win_update_shape_raw(ps, w); // Get window name and class if we are tracking them - if (opts.track_wdata) { - win_get_name(dpy, w); - win_get_class(dpy, w); + if (ps->o.track_wdata) { + win_get_name(ps, w); + win_get_class(ps, w); } - if (opts.track_focus) { + if (ps->o.track_focus) { // Occasionally compton does not seem able to get a FocusIn event from // a window just mapped. I suspect it's a timing issue again when the // XSelectInput() is called too late. We have to recheck the focused // window here. It makes no sense if we are using EWMH // _NET_ACTIVE_WINDOW. - if (!opts.use_ewmh_active_win) - recheck_focus(dpy); + if (!ps->o.use_ewmh_active_win) + recheck_focus(ps); // Consider a window without client window a WM window and mark it // focused if mark_wmwin_focused is on, or it's over-redirected and // mark_ovredir_focused is on - if ((opts.mark_wmwin_focused && !w->client_win) - || (opts.mark_ovredir_focused && w->id == w->client_win)) - w->focused = True; + if ((ps->o.mark_wmwin_focused && !w->client_win) + || (ps->o.mark_ovredir_focused && w->id == w->client_win)) + w->focused = true; } // Check for _COMPTON_SHADOW - if (opts.respect_attr_shadow) - win_update_attr_shadow_raw(dpy, w); + if (ps->o.respect_prop_shadow) + win_update_attr_shadow_raw(ps, w); // Many things above could affect shadow - determine_shadow(dpy, w); + determine_shadow(ps, w); // Fading in - calc_opacity(dpy, w, True); + calc_opacity(ps, w, true); // Set fading state - if (opts.no_fading_openclose) { - set_fade_callback(dpy, w, finish_map_win, True); + if (ps->o.no_fading_openclose) { + set_fade_callback(ps, w, finish_map_win, true); // Must be set after we execute the old fade callback, in case we // receive two continuous MapNotify for the same window - w->fade = False; + w->fade = false; } else { - set_fade_callback(dpy, w, NULL, True); - determine_fade(dpy, w); + set_fade_callback(ps, w, NULL, true); + determine_fade(ps, w); } - calc_dim(dpy, w); + calc_dim(ps, w); w->damaged = 1; @@ -2051,42 +1879,42 @@ map_win(Display *dpy, Window id, the window was unmapped, then configure the window to its correct place */ if (w->need_configure) { - configure_win(dpy, &w->queue_configure); + configure_win(ps, &w->queue_configure); } } static void -finish_map_win(Display *dpy, win *w) { - if (opts.no_fading_openclose) - determine_fade(dpy, w); +finish_map_win(session_t *ps, win *w) { + if (ps->o.no_fading_openclose) + determine_fade(ps, w); } static void -finish_unmap_win(Display *dpy, win *w) { +finish_unmap_win(session_t *ps, win *w) { w->damaged = 0; - update_reg_ignore_expire(w); + update_reg_ignore_expire(ps, w); if (w->extents != None) { /* destroys region */ - add_damage(dpy, w->extents); + add_damage(ps, w->extents); w->extents = None; } - free_pixmap(dpy, &w->pixmap); - free_picture(dpy, &w->picture); - free_region(dpy, &w->border_size); - free_picture(dpy, &w->shadow_pict); + free_pixmap(ps, &w->pixmap); + free_picture(ps, &w->picture); + free_region(ps, &w->border_size); + free_picture(ps, &w->shadow_pict); } static void -unmap_callback(Display *dpy, win *w) { - finish_unmap_win(dpy, w); +unmap_callback(session_t *ps, win *w) { + finish_unmap_win(ps, w); } static void -unmap_win(Display *dpy, Window id, Bool fade) { - win *w = find_win(id); +unmap_win(session_t *ps, Window id) { + win *w = find_win(ps, id); if (!w || IsUnmapped == w->a.map_state) return; @@ -2094,48 +1922,36 @@ unmap_win(Display *dpy, Window id, Bool fade) { // Fading out w->opacity_tgt = 0; - set_fade_callback(dpy, w, unmap_callback, False); - if (opts.no_fading_openclose) - w->fade = False; + set_fade_callback(ps, w, unmap_callback, false); + if (ps->o.no_fading_openclose) + w->fade = false; // don't care about properties anymore - // Will get BadWindow if the window is destroyed - set_ignore(dpy, NextRequest(dpy)); - XSelectInput(dpy, w->id, 0); - - if (w->client_win) { - set_ignore(dpy, NextRequest(dpy)); - XSelectInput(dpy, w->client_win, 0); - } + win_ev_stop(ps, w); } static opacity_t -wid_get_opacity_prop(Display *dpy, Window wid, opacity_t def) { - Atom actual; - int format; - unsigned long n, left; +wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def) { + opacity_t val = def; - unsigned char *data; - int result = XGetWindowProperty( - dpy, wid, opacity_atom, 0L, 1L, False, - XA_CARDINAL, &actual, &format, &n, &left, &data); + winprop_t prop = wid_get_prop(ps, wid, ps->atom_opacity, 1L, + XA_CARDINAL, 32); - if (result == Success && data != NULL) { - opacity_t i = *((opacity_t *) data); - XFree(data); - return i; - } + if (prop.nitems) + val = *((long *) prop.data); + + free_winprop(&prop); - return def; + return val; } static double -get_opacity_percent(Display *dpy, win *w) { +get_opacity_percent(win *w) { return ((double) w->opacity) / OPAQUE; } static void -determine_mode(Display *dpy, win *w) { +determine_mode(session_t *ps, win *w) { winmode mode; XRenderPictFormat *format; @@ -2144,7 +1960,7 @@ determine_mode(Display *dpy, win *w) { if (w->a.class == InputOnly) { format = 0; } else { - format = XRenderFindVisualFormat(dpy, w->a.visual); + format = XRenderFindVisualFormat(ps->dpy, w->a.visual); } if (format && format->type == PictTypeDirect @@ -2180,7 +1996,7 @@ determine_mode(Display *dpy, win *w) { * refetched */ static void -calc_opacity(Display *dpy, win *w, Bool refetch_prop) { +calc_opacity(session_t *ps, win *w, bool refetch_prop) { opacity_t opacity; // Do nothing for unmapped window, calc_opacity() will be called @@ -2191,42 +2007,45 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { // Do not refetch the opacity window attribute unless necessary, this // is probably an expensive operation in some cases if (refetch_prop) { - w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); - if (!opts.detect_client_opacity || !w->client_win + w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); + if (!ps->o.detect_client_opacity || !w->client_win || w->id == w->client_win) w->opacity_prop_client = OPAQUE; else - w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, + w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, OPAQUE); } if (OPAQUE == (opacity = w->opacity_prop) && OPAQUE == (opacity = w->opacity_prop_client)) { - opacity = opts.wintype_opacity[w->window_type] * OPAQUE; + opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; } // Respect inactive_opacity in some cases - if (opts.inactive_opacity && is_normal_win(w) && False == w->focused - && (OPAQUE == opacity || opts.inactive_opacity_override)) { - opacity = opts.inactive_opacity; + if (ps->o.inactive_opacity && is_normal_win(w) && false == w->focused + && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { + opacity = ps->o.inactive_opacity; } w->opacity_tgt = opacity; } +/** + * Determine whether a window is to be dimmed. + */ static void -calc_dim(Display *dpy, win *w) { - Bool dim; +calc_dim(session_t *ps, win *w) { + bool dim; - if (opts.inactive_dim && is_normal_win(w) && !(w->focused)) { - dim = True; + if (ps->o.inactive_dim && is_normal_win(w) && !(w->focused)) { + dim = true; } else { - dim = False; + dim = false; } if (dim != w->dim) { w->dim = dim; - add_damage_win(dpy, w); + add_damage_win(ps, w); } } @@ -2234,19 +2053,19 @@ calc_dim(Display *dpy, win *w) { * Determine if a window should fade on opacity change. */ static void -determine_fade(Display *dpy, win *w) { - w->fade = opts.wintype_fade[w->window_type]; +determine_fade(session_t *ps, win *w) { + w->fade = ps->o.wintype_fade[w->window_type]; } /** * Update window-shape. */ static void -win_update_shape_raw(Display *dpy, win *w) { - if (shape_exists) { - w->bounding_shaped = wid_bounding_shaped(dpy, w->id); - if (w->bounding_shaped && opts.detect_rounded_corners) - win_rounded_corners(dpy, w); +win_update_shape_raw(session_t *ps, win *w) { + if (ps->shape_exists) { + w->bounding_shaped = wid_bounding_shaped(ps, w->id); + if (w->bounding_shaped && ps->o.detect_rounded_corners) + win_rounded_corners(ps, w); } } @@ -2254,20 +2073,20 @@ win_update_shape_raw(Display *dpy, win *w) { * Update window-shape related information. */ static void -win_update_shape(Display *dpy, win *w) { - if (shape_exists) { - // Bool bounding_shaped_old = w->bounding_shaped; +win_update_shape(session_t *ps, win *w) { + if (ps->shape_exists) { + // bool bounding_shaped_old = w->bounding_shaped; - win_update_shape_raw(dpy, w); + win_update_shape_raw(ps, w); // Shadow state could be changed - determine_shadow(dpy, w); + determine_shadow(ps, w); /* // If clear_shadow state on the window possibly changed, destroy the old // shadow_pict - if (opts.clear_shadow && w->bounding_shaped != bounding_shaped_old) - free_picture(dpy, &w->shadow_pict); + if (ps->o.clear_shadow && w->bounding_shaped != bounding_shaped_old) + free_picture(ps, &w->shadow_pict); */ } } @@ -2278,18 +2097,18 @@ win_update_shape(Display *dpy, win *w) { * The property must be set on the outermost window, usually the WM frame. */ static void -win_update_attr_shadow_raw(Display *dpy, win *w) { - winattr_t attr = wid_get_attr(dpy, w->id, compton_shadow_atom, 1, +win_update_attr_shadow_raw(session_t *ps, win *w) { + winprop_t prop = wid_get_prop(ps, w->id, ps->atom_compton_shadow, 1, XA_CARDINAL, 32); - - if (!attr.nitems) { - free_winattr(&attr); - w->attr_shadow = -1; - return; + if (!prop.nitems) { + w->attr_shadow = -1; + } + else { + w->attr_shadow = *((long *) prop.data); } - w->attr_shadow = *((long *) attr.data); + free_winprop(&prop); } /** @@ -2297,13 +2116,13 @@ win_update_attr_shadow_raw(Display *dpy, win *w) { * things. */ static void -win_update_attr_shadow(Display *dpy, win *w) { +win_update_attr_shadow(session_t *ps, win *w) { long attr_shadow_old = w->attr_shadow; - win_update_attr_shadow_raw(dpy, w); + win_update_attr_shadow_raw(ps, w); if (w->attr_shadow != attr_shadow_old) - determine_shadow(dpy, w); + determine_shadow(ps, w); } /** @@ -2311,29 +2130,29 @@ win_update_attr_shadow(Display *dpy, win *w) { * on shadow state. */ static void -determine_shadow(Display *dpy, win *w) { - Bool shadow_old = w->shadow; +determine_shadow(session_t *ps, win *w) { + bool shadow_old = w->shadow; - w->shadow = (opts.wintype_shadow[w->window_type] - && !win_match(w, opts.shadow_blacklist, &w->cache_sblst) - && !(opts.shadow_ignore_shaped && w->bounding_shaped + w->shadow = (ps->o.wintype_shadow[w->window_type] + && !win_match(w, ps->o.shadow_blacklist, &w->cache_sblst) + && !(ps->o.shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) - && !(opts.respect_attr_shadow && 0 == w->attr_shadow)); + && !(ps->o.respect_prop_shadow && 0 == w->attr_shadow)); // Window extents need update on shadow state change if (w->shadow != shadow_old) { // Shadow geometry currently doesn't change on shadow state change - // calc_shadow_geometry(dpy, w); + // calc_shadow_geometry(ps, w); if (w->extents) { // Mark the old extents as damaged if the shadow is removed if (!w->shadow) - add_damage(dpy, w->extents); + add_damage(ps, w->extents); else - free_region(dpy, &w->extents); - w->extents = win_extents(dpy, w); + free_region(ps, &w->extents); + w->extents = win_extents(ps, w); // Mark the new extents as damaged if the shadow is added if (w->shadow) - add_damage_win(dpy, w); + add_damage_win(ps, w); } } } @@ -2343,10 +2162,10 @@ determine_shadow(Display *dpy, win *w) { */ static void -calc_win_size(Display *dpy, win *w) { +calc_win_size(session_t *ps, win *w) { w->widthb = w->a.width + w->a.border_width * 2; w->heightb = w->a.height + w->a.border_width * 2; - calc_shadow_geometry(dpy, w); + calc_shadow_geometry(ps, w); w->flags |= WFLAG_SIZE_CHANGE; } @@ -2354,11 +2173,11 @@ calc_win_size(Display *dpy, win *w) { * Calculate and update geometry of the shadow of a window. */ static void -calc_shadow_geometry(Display *dpy, win *w) { - w->shadow_dx = opts.shadow_offset_x; - w->shadow_dy = opts.shadow_offset_y; - w->shadow_width = w->widthb + gaussian_map->size; - w->shadow_height = w->heightb + gaussian_map->size; +calc_shadow_geometry(session_t *ps, win *w) { + w->shadow_dx = ps->o.shadow_offset_x; + w->shadow_dy = ps->o.shadow_offset_y; + w->shadow_width = w->widthb + ps->gaussian_map->size; + w->shadow_height = w->heightb + ps->gaussian_map->size; } /** @@ -2369,27 +2188,27 @@ calc_shadow_geometry(Display *dpy, win *w) { * @param client window ID of the client window */ static void -mark_client_win(Display *dpy, win *w, Window client) { +mark_client_win(session_t *ps, win *w, Window client) { w->client_win = client; - XSelectInput(dpy, client, determine_evmask(dpy, client, WIN_EVMODE_CLIENT)); + XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_CLIENT)); // Get the frame width and monitor further frame width changes on client // window if necessary - if (opts.frame_opacity) { - get_frame_extents(dpy, w, client); + if (ps->o.frame_opacity) { + get_frame_extents(ps, w, client); } // Detect window type here if (WINTYPE_UNKNOWN == w->window_type) - w->window_type = get_wintype_prop(dpy, w->client_win); + w->window_type = wid_get_prop_wintype(ps, w->client_win); // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take // override-redirect windows or windows without WM_TRANSIENT_FOR as // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. if (WINTYPE_UNKNOWN == w->window_type) { if (w->a.override_redirect - || !wid_has_attr(dpy, client, transient_atom)) + || !wid_has_attr(ps, client, ps->atom_transient)) w->window_type = WINTYPE_NORMAL; else w->window_type = WINTYPE_DIALOG; @@ -2397,8 +2216,9 @@ mark_client_win(Display *dpy, win *w, Window client) { } static void -add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { - if (find_win(id)) { +add_win(session_t *ps, Window id, Window prev, bool override_redirect) { + // Reject overlay window and already added windows + if (id == ps->overlay || find_win(ps, id)) { return; } @@ -2408,24 +2228,24 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { if (!new) return; if (prev) { - for (p = &list; *p; p = &(*p)->next) { + for (p = &ps->list; *p; p = &(*p)->next) { if ((*p)->id == prev && !(*p)->destroyed) break; } } else { - p = &list; + p = &ps->list; } new->id = id; - set_ignore(dpy, NextRequest(dpy)); + set_ignore_next(ps); - if (!XGetWindowAttributes(dpy, id, &new->a)) { + if (!XGetWindowAttributes(ps->dpy, id, &new->a)) { free(new); return; } new->damaged = 0; - new->to_paint = False; + new->to_paint = false; new->pixmap = None; new->picture = None; @@ -2433,9 +2253,9 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { 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->damage_sequence = NextRequest(ps->dpy); + set_ignore_next(ps); + new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); } new->name = NULL; @@ -2443,13 +2263,13 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->class_general = NULL; new->cache_sblst = NULL; new->cache_fblst = NULL; - new->bounding_shaped = False; - new->rounded_corners = False; + new->bounding_shaped = false; + new->rounded_corners = false; new->border_size = None; new->reg_ignore = None; new->extents = None; - new->shadow = False; + new->shadow = false; new->shadow_opacity = 0.0; new->shadow_pict = None; new->shadow_alpha_pict = None; @@ -2461,15 +2281,15 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->opacity_tgt = 0; new->opacity_prop = OPAQUE; new->opacity_prop_client = OPAQUE; - new->fade = False; + new->fade = false; new->fade_callback = NULL; new->alpha_pict = None; new->frame_opacity = 1.0; new->frame_alpha_pict = None; - new->dim = False; - new->focused = False; - new->destroyed = False; - new->need_configure = False; + new->dim = false; + new->focused = false; + new->destroyed = false; + new->need_configure = false; new->window_type = WINTYPE_UNKNOWN; new->mode = WINDOW_TRANS; new->attr_shadow = -1; @@ -2485,21 +2305,21 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->flags = 0; - calc_win_size(dpy, new); + calc_win_size(ps, new); new->next = *p; *p = new; if (new->a.map_state == IsViewable) { - map_win(dpy, id, new->damage_sequence - 1, True, override_redirect); + map_win(ps, id, override_redirect); } } static void -restack_win(Display *dpy, win *w, Window new_above) { +restack_win(session_t *ps, win *w, Window new_above) { Window old_above; - update_reg_ignore_expire(w); + update_reg_ignore_expire(ps, w); if (w->next) { old_above = w->next->id; @@ -2511,14 +2331,14 @@ restack_win(Display *dpy, win *w, Window new_above) { win **prev; /* unhook */ - for (prev = &list; *prev; prev = &(*prev)->next) { + for (prev = &ps->list; *prev; prev = &(*prev)->next) { if ((*prev) == w) break; } *prev = w->next; /* rehook */ - for (prev = &list; *prev; prev = &(*prev)->next) { + for (prev = &ps->list; *prev; prev = &(*prev)->next) { if ((*prev)->id == new_above && !(*prev)->destroyed) break; } @@ -2531,7 +2351,7 @@ restack_win(Display *dpy, win *w, Window new_above) { const char *desc; char *window_name = NULL; bool to_free; - win* c = list; + win* c = ps->list; printf("restack_win(%#010lx, %#010lx): " "Window stack modified. Current stack:\n", w->id, new_above); @@ -2539,7 +2359,7 @@ restack_win(Display *dpy, win *w, Window new_above) { for (; c; c = c->next) { window_name = "(Failed to get title)"; - to_free = ev_window_name(dpy, c->id, &window_name); + to_free = ev_window_name(ps, c->id, &window_name); desc = ""; if (c->destroyed) desc = "(D) "; @@ -2557,21 +2377,21 @@ restack_win(Display *dpy, win *w, Window new_above) { } static void -configure_win(Display *dpy, XConfigureEvent *ce) { - if (ce->window == root) { - if (tgt_buffer) { - XRenderFreePicture(dpy, tgt_buffer); - tgt_buffer = None; +configure_win(session_t *ps, XConfigureEvent *ce) { + if (ce->window == ps->root) { + if (ps->tgt_buffer) { + XRenderFreePicture(ps->dpy, ps->tgt_buffer); + ps->tgt_buffer = None; } - root_width = ce->width; - root_height = ce->height; + ps->root_width = ce->width; + ps->root_height = ce->height; - rebuild_screen_reg(dpy); + rebuild_screen_reg(ps); return; } - win *w = find_win(ce->window); + win *w = find_win(ps, ce->window); XserverRegion damage = None; if (!w) @@ -2579,38 +2399,38 @@ configure_win(Display *dpy, XConfigureEvent *ce) { if (w->a.map_state == IsUnmapped) { /* save the configure event for when the window maps */ - w->need_configure = True; + w->need_configure = true; w->queue_configure = *ce; - restack_win(dpy, w, ce->above); + restack_win(ps, w, ce->above); } else { if (!(w->need_configure)) { - restack_win(dpy, w, ce->above); + restack_win(ps, w, ce->above); } // Windows restack (including window restacks happened when this // window is not mapped) could mess up all reg_ignore - reg_ignore_expire = True; + ps->reg_ignore_expire = true; - w->need_configure = False; + w->need_configure = false; - damage = XFixesCreateRegion(dpy, 0, 0); + damage = XFixesCreateRegion(ps->dpy, 0, 0); if (w->extents != None) { - XFixesCopyRegion(dpy, damage, w->extents); + XFixesCopyRegion(ps->dpy, damage, w->extents); } // If window geometry did not change, don't free extents here if (w->a.x != ce->x || w->a.y != ce->y || w->a.width != ce->width || w->a.height != ce->height) { - free_region(dpy, &w->extents); - free_region(dpy, &w->border_size); + free_region(ps, &w->extents); + free_region(ps, &w->border_size); } w->a.x = ce->x; w->a.y = ce->y; if (w->a.width != ce->width || w->a.height != ce->height) { - free_pixmap(dpy, &w->pixmap); - free_picture(dpy, &w->picture); + free_pixmap(ps, &w->pixmap); + free_picture(ps, &w->picture); } if (w->a.width != ce->width || w->a.height != ce->height @@ -2618,19 +2438,19 @@ configure_win(Display *dpy, XConfigureEvent *ce) { w->a.width = ce->width; w->a.height = ce->height; w->a.border_width = ce->border_width; - calc_win_size(dpy, w); + calc_win_size(ps, w); // Rounded corner detection is affected by window size - if (shape_exists && opts.shadow_ignore_shaped - && opts.detect_rounded_corners && w->bounding_shaped) - win_update_shape(dpy, w); + if (ps->shape_exists && ps->o.shadow_ignore_shaped + && ps->o.detect_rounded_corners && w->bounding_shaped) + win_update_shape(ps, w); } if (damage) { - XserverRegion extents = win_extents(dpy, w); - XFixesUnionRegion(dpy, damage, damage, extents); - XFixesDestroyRegion(dpy, extents); - add_damage(dpy, damage); + XserverRegion extents = win_extents(ps, w); + XFixesUnionRegion(ps->dpy, damage, damage, extents); + XFixesDestroyRegion(ps->dpy, extents); + add_damage(ps, damage); } } @@ -2638,40 +2458,35 @@ configure_win(Display *dpy, XConfigureEvent *ce) { } static void -circulate_win(Display *dpy, XCirculateEvent *ce) { - win *w = find_win(ce->window); +circulate_win(session_t *ps, XCirculateEvent *ce) { + win *w = find_win(ps, ce->window); Window new_above; if (!w) return; if (ce->place == PlaceOnTop) { - new_above = list->id; + new_above = ps->list->id; } else { new_above = None; } - restack_win(dpy, w, new_above); + restack_win(ps, w, new_above); } static void -finish_destroy_win(Display *dpy, Window id) { +finish_destroy_win(session_t *ps, Window id) { win **prev, *w; - for (prev = &list; (w = *prev); prev = &w->next) { + for (prev = &ps->list; (w = *prev); prev = &w->next) { if (w->id == id && w->destroyed) { - finish_unmap_win(dpy, w); + finish_unmap_win(ps, w); *prev = w->next; // Clear active_win if it's pointing to the destroyed window - if (active_win) - active_win = NULL; + if (ps->active_win) + ps->active_win = NULL; - free_picture(dpy, &w->shadow_pict); - free_damage(dpy, &w->damage); - free_region(dpy, &w->reg_ignore); - free(w->name); - free(w->class_instance); - free(w->class_general); + free_win_res(ps, w); free(w); break; @@ -2680,67 +2495,72 @@ finish_destroy_win(Display *dpy, Window id) { } static void -destroy_callback(Display *dpy, win *w) { - finish_destroy_win(dpy, w->id); +destroy_callback(session_t *ps, win *w) { + finish_destroy_win(ps, w->id); } static void -destroy_win(Display *dpy, Window id, Bool fade) { - win *w = find_win(id); +destroy_win(session_t *ps, Window id) { + win *w = find_win(ps, id); if (w) { - w->destroyed = True; + w->destroyed = true; // Fading out the window w->opacity_tgt = 0; - set_fade_callback(dpy, w, destroy_callback, False); + set_fade_callback(ps, w, destroy_callback, false); } } static inline void -root_damaged(void) { - if (root_tile) { - XClearArea(dpy, root, 0, 0, 0, 0, True); - // if (root_picture != root_tile) { - XRenderFreePicture(dpy, root_tile); - root_tile = None; +root_damaged(session_t *ps) { + if (ps->root_tile) { + XClearArea(ps->dpy, ps->root, 0, 0, 0, 0, true); + // if (ps->root_picture != ps->root_tile) { + XRenderFreePicture(ps->dpy, ps->root_tile); + ps->root_tile = None; /* } if (root_damage) { - XserverRegion parts = XFixesCreateRegion(dpy, 0, 0); - XDamageSubtract(dpy, root_damage, None, parts); - add_damage(dpy, parts); + XserverRegion parts = XFixesCreateRegion(ps->dpy, 0, 0); + XDamageSubtract(ps->dpy, root_damage, None, parts); + add_damage(ps, parts); } */ } // Mark screen damaged if we are painting on overlay - if (opts.paint_on_overlay) - add_damage(dpy, get_screen_region(dpy)); + if (ps->o.paint_on_overlay) + add_damage(ps, get_screen_region(ps)); } static void -damage_win(Display *dpy, XDamageNotifyEvent *de) { +damage_win(session_t *ps, XDamageNotifyEvent *de) { /* - if (root == de->drawable) { + if (ps->root == de->drawable) { root_damaged(); return; } */ - win *w = find_win(de->drawable); + win *w = find_win(ps, de->drawable); if (!w) return; - repair_win(dpy, w); + repair_win(ps, w); } +/** + * Xlib error handler function. + */ static int error(Display *dpy, XErrorEvent *ev) { + session_t * const ps = ps_g; + int o; const char *name = "Unknown"; - if (should_ignore(dpy, ev->serial)) { + if (should_ignore(ps, ev->serial)) { return 0; } - if (ev->request_code == composite_opcode + if (ev->request_code == ps->composite_opcode && ev->minor_code == X_CompositeRedirectSubwindows) { fprintf(stderr, "Another composite manager is already running\n"); exit(1); @@ -2748,17 +2568,17 @@ error(Display *dpy, XErrorEvent *ev) { #define CASESTRRET2(s) case s: name = #s; break - o = ev->error_code - xfixes_error; + o = ev->error_code - ps->xfixes_error; switch (o) { CASESTRRET2(BadRegion); } - o = ev->error_code - damage_error; + o = ev->error_code - ps->damage_error; switch (o) { CASESTRRET2(BadDamage); } - o = ev->error_code - render_error; + o = ev->error_code - ps->render_error; switch (o) { CASESTRRET2(BadPictFormat); CASESTRRET2(BadPicture); @@ -2789,7 +2609,7 @@ error(Display *dpy, XErrorEvent *ev) { #undef CASESTRRET2 - print_timestamp(); + print_timestamp(ps); printf("error %d (%s) request %d minor %d serial %lu\n", ev->error_code, name, ev->request_code, ev->minor_code, ev->serial); @@ -2798,74 +2618,84 @@ error(Display *dpy, XErrorEvent *ev) { } static void -expose_root(Display *dpy, XRectangle *rects, int nrects) { - XserverRegion region = XFixesCreateRegion(dpy, rects, nrects); - add_damage(dpy, region); +expose_root(session_t *ps, XRectangle *rects, int nrects) { + XserverRegion region = XFixesCreateRegion(ps->dpy, rects, nrects); + add_damage(ps, region); } -static Bool -wid_get_text_prop(Display *dpy, Window wid, Atom prop, +/** + * Get the value of a text property of a window. + */ +static bool +wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr) { XTextProperty text_prop; - if (!(XGetTextProperty(dpy, wid, &text_prop, prop) && text_prop.value)) - return False; + if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value)) + return false; if (Success != - XmbTextPropertyToTextList(dpy, &text_prop, pstrlst, pnstr) + XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr) || !*pnstr) { *pnstr = 0; if (*pstrlst) XFreeStringList(*pstrlst); - return False; + return false; } - return True; + return true; } -static Bool -wid_get_name(Display *dpy, Window wid, char **name) { +/** + * Get the name of a window from window ID. + */ +static bool +wid_get_name(session_t *ps, Window wid, char **name) { XTextProperty text_prop; char **strlst = NULL; int nstr = 0; - // set_ignore(dpy, NextRequest(dpy)); - if (!(XGetTextProperty(dpy, wid, &text_prop, name_ewmh_atom) + // set_ignore_next(ps); + if (!(XGetTextProperty(ps->dpy, wid, &text_prop, ps->atom_name_ewmh) && text_prop.value)) { - // set_ignore(dpy, NextRequest(dpy)); + // set_ignore_next(ps); #ifdef DEBUG_WINDATA printf("wid_get_name(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); #endif - if (!(XGetWMName(dpy, wid, &text_prop) && text_prop.value)) { - return False; + if (!(XGetWMName(ps->dpy, wid, &text_prop) && text_prop.value)) { + return false; } } if (Success != - XmbTextPropertyToTextList(dpy, &text_prop, &strlst, &nstr) + XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) || !nstr || !strlst) { if (strlst) XFreeStringList(strlst); - return False; + return false; } *name = mstrcpy(strlst[0]); XFreeStringList(strlst); - return True; + return true; } +/** + * Retrieve the name of a window and update its win + * structure. + */ static int -win_get_name(Display *dpy, win *w) { - Bool ret; +win_get_name(session_t *ps, win *w) { + bool ret; char *name_old = w->name; // Can't do anything if there's no client window if (!w->client_win) - return False; + return false; // Get the name - ret = wid_get_name(dpy, w->client_win, &w->name); + ret = wid_get_name(ps, w->client_win, &w->name); // Return -1 if wid_get_name() failed, 0 if name didn't change, 1 if // it changes @@ -2888,14 +2718,18 @@ win_get_name(Display *dpy, win *w) { return ret; } -static Bool -win_get_class(Display *dpy, win *w) { +/** + * Retrieve the WM_CLASS of a window and update its + * win structure. + */ +static bool +win_get_class(session_t *ps, win *w) { char **strlst = NULL; int nstr = 0; // Can't do anything if there's no client window if (!w->client_win) - return False; + return false; // Free and reset old strings free(w->class_instance); @@ -2904,8 +2738,8 @@ win_get_class(Display *dpy, win *w) { w->class_general = NULL; // Retrieve the property string list - if (!wid_get_text_prop(dpy, w->client_win, class_atom, &strlst, &nstr)) - return False; + if (!wid_get_text_prop(ps, w->client_win, ps->atom_class, &strlst, &nstr)) + return false; // Copy the strings if successful w->class_instance = mstrcpy(strlst[0]); @@ -2921,7 +2755,7 @@ win_get_class(Display *dpy, win *w) { w->id, w->client_win, w->class_instance, w->class_general); #endif - return True; + return true; } #ifdef DEBUG_EVENTS @@ -2934,7 +2768,7 @@ ev_serial(XEvent *ev) { } static const char * -ev_name(XEvent *ev) { +ev_name(session_t *ps, XEvent *ev) { static char buf[128]; switch (ev->type & 0x7f) { CASESTRRET(FocusIn); @@ -2950,11 +2784,11 @@ ev_name(XEvent *ev) { CASESTRRET(PropertyNotify); CASESTRRET(ClientMessage); default: - if (ev->type == damage_event + XDamageNotify) { + if (ev->type == ps->damage_event + XDamageNotify) { return "Damage"; } - if (shape_exists && ev->type == shape_event) { + if (ps->shape_exists && ev->type == ps->shape_event) { return "ShapeNotify"; } @@ -2965,7 +2799,7 @@ ev_name(XEvent *ev) { } static Window -ev_window(XEvent *ev) { +ev_window(session_t *ps, XEvent *ev) { switch (ev->type) { case FocusIn: case FocusOut: @@ -2991,11 +2825,11 @@ ev_window(XEvent *ev) { case ClientMessage: return ev->xclient.window; default: - if (ev->type == damage_event + XDamageNotify) { + if (ev->type == ps->damage_event + XDamageNotify) { return ((XDamageNotifyEvent *)ev)->drawable; } - if (shape_exists && ev->type == shape_event) { + if (ps->shape_exists && ev->type == ps->shape_event) { return ((XShapeEvent *) ev)->window; } @@ -3039,9 +2873,7 @@ ev_focus_report(XFocusChangeEvent* ev) { #endif -/** - * Events - */ +// === Events === /** * Determine whether we should respond to a FocusIn/Out @@ -3054,7 +2886,7 @@ ev_focus_accept(XFocusChangeEvent *ev) { } inline static void -ev_focus_in(XFocusChangeEvent *ev) { +ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { #ifdef DEBUG_EVENTS ev_focus_report(ev); #endif @@ -3062,16 +2894,16 @@ ev_focus_in(XFocusChangeEvent *ev) { if (!ev_focus_accept(ev)) return; - win *w = find_win(ev->window); + win *w = find_win(ps, ev->window); // To deal with events sent from windows just destroyed if (!w) return; - set_focused(dpy, w, True); + set_focused(ps, w, true); } inline static void -ev_focus_out(XFocusChangeEvent *ev) { +ev_focus_out(session_t *ps, XFocusChangeEvent *ev) { #ifdef DEBUG_EVENTS ev_focus_report(ev); #endif @@ -3079,93 +2911,93 @@ ev_focus_out(XFocusChangeEvent *ev) { if (!ev_focus_accept(ev)) return; - win *w = find_win(ev->window); + win *w = find_win(ps, ev->window); // To deal with events sent from windows just destroyed if (!w) return; - set_focused(dpy, w, False); + set_focused(ps, w, false); } inline static void -ev_create_notify(XCreateWindowEvent *ev) { - assert(ev->parent == root); - add_win(dpy, ev->window, 0, ev->override_redirect); +ev_create_notify(session_t *ps, XCreateWindowEvent *ev) { + assert(ev->parent == ps->root); + add_win(ps, ev->window, 0, ev->override_redirect); } inline static void -ev_configure_notify(XConfigureEvent *ev) { +ev_configure_notify(session_t *ps, XConfigureEvent *ev) { #ifdef DEBUG_EVENTS printf(" { send_event: %d, " " above: %#010lx, " " override_redirect: %d }\n", ev->send_event, ev->above, ev->override_redirect); #endif - configure_win(dpy, ev); + configure_win(ps, ev); } inline static void -ev_destroy_notify(XDestroyWindowEvent *ev) { - destroy_win(dpy, ev->window, True); +ev_destroy_notify(session_t *ps, XDestroyWindowEvent *ev) { + destroy_win(ps, ev->window); } inline static void -ev_map_notify(XMapEvent *ev) { - map_win(dpy, ev->window, ev->serial, True, ev->override_redirect); +ev_map_notify(session_t *ps, XMapEvent *ev) { + map_win(ps, ev->window, ev->override_redirect); } inline static void -ev_unmap_notify(XUnmapEvent *ev) { - unmap_win(dpy, ev->window, True); +ev_unmap_notify(session_t *ps, XUnmapEvent *ev) { + unmap_win(ps, ev->window); } inline static void -ev_reparent_notify(XReparentEvent *ev) { - if (ev->parent == root) { - add_win(dpy, ev->window, 0, ev->override_redirect); +ev_reparent_notify(session_t *ps, XReparentEvent *ev) { + if (ev->parent == ps->root) { + add_win(ps, ev->window, 0, ev->override_redirect); } else { - destroy_win(dpy, ev->window, True); + destroy_win(ps, ev->window); // Reset event mask in case something wrong happens - XSelectInput(dpy, ev->window, - determine_evmask(dpy, ev->window, WIN_EVMODE_UNKNOWN)); + XSelectInput(ps->dpy, ev->window, + determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)); /* // Check if the window is a client window of another - win *w_top = find_toplevel2(dpy, ev->window); + win *w_top = find_toplevel2(ps, ev->window); if (w_top && !(w_top->client_win)) { - mark_client_win(dpy, w_top, ev->window); + mark_client_win(ps, w_top, ev->window); } */ } } inline static void -ev_circulate_notify(XCirculateEvent *ev) { - circulate_win(dpy, ev); +ev_circulate_notify(session_t *ps, XCirculateEvent *ev) { + circulate_win(ps, ev); } inline static void -ev_expose(XExposeEvent *ev) { - if (ev->window == root || (overlay && ev->window == overlay)) { +ev_expose(session_t *ps, XExposeEvent *ev) { + if (ev->window == ps->root || (ps->overlay && ev->window == ps->overlay)) { 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; + if (ps->n_expose == ps->size_expose) { + if (ps->expose_rects) { + ps->expose_rects = realloc(ps->expose_rects, + (ps->size_expose + more) * sizeof(XRectangle)); + ps->size_expose += more; } else { - expose_rects = malloc(more * sizeof(XRectangle)); - size_expose = more; + ps->expose_rects = malloc(more * sizeof(XRectangle)); + ps->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++; + ps->expose_rects[ps->n_expose].x = ev->x; + ps->expose_rects[ps->n_expose].y = ev->y; + ps->expose_rects[ps->n_expose].width = ev->width; + ps->expose_rects[ps->n_expose].height = ev->height; + ps->n_expose++; if (ev->count == 0) { - expose_root(dpy, expose_rects, n_expose); - n_expose = 0; + expose_root(ps, ps->expose_rects, ps->n_expose); + ps->n_expose = 0; } } } @@ -3177,46 +3009,47 @@ ev_expose(XExposeEvent *ev) { * returned could not be found. */ static void -update_ewmh_active_win(Display *dpy) { +update_ewmh_active_win(session_t *ps) { // Get the attribute firstly - winattr_t attr = wid_get_attr(dpy, root, ewmh_active_win_atom, + winprop_t prop = wid_get_prop(ps, ps->root, ps->atom_ewmh_active_win, 1L, XA_WINDOW, 32); - if (!attr.nitems) { - free_winattr(&attr); + if (!prop.nitems) { + free_winprop(&prop); return; } - + // Search for the window - Window wid = *((long *) attr.data); + Window wid = *((long *) prop.data); win *w = NULL; - free_winattr(&attr); + free_winprop(&prop); - if (!(w = find_toplevel(wid))) - if (!(w = find_win(wid))) - w = find_toplevel2(dpy, wid); + if (!(w = find_toplevel(ps, wid))) + if (!(w = find_win(ps, wid))) + w = find_toplevel2(ps, wid); // Mark the window focused if (w) { if (!w->focused) - set_focused(dpy, w, True); - if (active_win && w != active_win) - set_focused(dpy, active_win, False); - active_win = w; + set_focused(ps, w, true); + if (ps->active_win && w != ps->active_win) + set_focused(ps, ps->active_win, false); + ps->active_win = w; } } inline static void -ev_property_notify(XPropertyEvent *ev) { - if (root == ev->window) { - if (opts.track_focus && opts.use_ewmh_active_win - && ewmh_active_win_atom == ev->atom) { - update_ewmh_active_win(dpy); +ev_property_notify(session_t *ps, XPropertyEvent *ev) { + if (ps->root == ev->window) { + if (ps->o.track_focus && ps->o.use_ewmh_active_win + && ps->atom_ewmh_active_win == ev->atom) { + update_ewmh_active_win(ps); } else { // Destroy the root "image" if the wallpaper probably changed - for (int p = 0; background_props[p]; p++) { - if (ev->atom == XInternAtom(dpy, background_props[p], False)) { - root_damaged(); + for (int p = 0; background_props_str[p]; p++) { + if (ev->atom == + XInternAtom(ps->dpy, background_props_str[p], false)) { + root_damaged(ps); break; } } @@ -3227,62 +3060,62 @@ ev_property_notify(XPropertyEvent *ev) { } // If _NET_WM_OPACITY changes - if (ev->atom == opacity_atom) { + if (ev->atom == ps->atom_opacity) { win *w = NULL; - if ((w = find_win(ev->window))) - w->opacity_prop = wid_get_opacity_prop(dpy, w->id, OPAQUE); - else if (opts.detect_client_opacity - && (w = find_toplevel(ev->window))) - w->opacity_prop_client = wid_get_opacity_prop(dpy, w->client_win, + if ((w = find_win(ps, ev->window))) + w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); + else if (ps->o.detect_client_opacity + && (w = find_toplevel(ps, ev->window))) + w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, OPAQUE); if (w) { - calc_opacity(dpy, w, False); + calc_opacity(ps, w, false); } } // If frame extents property changes - if (opts.frame_opacity && ev->atom == frame_extents_atom) { - win *w = find_toplevel(ev->window); + if (ps->o.frame_opacity && ev->atom == ps->atom_frame_extents) { + win *w = find_toplevel(ps, ev->window); if (w) { - get_frame_extents(dpy, w, ev->window); + get_frame_extents(ps, w, ev->window); // If frame extents change, the window needs repaint - add_damage_win(dpy, w); + add_damage_win(ps, w); } } // If name changes - if (opts.track_wdata - && (name_atom == ev->atom || name_ewmh_atom == ev->atom)) { - win *w = find_toplevel(ev->window); - if (w && 1 == win_get_name(dpy, w)) - determine_shadow(dpy, w); + if (ps->o.track_wdata + && (ps->atom_name == ev->atom || ps->atom_name_ewmh == ev->atom)) { + win *w = find_toplevel(ps, ev->window); + if (w && 1 == win_get_name(ps, w)) + determine_shadow(ps, w); } // If class changes - if (opts.track_wdata && class_atom == ev->atom) { - win *w = find_toplevel(ev->window); + if (ps->o.track_wdata && ps->atom_class == ev->atom) { + win *w = find_toplevel(ps, ev->window); if (w) { - win_get_class(dpy, w); - determine_shadow(dpy, w); + win_get_class(ps, w); + determine_shadow(ps, w); } } // If _COMPTON_SHADOW changes - if (opts.respect_attr_shadow && compton_shadow_atom == ev->atom) { - win *w = find_win(ev->window); + if (ps->o.respect_prop_shadow && ps->atom_compton_shadow == ev->atom) { + win *w = find_win(ps, ev->window); if (w) - win_update_attr_shadow(dpy, w); + win_update_attr_shadow(ps, w); } } inline static void -ev_damage_notify(XDamageNotifyEvent *ev) { - damage_win(dpy, ev); +ev_damage_notify(session_t *ps, XDamageNotifyEvent *ev) { + damage_win(ps, ev); } inline static void -ev_shape_notify(XShapeEvent *ev) { - win *w = find_win(ev->window); +ev_shape_notify(session_t *ps, XShapeEvent *ev) { + win *w = find_win(ps, ev->window); if (!w || IsUnmapped == w->a.map_state) return; /* @@ -3293,31 +3126,31 @@ ev_shape_notify(XShapeEvent *ev) { */ if (w->border_size) { // Mark the old border_size as damaged - add_damage(dpy, w->border_size); + add_damage(ps, w->border_size); - w->border_size = border_size(dpy, w); + w->border_size = border_size(ps, w); // Mark the new border_size as damaged - add_damage(dpy, copy_region(dpy, w->border_size)); + add_damage(ps, copy_region(ps, w->border_size)); } // Redo bounding shape detection and rounded corner detection - win_update_shape(dpy, w); + win_update_shape(ps, w); - update_reg_ignore_expire(w); + update_reg_ignore_expire(ps, w); } /** * Handle ScreenChangeNotify events from X RandR extension. */ static void -ev_screen_change_notify(XRRScreenChangeNotifyEvent *ev) { - if (!opts.refresh_rate) { - update_refresh_rate(dpy); - if (!refresh_rate) { +ev_screen_change_notify(session_t *ps, XRRScreenChangeNotifyEvent *ev) { + if (!ps->o.refresh_rate) { + update_refresh_rate(ps); + if (!ps->refresh_rate) { fprintf(stderr, "ev_screen_change_notify(): Refresh rate detection " "failed, software VSync disabled."); - opts.vsync = VSYNC_NONE; + ps->o.vsync = VSYNC_NONE; } } } @@ -3327,26 +3160,26 @@ ev_screen_change_notify(XRRScreenChangeNotifyEvent *ev) { * Get a window's name from window ID. */ static bool -ev_window_name(Display *dpy, Window wid, char **name) { +ev_window_name(session_t *ps, Window wid, char **name) { bool to_free = false; *name = ""; if (wid) { *name = "(Failed to get title)"; - if (root == wid) + if (ps->root == wid) *name = "(Root window)"; - else if (overlay == wid) + else if (ps->overlay == wid) *name = "(Overlay)"; else { - win *w = find_win(wid); + win *w = find_win(ps, wid); if (!w) - w = find_toplevel(wid); + w = find_toplevel(ps, wid); if (w && w->name) *name = w->name; else if (!(w && w->client_win - && (to_free = wid_get_name(dpy, w->client_win, name)))) - to_free = wid_get_name(dpy, wid, name); + && (to_free = wid_get_name(ps, w->client_win, name)))) + to_free = wid_get_name(ps, wid, name); } } @@ -3355,22 +3188,22 @@ ev_window_name(Display *dpy, Window wid, char **name) { #endif static void -ev_handle(XEvent *ev) { +ev_handle(session_t *ps, XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) { - discard_ignore(dpy, ev->xany.serial); + discard_ignore(ps, ev->xany.serial); } #ifdef DEBUG_EVENTS - if (ev->type != damage_event + XDamageNotify) { - Window wid = ev_window(ev); + if (ev->type != ps->damage_event + XDamageNotify) { + Window wid = ev_window(ps, ev); char *window_name = NULL; - Bool to_free = false; + bool to_free = false; - to_free = ev_window_name(dpy, wid, &window_name); + to_free = ev_window_name(ps, wid, &window_name); - print_timestamp(); + print_timestamp(ps); printf("event %10.10s serial %#010x window %#010lx \"%s\"\n", - ev_name(ev), ev_serial(ev), wid, window_name); + ev_name(ps, ev), ev_serial(ev), wid, window_name); if (to_free) { XFree(window_name); @@ -3382,57 +3215,55 @@ ev_handle(XEvent *ev) { switch (ev->type) { case FocusIn: - ev_focus_in((XFocusChangeEvent *)ev); + ev_focus_in(ps, (XFocusChangeEvent *)ev); break; case FocusOut: - ev_focus_out((XFocusChangeEvent *)ev); + ev_focus_out(ps, (XFocusChangeEvent *)ev); break; case CreateNotify: - ev_create_notify((XCreateWindowEvent *)ev); + ev_create_notify(ps, (XCreateWindowEvent *)ev); break; case ConfigureNotify: - ev_configure_notify((XConfigureEvent *)ev); + ev_configure_notify(ps, (XConfigureEvent *)ev); break; case DestroyNotify: - ev_destroy_notify((XDestroyWindowEvent *)ev); + ev_destroy_notify(ps, (XDestroyWindowEvent *)ev); break; case MapNotify: - ev_map_notify((XMapEvent *)ev); + ev_map_notify(ps, (XMapEvent *)ev); break; case UnmapNotify: - ev_unmap_notify((XUnmapEvent *)ev); + ev_unmap_notify(ps, (XUnmapEvent *)ev); break; case ReparentNotify: - ev_reparent_notify((XReparentEvent *)ev); + ev_reparent_notify(ps, (XReparentEvent *)ev); break; case CirculateNotify: - ev_circulate_notify((XCirculateEvent *)ev); + ev_circulate_notify(ps, (XCirculateEvent *)ev); break; case Expose: - ev_expose((XExposeEvent *)ev); + ev_expose(ps, (XExposeEvent *)ev); break; case PropertyNotify: - ev_property_notify((XPropertyEvent *)ev); + ev_property_notify(ps, (XPropertyEvent *)ev); break; default: - if (shape_exists && ev->type == shape_event) { - ev_shape_notify((XShapeEvent *) ev); + if (ps->shape_exists && ev->type == ps->shape_event) { + ev_shape_notify(ps, (XShapeEvent *) ev); break; } - if (randr_exists && ev->type == (randr_event + RRScreenChangeNotify)) { - ev_screen_change_notify((XRRScreenChangeNotifyEvent *) ev); + if (ps->randr_exists && ev->type == (ps->randr_event + RRScreenChangeNotify)) { + ev_screen_change_notify(ps, (XRRScreenChangeNotifyEvent *) ev); break; } - if (ev->type == damage_event + XDamageNotify) { - ev_damage_notify((XDamageNotifyEvent *)ev); + if (ev->type == ps->damage_event + XDamageNotify) { + ev_damage_notify(ps, (XDamageNotifyEvent *)ev); } break; } } -/** - * Main - */ +// === Main === /** * Print usage text and exit. @@ -3541,7 +3372,7 @@ usage(void) { "--use-ewmh-active-win\n" " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n" " window is focused instead of using FocusIn/Out events.\n" - "--respect-attr-shadow\n" + "--respect-prop-shadow\n" " Respect _COMPTON_SHADOW. This a prototype-level feature, which\n" " you must not rely on.\n" "--unredir-if-possible\n" @@ -3573,7 +3404,7 @@ usage(void) { * Register a window as symbol, and initialize GLX context if wanted. */ static void -register_cm(Bool want_glxct) { +register_cm(session_t *ps, bool want_glxct) { Atom a; char *buf; int len, s; @@ -3582,10 +3413,10 @@ register_cm(Bool want_glxct) { // Create a window with the wanted GLX visual if (want_glxct) { XVisualInfo *pvi = NULL; - Bool ret = False; + bool ret = false; // Get visual for the window int attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; - pvi = glXChooseVisual(dpy, scr, attribs); + pvi = glXChooseVisual(ps->dpy, ps->scr, attribs); if (!pvi) { fprintf(stderr, "register_cm(): Failed to choose visual required " @@ -3594,28 +3425,28 @@ register_cm(Bool want_glxct) { else { // Create the window XSetWindowAttributes swa = { - .colormap = XCreateColormap(dpy, root, pvi->visual, AllocNone), + .colormap = XCreateColormap(ps->dpy, ps->root, pvi->visual, AllocNone), .border_pixel = 0, }; - pvi->screen = scr; - reg_win = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, pvi->depth, + pvi->screen = ps->scr; + ps->reg_win = XCreateWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, pvi->depth, InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa); - if (!reg_win) + if (!ps->reg_win) fprintf(stderr, "register_cm(): Failed to create window required " "by fake OpenGL VSync. OpenGL VSync turned off.\n"); else { // Get GLX context - glx_context = glXCreateContext(dpy, pvi, None, GL_TRUE); - if (!glx_context) { + ps->glx_context = glXCreateContext(ps->dpy, pvi, None, GL_TRUE); + if (!ps->glx_context) { fprintf(stderr, "register_cm(): Failed to get GLX context. " "OpenGL VSync turned off.\n"); - opts.vsync = VSYNC_NONE; + ps->o.vsync = VSYNC_NONE; } else { // Attach GLX context - if (!(ret = glXMakeCurrent(dpy, reg_win, glx_context))) + if (!(ret = glXMakeCurrent(ps->dpy, ps->reg_win, ps->glx_context))) fprintf(stderr, "register_cm(): Failed to attach GLX context." " OpenGL VSync turned off.\n"); } @@ -3625,20 +3456,19 @@ register_cm(Bool want_glxct) { XFree(pvi); if (!ret) - opts.vsync = VSYNC_NONE; + ps->o.vsync = VSYNC_NONE; } #endif - - if (!reg_win) - reg_win = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, + + if (!ps->reg_win) + ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, None, None); - Xutf8SetWMProperties( - dpy, reg_win, "xcompmgr", "xcompmgr", + Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL, NULL); len = strlen(REGISTER_PROP) + 2; - s = scr; + s = ps->scr; while (s >= 10) { ++len; @@ -3646,14 +3476,17 @@ register_cm(Bool want_glxct) { } buf = malloc(len); - snprintf(buf, len, REGISTER_PROP"%d", scr); + snprintf(buf, len, REGISTER_PROP"%d", ps->scr); - a = XInternAtom(dpy, buf, False); + a = XInternAtom(ps->dpy, buf, false); free(buf); - XSetSelectionOwner(dpy, a, reg_win, 0); + XSetSelectionOwner(ps->dpy, a, ps->reg_win, 0); } +/** + * Fork program to background and disable all I/O streams. + */ static void fork_after(void) { if (getppid() == 1) return; @@ -3769,7 +3602,7 @@ open_config_file(char *cpath, char **ppath) { * Parse a VSync option argument. */ static inline void -parse_vsync(const char *optarg) { +parse_vsync(session_t *ps, const char *optarg) { const static char * const vsync_str[] = { "none", // VSYNC_NONE "drm", // VSYNC_DRM @@ -3780,7 +3613,7 @@ parse_vsync(const char *optarg) { for (i = 0; i < (sizeof(vsync_str) / sizeof(vsync_str[0])); ++i) if (!strcasecmp(optarg, vsync_str[i])) { - opts.vsync = i; + ps->o.vsync = i; break; } if ((sizeof(vsync_str) / sizeof(vsync_str[0])) == i) { @@ -3792,7 +3625,7 @@ parse_vsync(const char *optarg) { * Parse a configuration file from default location. */ static void -parse_config(char *cpath, struct options_tmp *pcfgtmp) { +parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { char *path = NULL; FILE *f; config_t cfg; @@ -3830,31 +3663,31 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { // -D (fade_delta) if (lcfg_lookup_int(&cfg, "fade-delta", &ival)) - opts.fade_delta = ival; + ps->o.fade_delta = ival; // -I (fade_in_step) if (config_lookup_float(&cfg, "fade-in-step", &dval)) - opts.fade_in_step = normalize_d(dval) * OPAQUE; + ps->o.fade_in_step = normalize_d(dval) * OPAQUE; // -O (fade_out_step) if (config_lookup_float(&cfg, "fade-out-step", &dval)) - opts.fade_out_step = normalize_d(dval) * OPAQUE; + ps->o.fade_out_step = normalize_d(dval) * OPAQUE; // -r (shadow_radius) - lcfg_lookup_int(&cfg, "shadow-radius", &opts.shadow_radius); + lcfg_lookup_int(&cfg, "shadow-radius", &ps->o.shadow_radius); // -o (shadow_opacity) - config_lookup_float(&cfg, "shadow-opacity", &opts.shadow_opacity); + config_lookup_float(&cfg, "shadow-opacity", &ps->o.shadow_opacity); // -l (shadow_offset_x) - lcfg_lookup_int(&cfg, "shadow-offset-x", &opts.shadow_offset_x); + lcfg_lookup_int(&cfg, "shadow-offset-x", &ps->o.shadow_offset_x); // -t (shadow_offset_y) - lcfg_lookup_int(&cfg, "shadow-offset-y", &opts.shadow_offset_y); + lcfg_lookup_int(&cfg, "shadow-offset-y", &ps->o.shadow_offset_y); // -i (inactive_opacity) if (config_lookup_float(&cfg, "inactive-opacity", &dval)) - opts.inactive_opacity = normalize_d(dval) * OPAQUE; + ps->o.inactive_opacity = normalize_d(dval) * OPAQUE; // -e (frame_opacity) - config_lookup_float(&cfg, "frame-opacity", &opts.frame_opacity); + config_lookup_float(&cfg, "frame-opacity", &ps->o.frame_opacity); // -z (clear_shadow) - lcfg_lookup_bool(&cfg, "clear-shadow", &opts.clear_shadow); + lcfg_lookup_bool(&cfg, "clear-shadow", &ps->o.clear_shadow); // -c (shadow_enable) if (config_lookup_bool(&cfg, "shadow", &ival) && ival) - wintype_arr_enable(opts.wintype_shadow); + wintype_arr_enable(ps->o.wintype_shadow); // -C (no_dock_shadow) lcfg_lookup_bool(&cfg, "no-dock-shadow", &pcfgtmp->no_dock_shadow); // -G (no_dnd_shadow) @@ -3863,53 +3696,53 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { config_lookup_float(&cfg, "menu-opacity", &pcfgtmp->menu_opacity); // -f (fading_enable) if (config_lookup_bool(&cfg, "fading", &ival) && ival) - wintype_arr_enable(opts.wintype_fade); + wintype_arr_enable(ps->o.wintype_fade); // --no-fading-open-close - lcfg_lookup_bool(&cfg, "no-fading-openclose", &opts.no_fading_openclose); + lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose); // --shadow-red - config_lookup_float(&cfg, "shadow-red", &opts.shadow_red); + config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red); // --shadow-green - config_lookup_float(&cfg, "shadow-green", &opts.shadow_green); + config_lookup_float(&cfg, "shadow-green", &ps->o.shadow_green); // --shadow-blue - config_lookup_float(&cfg, "shadow-blue", &opts.shadow_blue); + config_lookup_float(&cfg, "shadow-blue", &ps->o.shadow_blue); // --inactive-opacity-override lcfg_lookup_bool(&cfg, "inactive-opacity-override", - &opts.inactive_opacity_override); + &ps->o.inactive_opacity_override); // --inactive-dim - config_lookup_float(&cfg, "inactive-dim", &opts.inactive_dim); + config_lookup_float(&cfg, "inactive-dim", &ps->o.inactive_dim); // --mark-wmwin-focused - lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opts.mark_wmwin_focused); + lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &ps->o.mark_wmwin_focused); // --mark-ovredir-focused lcfg_lookup_bool(&cfg, "mark-ovredir-focused", - &opts.mark_ovredir_focused); + &ps->o.mark_ovredir_focused); // --shadow-ignore-shaped lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", - &opts.shadow_ignore_shaped); + &ps->o.shadow_ignore_shaped); // --detect-rounded-corners lcfg_lookup_bool(&cfg, "detect-rounded-corners", - &opts.detect_rounded_corners); + &ps->o.detect_rounded_corners); // --detect-client-opacity lcfg_lookup_bool(&cfg, "detect-client-opacity", - &opts.detect_client_opacity); + &ps->o.detect_client_opacity); // --refresh-rate - lcfg_lookup_int(&cfg, "refresh-rate", &opts.refresh_rate); + lcfg_lookup_int(&cfg, "refresh-rate", &ps->o.refresh_rate); // --vsync if (config_lookup_string(&cfg, "vsync", &sval)) - parse_vsync(sval); + parse_vsync(ps, sval); // --alpha-step - config_lookup_float(&cfg, "alpha-step", &opts.alpha_step); + config_lookup_float(&cfg, "alpha-step", &ps->o.alpha_step); // --dbe - lcfg_lookup_bool(&cfg, "dbe", &opts.dbe); + lcfg_lookup_bool(&cfg, "dbe", &ps->o.dbe); // --paint-on-overlay - lcfg_lookup_bool(&cfg, "paint-on-overlay", &opts.paint_on_overlay); + lcfg_lookup_bool(&cfg, "paint-on-overlay", &ps->o.paint_on_overlay); // --sw-opti - lcfg_lookup_bool(&cfg, "sw-opti", &opts.sw_opti); + lcfg_lookup_bool(&cfg, "sw-opti", &ps->o.sw_opti); // --use-ewmh-active-win lcfg_lookup_bool(&cfg, "use-ewmh-active-win", - &opts.use_ewmh_active_win); + &ps->o.use_ewmh_active_win); // --unredir-if-possible lcfg_lookup_bool(&cfg, "unredir-if-possible", - &opts.unredir_if_possible); + &ps->o.unredir_if_possible); // --shadow-exclude { config_setting_t *setting = @@ -3919,20 +3752,20 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) { - condlst_add(&opts.shadow_blacklist, + condlst_add(&ps->o.shadow_blacklist, config_setting_get_string_elem(setting, i)); } } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { - condlst_add(&opts.shadow_blacklist, + condlst_add(&ps->o.shadow_blacklist, config_setting_get_string(setting)); } } } // Wintype settings { - wintype i; + wintype_t i; for (i = 0; i < NUM_WINTYPES; ++i) { char *str = mstrjoin("wintypes.", WINTYPES[i]); @@ -3940,11 +3773,11 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { free(str); if (setting) { if (config_setting_lookup_bool(setting, "shadow", &ival)) - opts.wintype_shadow[i] = (Bool) ival; + ps->o.wintype_shadow[i] = (bool) ival; if (config_setting_lookup_bool(setting, "fade", &ival)) - opts.wintype_fade[i] = (Bool) ival; + ps->o.wintype_fade[i] = (bool) ival; config_setting_lookup_float(setting, "opacity", - &opts.wintype_opacity[i]); + &ps->o.wintype_opacity[i]); } } } @@ -3957,7 +3790,7 @@ parse_config(char *cpath, struct options_tmp *pcfgtmp) { * Process arguments and configuration files. */ static void -get_cfg(int argc, char *const *argv) { +get_cfg(session_t *ps, int argc, char *const *argv) { const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb"; const static struct option longopts[] = { { "config", required_argument, NULL, 256 }, @@ -3981,30 +3814,33 @@ get_cfg(int argc, char *const *argv) { { "sw-opti", no_argument, NULL, 274 }, { "vsync-aggressive", no_argument, NULL, 275 }, { "use-ewmh-active-win", no_argument, NULL, 276 }, - { "respect-attr-shadow", no_argument, NULL, 277 }, + { "respect-prop-shadow", no_argument, NULL, 277 }, { "unredir-if-possible", no_argument, NULL, 278 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; struct options_tmp cfgtmp = { - .no_dock_shadow = False, - .no_dnd_shadow = False, + .no_dock_shadow = false, + .no_dnd_shadow = false, .menu_opacity = 1.0, }; - Bool shadow_enable = False, fading_enable = False; + bool shadow_enable = false, fading_enable = false; int o, longopt_idx, i; char *config_file = NULL; char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); for (i = 0; i < NUM_WINTYPES; ++i) { - opts.wintype_fade[i] = False; - opts.wintype_shadow[i] = False; - opts.wintype_opacity[i] = 1.0; + ps->o.wintype_fade[i] = false; + ps->o.wintype_shadow[i] = false; + ps->o.wintype_opacity[i] = 1.0; } // Pre-parse the commandline arguments to check for --config and invalid // switches + // Must reset optind to 0 here in case we reread the commandline + // arguments + optind = 1; while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { if (256 == o) @@ -4014,7 +3850,7 @@ get_cfg(int argc, char *const *argv) { } #ifdef CONFIG_LIBCONFIG - parse_config(config_file, &cfgtmp); + parse_config(ps, config_file, &cfgtmp); #endif // Parse commandline arguments. Range checking will be done later. @@ -4029,56 +3865,56 @@ get_cfg(int argc, char *const *argv) { switch (o) { // Short options case 'd': - opts.display = optarg; + ps->o.display = mstrcpy(optarg); break; case 'D': - opts.fade_delta = atoi(optarg); + ps->o.fade_delta = atoi(optarg); break; case 'I': - opts.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; + ps->o.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'O': - opts.fade_out_step = normalize_d(atof(optarg)) * OPAQUE; + ps->o.fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': - shadow_enable = True; + shadow_enable = true; break; case 'C': - cfgtmp.no_dock_shadow = True; + cfgtmp.no_dock_shadow = true; break; case 'G': - cfgtmp.no_dnd_shadow = True; + cfgtmp.no_dnd_shadow = true; break; case 'm': cfgtmp.menu_opacity = atof(optarg); break; case 'f': case 'F': - fading_enable = True; + fading_enable = true; break; case 'S': - opts.synchronize = True; + ps->o.synchronize = true; break; case 'r': - opts.shadow_radius = atoi(optarg); + ps->o.shadow_radius = atoi(optarg); break; case 'o': - opts.shadow_opacity = atof(optarg); + ps->o.shadow_opacity = atof(optarg); break; case 'l': - opts.shadow_offset_x = atoi(optarg); + ps->o.shadow_offset_x = atoi(optarg); break; case 't': - opts.shadow_offset_y = atoi(optarg); + ps->o.shadow_offset_y = atoi(optarg); break; case 'i': - opts.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); + ps->o.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; case 'e': - opts.frame_opacity = atof(optarg); + ps->o.frame_opacity = atof(optarg); break; case 'z': - opts.clear_shadow = True; + ps->o.clear_shadow = true; break; case 'n': case 'a': @@ -4087,7 +3923,7 @@ get_cfg(int argc, char *const *argv) { "-n, -a, and -s have been removed.\n"); break; case 'b': - opts.fork_after_register = True; + ps->o.fork_after_register = true; break; // Long options case 256: @@ -4095,95 +3931,94 @@ get_cfg(int argc, char *const *argv) { break; case 257: // --shadow-red - opts.shadow_red = atof(optarg); + ps->o.shadow_red = atof(optarg); break; case 258: // --shadow-green - opts.shadow_green = atof(optarg); + ps->o.shadow_green = atof(optarg); break; case 259: // --shadow-blue - opts.shadow_blue = atof(optarg); + ps->o.shadow_blue = atof(optarg); break; case 260: // --inactive-opacity-override - opts.inactive_opacity_override = True; + ps->o.inactive_opacity_override = true; break; case 261: // --inactive-dim - opts.inactive_dim = atof(optarg); + ps->o.inactive_dim = atof(optarg); break; case 262: // --mark-wmwin-focused - opts.mark_wmwin_focused = True; + ps->o.mark_wmwin_focused = true; break; case 263: // --shadow-exclude - condlst_add(&opts.shadow_blacklist, optarg); + condlst_add(&ps->o.shadow_blacklist, optarg); break; case 264: // --mark-ovredir-focused - opts.mark_ovredir_focused = True; + ps->o.mark_ovredir_focused = true; break; case 265: // --no-fading-openclose - opts.no_fading_openclose = True; + ps->o.no_fading_openclose = true; break; case 266: // --shadow-ignore-shaped - opts.shadow_ignore_shaped = True; + ps->o.shadow_ignore_shaped = true; break; case 267: // --detect-rounded-corners - opts.detect_rounded_corners = True; + ps->o.detect_rounded_corners = true; break; case 268: // --detect-client-opacity - opts.detect_client_opacity = True; + ps->o.detect_client_opacity = true; break; case 269: // --refresh-rate - opts.refresh_rate = atoi(optarg); + ps->o.refresh_rate = atoi(optarg); break; case 270: // --vsync - parse_vsync(optarg); + parse_vsync(ps, optarg); break; case 271: // --alpha-step - opts.alpha_step = atof(optarg); + ps->o.alpha_step = atof(optarg); break; case 272: // --dbe - opts.dbe = True; + ps->o.dbe = true; break; case 273: // --paint-on-overlay - opts.paint_on_overlay = True; + ps->o.paint_on_overlay = true; break; case 274: // --sw-opti - opts.sw_opti = True; + ps->o.sw_opti = true; break; case 275: // --vsync-aggressive - opts.vsync_aggressive = True; + ps->o.vsync_aggressive = true; break; case 276: // --use-ewmh-active-win - opts.use_ewmh_active_win = True; + ps->o.use_ewmh_active_win = true; break; case 277: - // --respect-attr-shadow - opts.respect_attr_shadow = True; + // --respect-prop-shadow + ps->o.respect_prop_shadow = true; break; case 278: // --unredir-if-possible - opts.unredir_if_possible = True; + ps->o.unredir_if_possible = true; break; default: usage(); - break; } } @@ -4192,88 +4027,91 @@ get_cfg(int argc, char *const *argv) { free(lc_numeric_old); // Range checking and option assignments - opts.fade_delta = max_i(opts.fade_delta, 1); - opts.shadow_radius = max_i(opts.shadow_radius, 1); - opts.shadow_red = normalize_d(opts.shadow_red); - opts.shadow_green = normalize_d(opts.shadow_green); - opts.shadow_blue = normalize_d(opts.shadow_blue); - opts.inactive_dim = normalize_d(opts.inactive_dim); - opts.frame_opacity = normalize_d(opts.frame_opacity); - opts.shadow_opacity = normalize_d(opts.shadow_opacity); + ps->o.fade_delta = max_i(ps->o.fade_delta, 1); + ps->o.shadow_radius = max_i(ps->o.shadow_radius, 1); + ps->o.shadow_red = normalize_d(ps->o.shadow_red); + ps->o.shadow_green = normalize_d(ps->o.shadow_green); + ps->o.shadow_blue = normalize_d(ps->o.shadow_blue); + ps->o.inactive_dim = normalize_d(ps->o.inactive_dim); + ps->o.frame_opacity = normalize_d(ps->o.frame_opacity); + ps->o.shadow_opacity = normalize_d(ps->o.shadow_opacity); cfgtmp.menu_opacity = normalize_d(cfgtmp.menu_opacity); - opts.refresh_rate = normalize_i_range(opts.refresh_rate, 0, 300); - opts.alpha_step = normalize_d_range(opts.alpha_step, 0.01, 1.0); - if (OPAQUE == opts.inactive_opacity) { - opts.inactive_opacity = 0; + ps->o.refresh_rate = normalize_i_range(ps->o.refresh_rate, 0, 300); + ps->o.alpha_step = normalize_d_range(ps->o.alpha_step, 0.01, 1.0); + if (OPAQUE == ps->o.inactive_opacity) { + ps->o.inactive_opacity = 0; } if (shadow_enable) - wintype_arr_enable(opts.wintype_shadow); - opts.wintype_shadow[WINTYPE_DESKTOP] = False; + wintype_arr_enable(ps->o.wintype_shadow); + ps->o.wintype_shadow[WINTYPE_DESKTOP] = false; if (cfgtmp.no_dock_shadow) - opts.wintype_shadow[WINTYPE_DOCK] = False; + ps->o.wintype_shadow[WINTYPE_DOCK] = false; if (cfgtmp.no_dnd_shadow) - opts.wintype_shadow[WINTYPE_DND] = False; + ps->o.wintype_shadow[WINTYPE_DND] = false; if (fading_enable) - wintype_arr_enable(opts.wintype_fade); + wintype_arr_enable(ps->o.wintype_fade); if (1.0 != cfgtmp.menu_opacity) { - opts.wintype_opacity[WINTYPE_DROPDOWN_MENU] = cfgtmp.menu_opacity; - opts.wintype_opacity[WINTYPE_POPUP_MENU] = cfgtmp.menu_opacity; + ps->o.wintype_opacity[WINTYPE_DROPDOWN_MENU] = cfgtmp.menu_opacity; + ps->o.wintype_opacity[WINTYPE_POPUP_MENU] = cfgtmp.menu_opacity; } // Other variables determined by options // Determine whether we need to track focus changes - if (opts.inactive_opacity || opts.inactive_dim) { - opts.track_focus = True; + if (ps->o.inactive_opacity || ps->o.inactive_dim) { + ps->o.track_focus = true; } // Determine whether we need to track window name and class - if (opts.shadow_blacklist || opts.fade_blacklist) - opts.track_wdata = True; + if (ps->o.shadow_blacklist || ps->o.fade_blacklist) + ps->o.track_wdata = true; } +/** + * Fetch all required atoms and save them to a session. + */ static void -get_atoms(void) { - opacity_atom = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False); - frame_extents_atom = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); - client_atom = XInternAtom(dpy, "WM_STATE", False); - name_atom = XA_WM_NAME; - name_ewmh_atom = XInternAtom(dpy, "_NET_WM_NAME", False); - class_atom = XA_WM_CLASS; - transient_atom = XA_WM_TRANSIENT_FOR; - ewmh_active_win_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); - compton_shadow_atom = XInternAtom(dpy, "_COMPTON_SHADOW", False); - - win_type_atom = XInternAtom(dpy, +init_atoms(session_t *ps) { + ps->atom_opacity = XInternAtom(ps->dpy, "_NET_WM_WINDOW_OPACITY", False); + ps->atom_frame_extents = XInternAtom(ps->dpy, "_NET_FRAME_EXTENTS", False); + ps->atom_client = XInternAtom(ps->dpy, "WM_STATE", False); + ps->atom_name = XA_WM_NAME; + ps->atom_name_ewmh = XInternAtom(ps->dpy, "_NET_WM_NAME", False); + ps->atom_class = XA_WM_CLASS; + ps->atom_transient = XA_WM_TRANSIENT_FOR; + ps->atom_ewmh_active_win = XInternAtom(ps->dpy, "_NET_ACTIVE_WINDOW", False); + ps->atom_compton_shadow = XInternAtom(ps->dpy, "_COMPTON_SHADOW", False); + + ps->atom_win_type = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE", False); - win_type[WINTYPE_UNKNOWN] = 0; - win_type[WINTYPE_DESKTOP] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_UNKNOWN] = 0; + ps->atoms_wintypes[WINTYPE_DESKTOP] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); - win_type[WINTYPE_DOCK] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_DOCK] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); - win_type[WINTYPE_TOOLBAR] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_TOOLBAR] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); - win_type[WINTYPE_MENU] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_MENU] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_MENU", False); - win_type[WINTYPE_UTILITY] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_UTILITY] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False); - win_type[WINTYPE_SPLASH] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_SPLASH] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); - win_type[WINTYPE_DIALOG] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_DIALOG] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); - win_type[WINTYPE_NORMAL] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_NORMAL] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); - win_type[WINTYPE_DROPDOWN_MENU] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_DROPDOWN_MENU] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False); - win_type[WINTYPE_POPUP_MENU] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_POPUP_MENU] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False); - win_type[WINTYPE_TOOLTIP] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_TOOLTIP] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_TOOLTIP", False); - win_type[WINTYPE_NOTIFY] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_NOTIFY] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); - win_type[WINTYPE_COMBO] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_COMBO] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_COMBO", False); - win_type[WINTYPE_DND] = XInternAtom(dpy, + ps->atoms_wintypes[WINTYPE_DND] = XInternAtom(ps->dpy, "_NET_WM_WINDOW_TYPE_DND", False); } @@ -4281,49 +4119,49 @@ get_atoms(void) { * Update refresh rate info with X Randr extension. */ static void -update_refresh_rate(Display *dpy) { +update_refresh_rate(session_t *ps) { XRRScreenConfiguration* randr_info; - if (!(randr_info = XRRGetScreenInfo(dpy, root))) + if (!(randr_info = XRRGetScreenInfo(ps->dpy, ps->root))) return; - refresh_rate = XRRConfigCurrentRate(randr_info); + ps->refresh_rate = XRRConfigCurrentRate(randr_info); XRRFreeScreenConfigInfo(randr_info); - if (refresh_rate) - refresh_intv = NS_PER_SEC / refresh_rate; + if (ps->refresh_rate) + ps->refresh_intv = NS_PER_SEC / ps->refresh_rate; else - refresh_intv = 0; + ps->refresh_intv = 0; } /** * Initialize refresh-rated based software optimization. * - * @return True for success, False otherwise + * @return true for success, false otherwise */ -static Bool -sw_opti_init(void) { +static bool +sw_opti_init(session_t *ps) { // Prepare refresh rate // Check if user provides one - refresh_rate = opts.refresh_rate; - if (refresh_rate) - refresh_intv = NS_PER_SEC / refresh_rate; + ps->refresh_rate = ps->o.refresh_rate; + if (ps->refresh_rate) + ps->refresh_intv = NS_PER_SEC / ps->refresh_rate; // Auto-detect refresh rate otherwise - if (!refresh_rate && randr_exists) { - update_refresh_rate(dpy); + if (!ps->refresh_rate && ps->randr_exists) { + update_refresh_rate(ps); } // Turn off vsync_sw if we can't get the refresh rate - if (!refresh_rate) - return False; + if (!ps->refresh_rate) + return false; // Monitor screen changes only if vsync_sw is enabled and we are using // an auto-detected refresh rate - if (randr_exists && !opts.refresh_rate) - XRRSelectInput(dpy, root, RRScreenChangeNotify); + if (ps->randr_exists && !ps->o.refresh_rate) + XRRSelectInput(ps->dpy, ps->root, RRScreenChangeNotify); - return True; + return true; } /** @@ -4353,18 +4191,23 @@ lceil_ntimes(long dividend, long divisor) { * problems */ static int -evpoll(struct pollfd *fd, int timeout) { +evpoll(session_t *ps, int timeout) { + struct pollfd ufd = { + .fd = ConnectionNumber(ps->dpy), + .events = POLLIN + }; + // Always wait infinitely if asked so, to minimize CPU usage if (timeout < 0) { - int ret = poll(fd, 1, timeout); - // Reset fade_time so the fading steps during idling are not counted - fade_time = get_time_ms(); + int ret = poll(&ufd, 1, timeout); + // Reset ps->fade_time so the fading steps during idling are not counted + ps->fade_time = get_time_ms(); return ret; } // Just do a poll() if we are not using optimization - if (!opts.sw_opti) - return poll(fd, 1, timeout); + if (!ps->o.sw_opti) + return poll(&ufd, 1, timeout); // Convert the old timeout to struct timespec struct timespec next_paint_tmout = { @@ -4374,7 +4217,7 @@ evpoll(struct pollfd *fd, int timeout) { // Get the nanosecond offset of the time when the we reach the timeout // I don't think a 32-bit long could overflow here. - long target_relative_offset = (next_paint_tmout.tv_nsec + get_time_timespec().tv_nsec - paint_tm_offset) % NS_PER_SEC; + long target_relative_offset = (next_paint_tmout.tv_nsec + get_time_timespec().tv_nsec - ps->paint_tm_offset) % NS_PER_SEC; if (target_relative_offset < 0) target_relative_offset += NS_PER_SEC; @@ -4383,39 +4226,39 @@ evpoll(struct pollfd *fd, int timeout) { // If the target time is sufficiently close to a refresh time, don't add // an offset, to avoid certain blocking conditions. if ((target_relative_offset % NS_PER_SEC) < SW_OPTI_TOLERANCE) - return poll(fd, 1, timeout); + return poll(&ufd, 1, timeout); // Add an offset so we wait until the next refresh after timeout - next_paint_tmout.tv_nsec += lceil_ntimes(target_relative_offset, refresh_intv) - target_relative_offset; + next_paint_tmout.tv_nsec += lceil_ntimes(target_relative_offset, ps->refresh_intv) - target_relative_offset; if (next_paint_tmout.tv_nsec > NS_PER_SEC) { next_paint_tmout.tv_nsec -= NS_PER_SEC; ++next_paint_tmout.tv_sec; } - return ppoll(fd, 1, &next_paint_tmout, NULL); + return ppoll(&ufd, 1, &next_paint_tmout, NULL); } /** * Initialize DRM VSync. * - * @return True for success, False otherwise + * @return true for success, false otherwise */ -static Bool -vsync_drm_init(void) { +static bool +vsync_drm_init(session_t *ps) { #ifdef CONFIG_VSYNC_DRM // Should we always open card0? - if ((drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { + if ((ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { fprintf(stderr, "vsync_drm_init(): Failed to open device.\n"); - return False; + return false; } - if (vsync_drm_wait()) - return False; + if (vsync_drm_wait(ps)) + return false; - return True; + return true; #else fprintf(stderr, "Program not compiled with DRM VSync support.\n"); - return False; + return false; #endif } @@ -4426,7 +4269,7 @@ vsync_drm_init(void) { * Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp */ static int -vsync_drm_wait(void) { +vsync_drm_wait(session_t *ps) { int ret = -1; drm_wait_vblank_t vbl; @@ -4434,7 +4277,7 @@ vsync_drm_wait(void) { vbl.request.sequence = 1; do { - ret = ioctl(drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl); + ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl); vbl.request.type &= ~_DRM_VBLANK_RELATIVE; } while (ret && errno == EINTR); @@ -4443,7 +4286,7 @@ vsync_drm_wait(void) { "unimplemented in this drmver?\n"); return ret; - + } #endif @@ -4453,26 +4296,26 @@ vsync_drm_wait(void) { * Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e * Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html * - * @return True for success, False otherwise + * @return true for success, false otherwise */ -static Bool -vsync_opengl_init(void) { +static bool +vsync_opengl_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL // Get video sync functions - glx_get_video_sync = (f_GetVideoSync) + ps->glx_get_video_sync = (f_GetVideoSync) glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI"); - glx_wait_video_sync = (f_WaitVideoSync) + ps->glx_wait_video_sync = (f_WaitVideoSync) glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI"); - if (!glx_wait_video_sync || !glx_get_video_sync) { + if (!ps->glx_wait_video_sync || !ps->glx_get_video_sync) { fprintf(stderr, "vsync_opengl_init(): " "Failed to get glXWait/GetVideoSyncSGI function.\n"); - return False; + return false; } - - return True; + + return true; #else fprintf(stderr, "Program not compiled with OpenGL VSync support.\n"); - return False; + return false; #endif } @@ -4481,11 +4324,11 @@ vsync_opengl_init(void) { * Wait for next VSync, OpenGL method. */ static void -vsync_opengl_wait(void) { +vsync_opengl_wait(session_t *ps) { unsigned vblank_count; - glx_get_video_sync(&vblank_count); - glx_wait_video_sync(2, (vblank_count + 1) % 2, &vblank_count); + ps->glx_get_video_sync(&vblank_count); + ps->glx_wait_video_sync(2, (vblank_count + 1) % 2, &vblank_count); // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required? } #endif @@ -4494,20 +4337,20 @@ vsync_opengl_wait(void) { * Wait for next VSync. */ static void -vsync_wait(void) { - if (VSYNC_NONE == opts.vsync) +vsync_wait(session_t *ps) { + if (VSYNC_NONE == ps->o.vsync) return; #ifdef CONFIG_VSYNC_DRM - if (VSYNC_DRM == opts.vsync) { - vsync_drm_wait(); + if (VSYNC_DRM == ps->o.vsync) { + vsync_drm_wait(ps); return; } #endif #ifdef CONFIG_VSYNC_OPENGL - if (VSYNC_OPENGL == opts.vsync) { - vsync_opengl_wait(); + if (VSYNC_OPENGL == ps->o.vsync) { + vsync_opengl_wait(ps); return; } #endif @@ -4522,18 +4365,18 @@ vsync_wait(void) { * Pregenerate alpha pictures. */ static void -init_alpha_picts(Display *dpy) { +init_alpha_picts(session_t *ps) { int i; - int num = lround(1.0 / opts.alpha_step) + 1; + int num = lround(1.0 / ps->o.alpha_step) + 1; - alpha_picts = malloc(sizeof(Picture) * num); + ps->alpha_picts = malloc(sizeof(Picture) * num); for (i = 0; i < num; ++i) { - double o = i * opts.alpha_step; - if ((1.0 - o) > opts.alpha_step) - alpha_picts[i] = solid_picture(dpy, False, o, 0, 0, 0); + double o = i * ps->o.alpha_step; + if ((1.0 - o) > ps->o.alpha_step) + ps->alpha_picts[i] = solid_picture(ps, false, o, 0, 0, 0); else - alpha_picts[i] = None; + ps->alpha_picts[i] = None; } } @@ -4541,12 +4384,12 @@ init_alpha_picts(Display *dpy) { * Initialize double buffer. */ static void -init_dbe(void) { - if (!(root_dbe = XdbeAllocateBackBufferName(dpy, - (opts.paint_on_overlay ? overlay: root), XdbeCopied))) { +init_dbe(session_t *ps) { + if (!(ps->root_dbe = XdbeAllocateBackBufferName(ps->dpy, + (ps->o.paint_on_overlay ? ps->overlay: ps->root), XdbeCopied))) { fprintf(stderr, "Failed to create double buffer. Double buffering " "turned off.\n"); - opts.dbe = False; + ps->o.dbe = false; return; } } @@ -4555,27 +4398,27 @@ init_dbe(void) { * Initialize X composite overlay window. */ static void -init_overlay(void) { - overlay = XCompositeGetOverlayWindow(dpy, root); - if (overlay) { +init_overlay(session_t *ps) { + ps->overlay = XCompositeGetOverlayWindow(ps->dpy, ps->root); + if (ps->overlay) { // Set window region of the overlay window, code stolen from // compiz-0.8.8 - XserverRegion region = XFixesCreateRegion (dpy, NULL, 0); - XFixesSetWindowShapeRegion(dpy, overlay, ShapeBounding, 0, 0, 0); - XFixesSetWindowShapeRegion(dpy, overlay, ShapeInput, 0, 0, region); - XFixesDestroyRegion (dpy, region); + XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); + XFixesSetWindowShapeRegion(ps->dpy, ps->overlay, ShapeBounding, 0, 0, 0); + XFixesSetWindowShapeRegion(ps->dpy, ps->overlay, ShapeInput, 0, 0, region); + XFixesDestroyRegion(ps->dpy, region); // Listen to Expose events on the overlay - XSelectInput(dpy, overlay, ExposureMask); + XSelectInput(ps->dpy, ps->overlay, ExposureMask); // Retrieve DamageNotify on root window if we are painting on an // overlay - // root_damage = XDamageCreate(dpy, root, XDamageReportNonEmpty); + // root_damage = XDamageCreate(ps->dpy, root, XDamageReportNonEmpty); } else { fprintf(stderr, "Cannot get X Composite overlay window. Falling " "back to painting on root window.\n"); - opts.paint_on_overlay = False; + ps->o.paint_on_overlay = false; } } @@ -4583,23 +4426,23 @@ init_overlay(void) { * Redirect all windows. */ static void -redir_start(Display *dpy) { - if (!redirected) { +redir_start(session_t *ps) { + if (!ps->redirected) { #ifdef DEBUG_REDIR printf("redir_start(): Screen redirected.\n"); #endif // Map overlay window. Done firstly according to this: // https://bugzilla.gnome.org/show_bug.cgi?id=597014 - if (overlay) - XMapWindow(dpy, overlay); + if (ps->overlay) + XMapWindow(ps->dpy, ps->overlay); - XCompositeRedirectSubwindows(dpy, root, CompositeRedirectManual); + XCompositeRedirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); // Must call XSync() here - XSync(dpy, False); + XSync(ps->dpy, False); - redirected = True; + ps->redirected = true; } } @@ -4607,103 +4450,252 @@ redir_start(Display *dpy) { * Unredirect all windows. */ static void -redir_stop(Display *dpy) { - if (redirected) { +redir_stop(session_t *ps) { + if (ps->redirected) { #ifdef DEBUG_REDIR printf("redir_stop(): Screen unredirected.\n"); #endif // Destroy all Pictures as they expire once windows are unredirected // If we don't destroy them here, looks like the resources are just // kept inaccessible somehow - for (win *w = list; w; w = w->next) { - free_pixmap(dpy, &w->pixmap); - free_picture(dpy, &w->picture); + for (win *w = ps->list; w; w = w->next) { + free_pixmap(ps, &w->pixmap); + free_picture(ps, &w->picture); } - XCompositeUnredirectSubwindows(dpy, root, CompositeRedirectManual); - + XCompositeUnredirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); // Unmap overlay window - if (overlay) - XUnmapWindow(dpy, overlay); + if (ps->overlay) + XUnmapWindow(ps->dpy, ps->overlay); // Must call XSync() here - XSync(dpy, False); - - redirected = False; + XSync(ps->dpy, False); + ps->redirected = 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; - win *t; - - gettimeofday(&time_start, NULL); - - // Set locale so window names with special characters are interpreted - // correctly - setlocale (LC_ALL, ""); +/** + * Initialize a session. + * + * @param ps_old old session, from which the function will take the X + * connection, then free it + * @param argc number of commandline arguments + * @param argv commandline arguments + */ +static session_t * +session_init(session_t *ps_old, int argc, char **argv) { + const static session_t s_def = { + .dpy = NULL, + .scr = 0, + .vis = NULL, + .depth = 0, + .root = None, + .root_height = 0, + .root_width = 0, + // .root_damage = None, + .overlay = None, + .root_tile = None, + .screen_reg = None, + .tgt_picture = None, + .tgt_buffer = None, + .root_dbe = None, + .reg_win = None, + .o = { + .display = NULL, + .mark_wmwin_focused = false, + .mark_ovredir_focused = false, + .fork_after_register = false, + .synchronize = false, + .detect_rounded_corners = false, + .paint_on_overlay = false, + .unredir_if_possible = false, + + .refresh_rate = 0, + .sw_opti = false, + .vsync = VSYNC_NONE, + .dbe = false, + .vsync_aggressive = false, + + .wintype_shadow = { false }, + .shadow_red = 0.0, + .shadow_green = 0.0, + .shadow_blue = 0.0, + .shadow_radius = 12, + .shadow_offset_x = -15, + .shadow_offset_y = -15, + .shadow_opacity = .75, + .clear_shadow = false, + .shadow_blacklist = NULL, + .shadow_ignore_shaped = false, + .respect_prop_shadow = false, + + .wintype_fade = { false }, + .fade_in_step = 0.028 * OPAQUE, + .fade_out_step = 0.03 * OPAQUE, + .fade_delta = 10, + .no_fading_openclose = false, + .fade_blacklist = NULL, + + .wintype_opacity = { 0.0 }, + .inactive_opacity = 0, + .inactive_opacity_override = false, + .frame_opacity = 0.0, + .detect_client_opacity = false, + .inactive_dim = 0.0, + .alpha_step = 0.03, + .use_ewmh_active_win = false, + + .track_focus = false, + .track_wdata = false, + }, + + .all_damage = None, + .time_start = { 0, 0 }, + .redirected = false, + .unredir_possible = false, + .alpha_picts = NULL, + .reg_ignore_expire = false, + .idling = false, + .fade_time = 0, + .ignore_head = NULL, + .ignore_tail = NULL, + .reset = false, + + .expose_rects = NULL, + .size_expose = 0, + .n_expose = 0, + + .list = NULL, + .active_win = NULL, + .black_picture = None, + .dim_picture = None, + .cshadow_picture = None, + .gaussian_map = NULL, + .cgsize = 0, + .shadow_corner = NULL, + .shadow_top = NULL, + + .refresh_rate = 0, + .refresh_intv = 0UL, + .paint_tm_offset = 0L, + + .drm_fd = 0, + + .glx_context = None, + .glx_get_video_sync = NULL, + .glx_wait_video_sync = NULL, + + .xfixes_event = 0, + .xfixes_error = 0, + .damage_event = 0, + .damage_error = 0, + .render_event = 0, + .render_error = 0, + .composite_event = 0, + .composite_error = 0, + .composite_opcode = 0, + .has_name_pixmap = false, + .shape_exists = false, + .shape_event = 0, + .shape_error = 0, + .randr_exists = 0, + .randr_event = 0, + .randr_error = 0, + .glx_exists = false, + .glx_event = 0, + .glx_error = 0, + .dbe_exists = false, + + .atom_opacity = None, + .atom_frame_extents = None, + .atom_client = None, + .atom_name = None, + .atom_name_ewmh = None, + .atom_class = None, + .atom_transient = None, + .atom_ewmh_active_win = None, + .atom_compton_shadow = None, + .atom_win_type = None, + .atoms_wintypes = { 0 } + }; - get_cfg(argc, argv); + int i; - dpy = XOpenDisplay(opts.display); - if (!dpy) { - fprintf(stderr, "Can't open display\n"); - exit(1); + // Allocate a session and copy default values into it + session_t *ps = malloc(sizeof(session_t)); + memcpy(ps, &s_def, sizeof(session_t)); + ps_g = ps; + ps->ignore_tail = &ps->ignore_head; + gettimeofday(&ps->time_start, NULL); + + get_cfg(ps, argc, argv); + + // Inherit old Display if possible, primarily for resource leak checking + if (ps_old && ps_old->dpy) + ps->dpy = ps_old->dpy; + + // Open Display + if (!ps->dpy) { + ps->dpy = XOpenDisplay(ps->o.display); + if (!ps->dpy) { + fprintf(stderr, "Can't open display\n"); + exit(1); + } } XSetErrorHandler(error); - if (opts.synchronize) { - XSynchronize(dpy, 1); + if (ps->o.synchronize) { + XSynchronize(ps->dpy, 1); } - scr = DefaultScreen(dpy); - root = RootWindow(dpy, scr); + ps->scr = DefaultScreen(ps->dpy); + ps->root = RootWindow(ps->dpy, ps->scr); - if (!XRenderQueryExtension(dpy, &render_event, &render_error)) { + ps->vis = DefaultVisual(ps->dpy, ps->scr); + ps->depth = DefaultDepth(ps->dpy, ps->scr); + + if (!XRenderQueryExtension(ps->dpy, + &ps->render_event, &ps->render_error)) { fprintf(stderr, "No render extension\n"); exit(1); } - if (!XQueryExtension(dpy, COMPOSITE_NAME, &composite_opcode, - &composite_event, &composite_error)) { + if (!XQueryExtension(ps->dpy, COMPOSITE_NAME, &ps->composite_opcode, + &ps->composite_event, &ps->composite_error)) { fprintf(stderr, "No composite extension\n"); exit(1); } - XCompositeQueryVersion(dpy, &composite_major, &composite_minor); + { + int composite_major = 0, composite_minor = 0; + + XCompositeQueryVersion(ps->dpy, &composite_major, &composite_minor); - if (composite_major > 0 || composite_minor >= 2) { - has_name_pixmap = True; + if (composite_major > 0 || composite_minor >= 2) { + ps->has_name_pixmap = true; + } } - if (!XDamageQueryExtension(dpy, &damage_event, &damage_error)) { + if (!XDamageQueryExtension(ps->dpy, &ps->damage_event, &ps->damage_error)) { fprintf(stderr, "No damage extension\n"); exit(1); } - if (!XFixesQueryExtension(dpy, &xfixes_event, &xfixes_error)) { + if (!XFixesQueryExtension(ps->dpy, &ps->xfixes_event, &ps->xfixes_error)) { fprintf(stderr, "No XFixes extension\n"); exit(1); } // Query X Shape - if (XShapeQueryExtension(dpy, &shape_event, &shape_error)) { - shape_exists = True; + if (XShapeQueryExtension(ps->dpy, &ps->shape_event, &ps->shape_error)) { + ps->shape_exists = true; } // Query X RandR - if (opts.sw_opti && !opts.refresh_rate) { - if (XRRQueryExtension(dpy, &randr_event, &randr_error)) - randr_exists = True; + if (ps->o.sw_opti && !ps->o.refresh_rate) { + if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) + ps->randr_exists = true; else fprintf(stderr, "No XRandR extension, automatic refresh rate " "detection impossible.\n"); @@ -4711,170 +4703,368 @@ main(int argc, char **argv) { #ifdef CONFIG_VSYNC_OPENGL // Query X GLX extension - if (VSYNC_OPENGL == opts.vsync) { - if (glXQueryExtension(dpy, &glx_event, &glx_error)) - glx_exists = True; + if (VSYNC_OPENGL == ps->o.vsync) { + if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) + ps->glx_exists = true; else { fprintf(stderr, "No GLX extension, OpenGL VSync impossible.\n"); - opts.vsync = VSYNC_NONE; + ps->o.vsync = VSYNC_NONE; } } #endif // Query X DBE extension - if (opts.dbe) { + if (ps->o.dbe) { int dbe_ver_major = 0, dbe_ver_minor = 0; - if (XdbeQueryExtension(dpy, &dbe_ver_major, &dbe_ver_minor)) + if (XdbeQueryExtension(ps->dpy, &dbe_ver_major, &dbe_ver_minor)) if (dbe_ver_major >= 1) - dbe_exists = True; + ps->dbe_exists = true; else fprintf(stderr, "DBE extension version too low. Double buffering " "impossible.\n"); else { fprintf(stderr, "No DBE extension. Double buffering impossible.\n"); } - if (!dbe_exists) - opts.dbe = False; + if (!ps->dbe_exists) + ps->o.dbe = false; } - register_cm((VSYNC_OPENGL == opts.vsync)); + register_cm(ps, (VSYNC_OPENGL == ps->o.vsync)); // Initialize software optimization - if (opts.sw_opti) - opts.sw_opti = sw_opti_init(); + if (ps->o.sw_opti) + ps->o.sw_opti = sw_opti_init(ps); // Initialize DRM/OpenGL VSync - if ((VSYNC_DRM == opts.vsync && !vsync_drm_init()) - || (VSYNC_OPENGL == opts.vsync && !vsync_opengl_init())) - opts.vsync = VSYNC_NONE; + if ((VSYNC_DRM == ps->o.vsync && !vsync_drm_init(ps)) + || (VSYNC_OPENGL == ps->o.vsync && !vsync_opengl_init(ps))) + ps->o.vsync = VSYNC_NONE; // Overlay must be initialized before double buffer - if (opts.paint_on_overlay) - init_overlay(); + if (ps->o.paint_on_overlay) + init_overlay(ps); - if (opts.dbe) - init_dbe(); + if (ps->o.dbe) + init_dbe(ps); - if (opts.fork_after_register) fork_after(); + if (ps->o.fork_after_register) fork_after(); - get_atoms(); - init_alpha_picts(dpy); + init_atoms(ps); + init_alpha_picts(ps); - pa.subwindow_mode = IncludeInferiors; + ps->gaussian_map = make_gaussian_map(ps->o.shadow_radius); + presum_gaussian(ps, ps->gaussian_map); - gaussian_map = make_gaussian_map(dpy, opts.shadow_radius); - presum_gaussian(gaussian_map); + ps->root_width = DisplayWidth(ps->dpy, ps->scr); + ps->root_height = DisplayHeight(ps->dpy, ps->scr); - root_width = DisplayWidth(dpy, scr); - root_height = DisplayHeight(dpy, scr); + rebuild_screen_reg(ps); - rebuild_screen_reg(dpy); + { + XRenderPictureAttributes pa; + pa.subwindow_mode = IncludeInferiors; - root_picture = XRenderCreatePicture(dpy, root, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), - CPSubwindowMode, &pa); - if (opts.paint_on_overlay) { - tgt_picture = XRenderCreatePicture(dpy, overlay, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + ps->root_picture = XRenderCreatePicture(ps->dpy, ps->root, + XRenderFindVisualFormat(ps->dpy, ps->vis), CPSubwindowMode, &pa); - } - else { - tgt_picture = root_picture; + if (ps->o.paint_on_overlay) { + ps->tgt_picture = XRenderCreatePicture(ps->dpy, ps->overlay, + XRenderFindVisualFormat(ps->dpy, ps->vis), + CPSubwindowMode, &pa); + } + else { + ps->tgt_picture = ps->root_picture; + } } - black_picture = solid_picture(dpy, True, 1, 0, 0, 0); + ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0); // Generates another Picture for shadows if the color is modified by // user - if (!opts.shadow_red && !opts.shadow_green && !opts.shadow_blue) { - cshadow_picture = black_picture; + if (!ps->o.shadow_red && !ps->o.shadow_green && !ps->o.shadow_blue) { + ps->cshadow_picture = ps->black_picture; } else { - cshadow_picture = solid_picture(dpy, True, 1, - opts.shadow_red, opts.shadow_green, opts.shadow_blue); + ps->cshadow_picture = solid_picture(ps, true, 1, + ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue); } // Generates a picture for inactive_dim - if (opts.inactive_dim) { - dim_picture = solid_picture(dpy, True, opts.inactive_dim, 0, 0, 0); + if (ps->o.inactive_dim) { + ps->dim_picture = solid_picture(ps, true, ps->o.inactive_dim, 0, 0, 0); } - all_damage = None; - XGrabServer(dpy); + ps->all_damage = None; + XGrabServer(ps->dpy); - redir_start(dpy); + redir_start(ps); - XSelectInput(dpy, root, + XSelectInput(ps->dpy, ps->root, SubstructureNotifyMask | ExposureMask | StructureNotifyMask | PropertyChangeMask); - XQueryTree(dpy, root, &root_return, - &parent_return, &children, &nchildren); + { + Window root_return, parent_return; + Window *children; + unsigned int nchildren; + + XQueryTree(ps->dpy, ps->root, &root_return, + &parent_return, &children, &nchildren); + + for (i = 0; i < nchildren; i++) { + add_win(ps, children[i], i ? children[i-1] : None, false); + } - for (i = 0; i < nchildren; i++) { - add_win(dpy, children[i], i ? children[i-1] : None, False); + XFree(children); } - XFree(children); - if (opts.track_focus) { - recheck_focus(dpy); + if (ps->o.track_focus) { + recheck_focus(ps); } - XUngrabServer(dpy); + XUngrabServer(ps->dpy); + + // Free the old session + if (ps_old) + free(ps_old); + + return ps; +} + +/** + * Destroy a session. + * + * Does not close the X connection or free the session_t + * structure, though. + * + * @param ps session to destroy + */ +static void +session_destroy(session_t *ps) { + redir_stop(ps); + + // Stop listening to events on root window + XSelectInput(ps->dpy, ps->root, 0); - ufd.fd = ConnectionNumber(dpy); - ufd.events = POLLIN; + // Free window linked list + { + win *next = NULL; + for (win *w = ps->list; w; w = next) { + // Must be put here to avoid segfault + next = w->next; - if (opts.sw_opti) - paint_tm_offset = get_time_timespec().tv_nsec; + if (IsViewable == w->a.map_state && !w->destroyed) + win_ev_stop(ps, w); - reg_ignore_expire = True; + free_win_res(ps, w); + free(w); + } - fade_time = get_time_ms(); + ps->list = NULL; + } - t = paint_preprocess(dpy, list); + // Free alpha_picts + { + const int max = lround(1.0 / ps->o.alpha_step) + 1; + for (int i = 0; i < max; ++i) + free_picture(ps, &ps->alpha_picts[i]); + free(ps->alpha_picts); + ps->alpha_picts = NULL; + } + + // Free blacklists + free_wincondlst(&ps->o.shadow_blacklist); + free_wincondlst(&ps->o.fade_blacklist); + + // Free ignore linked list + { + ignore_t *next = NULL; + for (ignore_t *ign = ps->ignore_head; ign; ign = next) { + next = ign->next; - if (redirected) - paint_all(dpy, None, t); + free(ign); + } + + // Reset head and tail + ps->ignore_head = NULL; + ps->ignore_tail = &ps->ignore_head; + } + + // Free cshadow_picture and black_picture + if (ps->cshadow_picture == ps->black_picture) + ps->cshadow_picture = None; + else + free_picture(ps, &ps->cshadow_picture); + + free_picture(ps, &ps->black_picture); + + // Free tgt_{buffer,picture} and root_picture + if (ps->tgt_buffer == ps->tgt_picture) + ps->tgt_buffer = None; + else + free_picture(ps, &ps->tgt_buffer); + + if (ps->tgt_picture == ps->root_picture) + ps->tgt_picture = None; + else + free_picture(ps, &ps->tgt_picture); + + free_picture(ps, &ps->root_picture); + + // Free other X resources + free_picture(ps, &ps->dim_picture); + free_picture(ps, &ps->root_tile); + free_region(ps, &ps->screen_reg); + free_region(ps, &ps->all_damage); + free(ps->expose_rects); + free(ps->shadow_corner); + free(ps->shadow_top); + free(ps->gaussian_map); + free(ps->o.display); + + // Free reg_win and glx_context + if (ps->reg_win) { + XDestroyWindow(ps->dpy, ps->reg_win); + ps->reg_win = None; + } + if (ps->glx_context) { + glXDestroyContext(ps->dpy, ps->glx_context); + ps->glx_context = None; + } + + // Free double buffer + if (ps->root_dbe) { + XdbeDeallocateBackBufferName(ps->dpy, ps->root_dbe); + ps->root_dbe = None; + } + + // Close file opened for DRM VSync + if (ps->drm_fd) { + close(ps->drm_fd); + ps->drm_fd = 0; + } + + // Release overlay window + if (ps->overlay) { + XCompositeReleaseOverlayWindow(ps->dpy, ps->overlay); + ps->overlay = None; + } + + // Flush all events + XSync(ps->dpy, True); + + if (ps == ps_g) + ps_g = NULL; +} + +/** + * Do the actual work. + * + * @param ps current session + */ +static void +session_run(session_t *ps) { + win *t; + + if (ps->o.sw_opti) + ps->paint_tm_offset = get_time_timespec().tv_nsec; + + ps->reg_ignore_expire = true; + + ps->fade_time = get_time_ms(); + + t = paint_preprocess(ps, ps->list); + + if (ps->redirected) + paint_all(ps, None, t); // Initialize idling - idling = False; + ps->idling = false; // Main loop - while (1) { - Bool ev_received = False; + while (!ps->reset) { + bool ev_received = false; - while (XEventsQueued(dpy, QueuedAfterReading) - || (evpoll(&ufd, - (ev_received ? 0: (idling ? -1: fade_timeout()))) > 0)) { + while (XEventsQueued(ps->dpy, QueuedAfterReading) + || (evpoll(ps, + (ev_received ? 0: (ps->idling ? -1: fade_timeout(ps)))) > 0)) { // Sometimes poll() returns 1 but no events are actually read, // causing XNextEvent() to block, I have no idea what's wrong, so we // check for the number of events here - if (XEventsQueued(dpy, QueuedAfterReading)) { - XNextEvent(dpy, &ev); - ev_handle((XEvent *) &ev); - ev_received = True; + if (XEventsQueued(ps->dpy, QueuedAfterReading)) { + XEvent ev; + + XNextEvent(ps->dpy, &ev); + ev_handle(ps, &ev); + ev_received = true; } } // idling will be turned off during paint_preprocess() if needed - idling = True; + ps->idling = true; - t = paint_preprocess(dpy, list); + t = paint_preprocess(ps, ps->list); // If the screen is unredirected, free all_damage to stop painting - if (!redirected) - free_region(dpy, &all_damage); + if (!ps->redirected) + free_region(ps, &ps->all_damage); - if (all_damage && !is_region_empty(dpy, all_damage)) { + if (ps->all_damage && !is_region_empty(ps, ps->all_damage)) { static int paint; - paint_all(dpy, all_damage, t); - reg_ignore_expire = False; + paint_all(ps, ps->all_damage, t); + ps->reg_ignore_expire = false; paint++; - XSync(dpy, False); - all_damage = None; + XSync(ps->dpy, False); + ps->all_damage = None; } } } + +/** + * Turn on the program reset flag. + * + * This will result in compton resetting itself after next paint. + */ +static void +reset_enable(int __attribute__((unused)) signum) { + session_t * const ps = ps_g; + + ps->reset = true; +} + +/** + * The function that everybody knows. + */ +int +main(int argc, char **argv) { + // Set locale so window names with special characters are interpreted + // correctly + setlocale(LC_ALL, ""); + + // Set up SIGUSR1 signal handler to reset program + { + sigset_t block_mask; + sigemptyset(&block_mask); + const struct sigaction action= { + .sa_handler = reset_enable, + .sa_mask = block_mask, + .sa_flags = 0 + }; + sigaction(SIGUSR1, &action, NULL); + } + + // Main loop + session_t *ps_old = ps_g; + while (1) { + ps_g = session_init(ps_old, argc, argv); + session_run(ps_g); + ps_old = ps_g; + session_destroy(ps_g); + } + + free(ps_g); + + return 0; +} diff --git a/compton.h b/compton.h index 2b3e08604..51f59eb98 100644 --- a/compton.h +++ b/compton.h @@ -15,6 +15,7 @@ // #define DEBUG_WINDATA 1 // #define DEBUG_WINMATCH 1 // #define DEBUG_REDIR 1 +// #define DEBUG_ALLOC_REG 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled @@ -48,8 +49,8 @@ #include #include #include - #include +#include #ifdef CONFIG_REGEX_PCRE #include @@ -75,6 +76,7 @@ #include #include #include +#include #ifdef CONFIG_VSYNC_DRM #include @@ -90,58 +92,6 @@ #include #endif -static void -print_timestamp(void); - -#ifdef DEBUG_ALLOC_REG - -#include -#define BACKTRACE_SIZE 5 - -/** - * Print current backtrace, excluding the first two items. - * - * Stolen from glibc manual. - */ -static inline void -print_backtrace(void) { - void *array[BACKTRACE_SIZE]; - size_t size; - char **strings; - - size = backtrace(array, BACKTRACE_SIZE); - strings = backtrace_symbols(array, size); - - for (size_t i = 2; i < size; i++) - printf ("%s\n", strings[i]); - - free(strings); -} - -static inline XserverRegion -XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, - const char *func, int line) { - XserverRegion reg = XFixesCreateRegion(dpy, p, n); - print_timestamp(); - printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); - print_backtrace(); - fflush(stdout); - return reg; -} - -static inline void -XFixesDestroyRegion_(Display *dpy, XserverRegion reg, - const char *func, int line) { - XFixesDestroyRegion(dpy, reg); - print_timestamp(); - printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); - fflush(stdout); -} - -#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) -#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) -#endif - // === Constants === #if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) #error libXcomposite version unsupported @@ -150,10 +100,6 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, #define ROUNDED_PERCENT 0.05 #define ROUNDED_PIXELS 10 -// For printing timestamps -#include -extern struct timeval time_start; - #define OPAQUE 0xffffffff #define REGISTER_PROP "_NET_WM_CM_S" @@ -194,7 +140,7 @@ typedef enum { WINTYPE_COMBO, WINTYPE_DND, NUM_WINTYPES -} wintype; +} wintype_t; typedef enum { WINDOW_SOLID, @@ -204,13 +150,13 @@ typedef enum { typedef struct { unsigned char *data; - int nitems; -} winattr_t; + unsigned long nitems; +} winprop_t; typedef struct _ignore { struct _ignore *next; unsigned long sequence; -} ignore; +} ignore_t; enum wincond_target { CONDTGT_NAME, @@ -238,8 +184,311 @@ typedef struct _wincond { #endif int16_t flags; struct _wincond *next; -} wincond; +} wincond_t; + +/// VSync modes. +typedef enum { + VSYNC_NONE, + VSYNC_DRM, + VSYNC_OPENGL, +} vsync_t; + +#ifdef CONFIG_VSYNC_OPENGL +typedef int (*f_WaitVideoSync) (int, int, unsigned *); +typedef int (*f_GetVideoSync) (unsigned *); +#endif + +typedef struct { + int size; + double *data; +} conv; + +struct _win; +/// Structure representing all options. +typedef struct { + // === General === + char *display; + /// Whether to try to detect WM windows and mark them as focused. + bool mark_wmwin_focused; + /// Whether to mark override-redirect windows as focused. + bool mark_ovredir_focused; + /// Whether to fork to background. + bool fork_after_register; + /// Whether to detect rounded corners. + bool detect_rounded_corners; + /// Whether to paint on X Composite overlay window instead of root + /// window. + bool paint_on_overlay; + /// Whether to unredirect all windows if a full-screen opaque window + /// is detected. + bool unredir_if_possible; + /// Whether to work under synchronized mode for debugging. + bool synchronize; + + // === VSync & software optimization === + /// User-specified refresh rate. + int refresh_rate; + /// Whether to enable refresh-rate-based software optimization. + bool sw_opti; + /// VSync method to use; + vsync_t vsync; + /// Whether to enable double buffer. + bool dbe; + /// Whether to do VSync aggressively. + bool vsync_aggressive; + + // === Shadow === + bool wintype_shadow[NUM_WINTYPES]; + /// Red, green and blue tone of the shadow. + double shadow_red, shadow_green, shadow_blue; + int shadow_radius; + int shadow_offset_x, shadow_offset_y; + double shadow_opacity; + bool clear_shadow; + /// Shadow blacklist. A linked list of conditions. + wincond_t *shadow_blacklist; + /// Whether bounding-shaped window should be ignored. + bool shadow_ignore_shaped; + /// Whether to respect _COMPTON_SHADOW. + bool respect_prop_shadow; + + // === Fading === + bool wintype_fade[NUM_WINTYPES]; + /// How much to fade in in a single fading step. + opacity_t fade_in_step; + /// How much to fade out in a single fading step. + opacity_t fade_out_step; + unsigned long fade_delta; + /// Whether to disable fading on window open/close. + bool no_fading_openclose; + /// Fading blacklist. A linked list of conditions. + wincond_t *fade_blacklist; + + // === Opacity === + double wintype_opacity[NUM_WINTYPES]; + /// Default opacity for inactive windows. + /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for + /// not enabled, default. + opacity_t inactive_opacity; + /// Whether inactive_opacity overrides the opacity set by window + /// attributes. + bool inactive_opacity_override; + /// Frame opacity. Relative to window opacity, also affects shadow + /// opacity. + double frame_opacity; + /// Whether to detect _NET_WM_OPACITY on client windows. Used on window + /// managers that don't pass _NET_WM_OPACITY to frame windows. + bool detect_client_opacity; + /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. + double inactive_dim; + /// Step for pregenerating alpha pictures. 0.01 - 1.0. + double alpha_step; + /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. + bool use_ewmh_active_win; + + // === Calculated === + /// Whether compton needs to track focus changes. + bool track_focus; + /// Whether compton needs to track window name and class. + bool track_wdata; + +} options_t; + +/// Structure containing all necessary data for a compton session. +typedef struct { + // === Display related === + /// Display in use. + Display *dpy; + /// Default screen. + int scr; + /// Default visual. + Visual *vis; + /// Default depth. + int depth; + /// Root window. + Window root; + /// Height of root window. + int root_height; + /// Width of root window. + int root_width; + // Damage of root window. + // Damage root_damage; + /// X Composite overlay window. Used if --paint-on-overlay. + Window overlay; + /// Picture of the root window background. + Picture root_tile; + /// A region of the size of the screen. + XserverRegion screen_reg; + /// Picture of root window. Destination of painting in no-DBE painting + /// mode. + Picture root_picture; + /// A Picture acting as the painting target. + Picture tgt_picture; + /// Temporary buffer to paint to before sending to display. + Picture tgt_buffer; + /// DBE back buffer for root window. Used in DBE painting mode. + XdbeBackBuffer root_dbe; + /// Window ID of the window we register as a symbol. + Window reg_win; + + // === Operation related === + /// Program options. + options_t o; + /// Program start time. + struct timeval time_start; + /// The region needs to painted on next paint. + XserverRegion all_damage; + /// Whether all windows are currently redirected. + bool redirected; + /// Whether there's a highest full-screen window, and all windows could + /// be unredirected. + bool unredir_possible; + /// Pre-generated alpha pictures. + Picture *alpha_picts; + /// Whether all reg_ignore of windows should expire in this paint. + bool reg_ignore_expire; + /// Whether the program is idling. I.e. no fading, no potential window + /// changes. + bool idling; + /// Time of last fading. In milliseconds. + unsigned long fade_time; + /// Head pointer of the error ignore linked list. + ignore_t *ignore_head; + /// Pointer to the next member of tail element of the error + /// ignore linked list. + ignore_t **ignore_tail; + /// Reset program after next paint. + bool reset; + + // === Expose event related === + /// Pointer to an array of XRectangle-s of exposed region. + XRectangle *expose_rects; + /// Number of XRectangle-s in expose_rects. + int size_expose; + /// Index of the next free slot in expose_rects. + int n_expose; + + // === Window related === + /// Linked list of all windows. + struct _win *list; + /// Current active window. Used by EWMH _NET_ACTIVE_WINDOW focus + /// detection. + struct _win *active_win; + + // === Shadow/dimming related === + /// 1x1 black Picture. + Picture black_picture; + /// Picture used for dimming inactive windows. + Picture dim_picture; + /// 1x1 Picture of the shadow color. + Picture cshadow_picture; + /// Gaussian map of shadow. + conv *gaussian_map; + // for shadow precomputation + /// Shadow depth on one side. + int cgsize; + /// Pre-computed color table for corners of shadow. + unsigned char *shadow_corner; + /// Pre-computed color table for a side of shadow. + unsigned char *shadow_top; + + // === Software-optimization-related === + /// Currently used refresh rate. + short refresh_rate; + /// Interval between refresh in nanoseconds. + unsigned long refresh_intv; + /// Nanosecond offset of the first painting. + long paint_tm_offset; + + #ifdef CONFIG_VSYNC_DRM + // === DRM VSync related === + /// File descriptor of DRI device file. Used for DRM VSync. + int drm_fd; + #endif + + #ifdef CONFIG_VSYNC_OPENGL + // === OpenGL VSync related === + /// GLX context. + GLXContext glx_context; + /// Pointer to glXGetVideoSyncSGI function. + f_GetVideoSync glx_get_video_sync; + /// Pointer to glXWaitVideoSyncSGI function. + f_WaitVideoSync glx_wait_video_sync; + #endif + + // === X extension related === + /// Event base number for X Fixes extension. + int xfixes_event; + /// Error base number for X Fixes extension. + int xfixes_error; + /// Event base number for X Damage extension. + int damage_event; + /// Error base number for X Damage extension. + int damage_error; + /// Event base number for X Render extension. + int render_event; + /// Error base number for X Render extension. + int render_error; + /// Event base number for X Composite extension. + int composite_event; + /// Error base number for X Composite extension. + int composite_error; + /// Major opcode for X Composite extension. + int composite_opcode; + /// Whether X Composite NameWindowPixmap is available. Aka if X + /// Composite version >= 0.2. + bool has_name_pixmap; + /// Whether X Shape extension exists. + bool shape_exists; + /// Event base number for X Shape extension. + int shape_event; + /// Error base number for X Shape extension. + int shape_error; + /// Whether X RandR extension exists. + bool randr_exists; + /// Event base number for X RandR extension. + int randr_event; + /// Error base number for X RandR extension. + int randr_error; + #ifdef CONFIG_VSYNC_OPENGL + /// Whether X GLX extension exists. + bool glx_exists; + /// Event base number for X GLX extension. + int glx_event; + /// Error base number for X GLX extension. + int glx_error; + #endif + /// Whether X DBE extension exists. + bool dbe_exists; + + // === Atoms === + /// Atom of property _NET_WM_OPACITY. + Atom atom_opacity; + /// Atom of _NET_FRAME_EXTENTS. + Atom atom_frame_extents; + /// Property atom to identify top-level frame window. Currently + /// WM_STATE. + Atom atom_client; + /// Atom of property WM_NAME. + Atom atom_name; + /// Atom of property _NET_WM_NAME. + Atom atom_name_ewmh; + /// Atom of property WM_CLASS. + Atom atom_class; + /// Atom of property WM_TRANSIENT_FOR. + Atom atom_transient; + /// Atom of property _NET_ACTIVE_WINDOW. + Atom atom_ewmh_active_win; + /// Atom of property _COMPTON_SHADOW. + Atom atom_compton_shadow; + /// Atom of property _NET_WM_WINDOW_TYPE. + Atom atom_win_type; + /// Array of atoms of all possible window types. + Atom atoms_wintypes[NUM_WINTYPES]; +} session_t; + +/// Structure representing a top-level window compton manages. typedef struct _win { struct _win *next; Window id; @@ -253,25 +502,26 @@ typedef struct _win { XserverRegion border_size; XserverRegion extents; // Type of the window. - wintype window_type; + wintype_t window_type; /// Whether the window is focused. - Bool focused; - Bool destroyed; + bool focused; + /// Whether the window has been destroyed. + bool destroyed; /// Cached width/height of the window including border. int widthb, heightb; /// Whether the window is bounding-shaped. - Bool bounding_shaped; + bool bounding_shaped; /// Whether the window just have rounded corners. - Bool rounded_corners; - /// Whether this window is to be painted - Bool to_paint; + bool rounded_corners; + /// Whether this window is to be painted. + bool to_paint; // Blacklist related members char *name; char *class_instance; char *class_general; - wincond *cache_sblst; - wincond *cache_fblst; + wincond_t *cache_sblst; + wincond_t *cache_fblst; // Opacity-related members /// Current window opacity. @@ -290,9 +540,9 @@ typedef struct _win { // Fading-related members /// Do not fade if it's false. Change on window type change. /// Used by fading blacklist in the future. - Bool fade; + bool fade; /// Callback to be called after fading completed. - void (*fade_callback) (Display *dpy, struct _win *w); + void (*fade_callback) (session_t *ps, struct _win *w); // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. @@ -304,7 +554,7 @@ typedef struct _win { // Shadow-related members /// Whether a window has shadow. Affected by window type. - Bool shadow; + bool shadow; /// Opacity of the shadow. Affected by window opacity and frame opacity. double shadow_opacity; /// X offset of shadow. Affected by commandline argument. @@ -325,14 +575,17 @@ typedef struct _win { // Dim-related members /// Whether the window is to be dimmed. - Bool dim; + bool dim; /// Window flags. Definitions above. int_fast16_t flags; unsigned long damage_sequence; /* sequence when damage was created */ - Bool need_configure; + /// Whether there's a pending ConfigureNotify happening + /// when the window is unmapped. + bool need_configure; + /// Queued ConfigureNotify when the window is unmapped. XConfigureEvent queue_configure; /// Region to be ignored when painting. Basically the region where /// higher opaque windows will paint upon. Depends on window frame @@ -343,132 +596,83 @@ typedef struct _win { struct _win *prev_trans; } win; -/// VSync modes. -typedef enum { - VSYNC_NONE, - VSYNC_DRM, - VSYNC_OPENGL, -} vsync_t; - -#ifdef CONFIG_VSYNC_OPENGL -typedef int (*f_WaitVideoSync) (int, int, unsigned *); -typedef int (*f_GetVideoSync) (unsigned *); -#endif - -typedef struct { - // General - char *display; - /// Whether to try to detect WM windows and mark them as focused. - Bool mark_wmwin_focused; - /// Whether to mark override-redirect windows as focused. - Bool mark_ovredir_focused; - /// Whether to fork to background. - Bool fork_after_register; - /// Whether to detect rounded corners. - Bool detect_rounded_corners; - /// Whether to paint on X Composite overlay window instead of root - /// window. - Bool paint_on_overlay; - /// Whether to unredirect all windows if a full-screen opaque window - /// is detected. - Bool unredir_if_possible; - /// Whether to work under synchronized mode for debugging. - Bool synchronize; - - // VSync and software optimization - /// User-specified refresh rate. - int refresh_rate; - /// Whether to enable refresh-rate-based software optimization. - Bool sw_opti; - /// VSync method to use; - vsync_t vsync; - /// Whether to enable double buffer. - Bool dbe; - /// Whether to do VSync aggressively. - Bool vsync_aggressive; - - // Shadow - Bool wintype_shadow[NUM_WINTYPES]; - /// Red, green and blue tone of the shadow. - double shadow_red, shadow_green, shadow_blue; - int shadow_radius; - int shadow_offset_x, shadow_offset_y; - double shadow_opacity; - Bool clear_shadow; - /// Shadow blacklist. A linked list of conditions. - wincond *shadow_blacklist; - /// Whether bounding-shaped window should be ignored. - Bool shadow_ignore_shaped; - /// Whether to respect _COMPTON_SHADOW. - Bool respect_attr_shadow; - - // Fading - Bool wintype_fade[NUM_WINTYPES]; - /// How much to fade in in a single fading step. - opacity_t fade_in_step; - /// How much to fade out in a single fading step. - opacity_t fade_out_step; - unsigned long fade_delta; - Bool no_fading_openclose; - /// Fading blacklist. A linked list of conditions. - wincond *fade_blacklist; - - // Opacity - double wintype_opacity[NUM_WINTYPES]; - /// Default opacity for inactive windows. - /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for - /// not enabled, default. - opacity_t inactive_opacity; - /// Whether inactive_opacity overrides the opacity set by window - /// attributes. - Bool inactive_opacity_override; - /// Frame opacity. Relative to window opacity, also affects shadow - /// opacity. - double frame_opacity; - /// Whether to detect _NET_WM_OPACITY on client windows. Used on window - /// managers that don't pass _NET_WM_OPACITY to frame windows. - Bool detect_client_opacity; - /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. - double inactive_dim; - /// Step for pregenerating alpha pictures. 0.01 - 1.0. - double alpha_step; - /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. - Bool use_ewmh_active_win; - - // Calculated - /// Whether compton needs to track focus changes. - Bool track_focus; - /// Whether compton needs to track window name and class. - Bool track_wdata; - -} options_t; - +/// Temporary structure used for communication between +/// get_cfg() and parse_config(). struct options_tmp { - Bool no_dock_shadow; - Bool no_dnd_shadow; + bool no_dock_shadow; + bool no_dnd_shadow; double menu_opacity; }; -typedef struct _conv { - int size; - double *data; -} conv; - typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, WIN_EVMODE_CLIENT } win_evmode_t; -extern int root_height, root_width; -extern Atom atom_client_attr; -extern Bool idling; -extern Bool shape_exists; -extern Bool reg_ignore_expire; +extern const char *WINTYPES[NUM_WINTYPES]; +extern session_t *ps_g; + +// == Debugging code == +static void +print_timestamp(session_t *ps); + +#ifdef DEBUG_ALLOC_REG + +#include +#define BACKTRACE_SIZE 5 + +/** + * Print current backtrace, excluding the first two items. + * + * Stolen from glibc manual. + */ +static inline void +print_backtrace(void) { + void *array[BACKTRACE_SIZE]; + size_t size; + char **strings; + + size = backtrace(array, BACKTRACE_SIZE); + strings = backtrace_symbols(array, size); + + for (size_t i = 2; i < size; i++) + printf ("%s\n", strings[i]); + + free(strings); +} + +/** + * Wrapper of XFixesCreateRegion, for debugging. + */ +static inline XserverRegion +XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, + const char *func, int line) { + XserverRegion reg = XFixesCreateRegion(dpy, p, n); + print_timestamp(ps_g); + printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); + print_backtrace(); + fflush(stdout); + return reg; +} /** - * Functions + * Wrapper of XFixesDestroyRegion, for debugging. */ +static inline void +XFixesDestroyRegion_(Display *dpy, XserverRegion reg, + const char *func, int line) { + XFixesDestroyRegion(dpy, reg); + print_timestamp(ps_g); + printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); + fflush(stdout); +} + +#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) +#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) +#endif + +// == Functions == // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline @@ -476,40 +680,45 @@ extern Bool reg_ignore_expire; // Helper functions static void -discard_ignore(Display *dpy, unsigned long sequence); +discard_ignore(session_t *ps, unsigned long sequence); static void -set_ignore(Display *dpy, unsigned long sequence); +set_ignore(session_t *ps, unsigned long sequence); + +static inline void +set_ignore_next(session_t *ps) { + set_ignore(ps, NextRequest(ps->dpy)); +} static int -should_ignore(Display *dpy, unsigned long sequence); +should_ignore(session_t *ps, unsigned long sequence); /** * Subtract two unsigned long values. * * Truncate to 0 if the result is negative. */ -static inline unsigned long +static inline unsigned long __attribute__((const)) sub_unslong(unsigned long a, unsigned long b) { return (a > b) ? a - b : 0; } /** - * Set a Bool array of all wintypes to true. + * Set a bool array of all wintypes to true. */ static void -wintype_arr_enable(Bool arr[]) { - wintype i; +wintype_arr_enable(bool arr[]) { + wintype_t i; for (i = 0; i < NUM_WINTYPES; ++i) { - arr[i] = True; + arr[i] = true; } } /** * Allocate the space and copy a string. */ -static inline char * +static inline char * __attribute__((const)) mstrcpy(const char *src) { char *str = malloc(sizeof(char) * (strlen(src) + 1)); @@ -521,7 +730,7 @@ mstrcpy(const char *src) { /** * Allocate the space and join two strings. */ -static inline char * +static inline char * __attribute__((const)) mstrjoin(const char *src1, const char *src2) { char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); @@ -534,7 +743,7 @@ mstrjoin(const char *src1, const char *src2) { /** * Allocate the space and join two strings; */ -static inline char * +static inline char * __attribute__((const)) mstrjoin3(const char *src1, const char *src2, const char *src3) { char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + strlen(src3) + 1)); @@ -554,7 +763,7 @@ mstrjoin3(const char *src1, const char *src2, const char *src3) { * @param max maximum value * @return normalized value */ -static inline int +static inline int __attribute__((const)) normalize_i_range(int i, int min, int max) { if (i > max) return max; if (i < min) return min; @@ -564,7 +773,7 @@ normalize_i_range(int i, int min, int max) { /** * Select the larger integer of two. */ -static inline int +static inline int __attribute__((const)) max_i(int a, int b) { return (a > b ? a : b); } @@ -572,7 +781,7 @@ max_i(int a, int b) { /** * Select the smaller integer of two. */ -static inline int +static inline int __attribute__((const)) min_i(int a, int b) { return (a > b ? b : a); } @@ -585,7 +794,7 @@ min_i(int a, int b) { * @param max maximum value * @return normalized value */ -static inline double +static inline double __attribute__((const)) normalize_d_range(double d, double min, double max) { if (d > max) return max; if (d < min) return min; @@ -598,7 +807,7 @@ normalize_d_range(double d, double min, double max) { * @param d double value to normalize * @return normalized value */ -static inline double +static inline double __attribute__((const)) normalize_d(double d) { return normalize_d_range(d, 0.0, 1.0); } @@ -610,15 +819,15 @@ normalize_d(double d) { * @param count amount of elements in the array * @param wid window ID to search for */ -static inline Bool +static inline bool array_wid_exists(const Window *arr, int count, Window wid) { while (count--) { if (arr[count] == wid) { - return True; + return true; } } - return False; + return false; } /* @@ -695,9 +904,9 @@ timespec_subtract(struct timespec *result, * * Note its starting time is unspecified. */ -static inline struct timespec +static inline struct timespec __attribute__((const)) get_time_timespec(void) { - struct timespec tm = { 0 }; + struct timespec tm = { 0, 0 }; clock_gettime(CLOCK_MONOTONIC, &tm); @@ -711,12 +920,12 @@ get_time_timespec(void) { * Used for debugging. */ static void -print_timestamp(void) { +print_timestamp(session_t *ps) { struct timeval tm, diff; if (gettimeofday(&tm, NULL)) return; - timeval_subtract(&diff, &tm, &time_start); + timeval_subtract(&diff, &tm, &ps->time_start); printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); } @@ -724,9 +933,9 @@ print_timestamp(void) { * Destroy a XserverRegion. */ inline static void -free_region(Display *dpy, XserverRegion *p) { +free_region(session_t *ps, XserverRegion *p) { if (*p) { - XFixesDestroyRegion(dpy, *p); + XFixesDestroyRegion(ps->dpy, *p); *p = None; } } @@ -735,9 +944,9 @@ free_region(Display *dpy, XserverRegion *p) { * Destroy a Picture. */ inline static void -free_picture(Display *dpy, Picture *p) { +free_picture(session_t *ps, Picture *p) { if (*p) { - XRenderFreePicture(dpy, *p); + XRenderFreePicture(ps->dpy, *p); *p = None; } } @@ -746,9 +955,9 @@ free_picture(Display *dpy, Picture *p) { * Destroy a Pixmap. */ inline static void -free_pixmap(Display *dpy, Pixmap *p) { +free_pixmap(session_t *ps, Pixmap *p) { if (*p) { - XFreePixmap(dpy, *p); + XFreePixmap(ps->dpy, *p); *p = None; } } @@ -757,15 +966,64 @@ free_pixmap(Display *dpy, Pixmap *p) { * Destroy a Damage. */ inline static void -free_damage(Display *dpy, Damage *p) { +free_damage(session_t *ps, Damage *p) { if (*p) { // BadDamage will be thrown if the window is destroyed - set_ignore(dpy, NextRequest(dpy)); - XDamageDestroy(dpy, *p); + set_ignore_next(ps); + XDamageDestroy(ps->dpy, *p); *p = None; } } +/** + * Destroy a wincond_t. + */ +inline static void +free_wincond(wincond_t *cond) { + if (cond->pattern) + free(cond->pattern); +#ifdef CONFIG_REGEX_PCRE + if (cond->regex_pcre_extra) + pcre_free_study(cond->regex_pcre_extra); + if (cond->regex_pcre) + pcre_free(cond->regex_pcre); +#endif + free(cond); +} + +/** + * Destroy a linked list of wincond_t. + */ +inline static void +free_wincondlst(wincond_t **cond_lst) { + wincond_t *next = NULL; + + for (wincond_t *cond = *cond_lst; cond; cond = next) { + next = cond->next; + + free_wincond(cond); + } + + *cond_lst = NULL; +} + +/** + * Destroy all resources in a struct _win. + */ +inline static void +free_win_res(session_t *ps, win *w) { + free_region(ps, &w->extents); + free_pixmap(ps, &w->pixmap); + free_picture(ps, &w->picture); + free_region(ps, &w->border_size); + free_picture(ps, &w->shadow_pict); + free_damage(ps, &w->damage); + free_region(ps, &w->reg_ignore); + free(w->name); + free(w->class_instance); + free(w->class_general); +} + /** * Get current system clock in milliseconds. * @@ -782,53 +1040,51 @@ get_time_ms(void) { } static int -fade_timeout(void); +fade_timeout(session_t *ps); static void -run_fade(Display *dpy, win *w, unsigned steps); +run_fade(session_t *ps, win *w, unsigned steps); static void -set_fade_callback(Display *dpy, win *w, - void (*callback) (Display *dpy, win *w), Bool exec_callback); +set_fade_callback(session_t *ps, win *w, + void (*callback) (session_t *ps, win *w), bool exec_callback); /** * Execute fade callback of a window if fading finished. */ static inline void -check_fade_fin(Display *dpy, win *w) { +check_fade_fin(session_t *ps, win *w) { if (w->fade_callback && w->opacity == w->opacity_tgt) { // Must be the last line as the callback could destroy w! - set_fade_callback(dpy, w, NULL, True); + set_fade_callback(ps, w, NULL, true); } } static void -set_fade_callback(Display *dpy, win *w, - void (*callback) (Display *dpy, win *w), Bool exec_callback); +set_fade_callback(session_t *ps, win *w, + void (*callback) (session_t *ps, win *w), bool exec_callback); static double gaussian(double r, double x, double y); static conv * -make_gaussian_map(Display *dpy, double r); +make_gaussian_map(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); +presum_gaussian(session_t *ps, conv *map); static XImage * -make_shadow(Display *dpy, double opacity, - int width, int height, Bool clear_shadow); +make_shadow(session_t *ps, double opacity, int width, int height); static Picture -shadow_picture(Display *dpy, double opacity, int width, int height, - Bool clear_shadow); +shadow_picture(session_t *ps, double opacity, int width, int height); static Picture -solid_picture(Display *dpy, Bool argb, double a, +solid_picture(session_t *ps, bool argb, double a, double r, double g, double b); static inline bool is_normal_win(const win *w) { @@ -839,25 +1095,25 @@ static inline bool is_normal_win(const win *w) { /** * Determine if a window has a specific attribute. * - * @param dpy Display to use + * @param session_t current session * @param w window to check * @param atom atom of attribute to check * @return 1 if it has the attribute, 0 otherwise */ -static inline Bool -wid_has_attr(Display *dpy, Window w, Atom atom) { +static inline bool +wid_has_attr(const session_t *ps, Window w, Atom atom) { Atom type = None; int format; unsigned long nitems, after; unsigned char *data; - if (Success == XGetWindowProperty(dpy, w, atom, 0, 0, False, + if (Success == XGetWindowProperty(ps->dpy, w, atom, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data)) { XFree(data); - if (type) return True; + if (type) return true; } - return False; + return false; } /** @@ -866,17 +1122,17 @@ wid_has_attr(Display *dpy, Window w, Atom atom) { * Returns a blank structure if the returned type and format does not * match the requested type and format. * - * @param dpy Display to use + * @param session_t current session * @param w window * @param atom atom of attribute to fetch * @param length length to read * @param rtype atom of the requested type * @param rformat requested format - * @return a winattr_t structure containing the attribute + * @return a winprop_t structure containing the attribute * and number of items. A blank one on failure. */ -static winattr_t -wid_get_attr(Display *dpy, Window w, Atom atom, long length, +static winprop_t +wid_get_prop(const session_t *ps, Window w, Atom atom, long length, Atom rtype, int rformat) { Atom type = None; int format = 0; @@ -884,10 +1140,10 @@ wid_get_attr(Display *dpy, Window w, Atom atom, long length, unsigned char *data = NULL; // Use two if statements to deal with the sequence point issue. - if (Success == XGetWindowProperty(dpy, w, atom, 0L, length, False, + if (Success == XGetWindowProperty(ps->dpy, w, atom, 0L, length, False, rtype, &type, &format, &nitems, &after, &data)) { if (type == rtype && format == rformat) { - return (winattr_t) { + return (winprop_t) { .data = data, .nitems = nitems }; @@ -896,83 +1152,102 @@ wid_get_attr(Display *dpy, Window w, Atom atom, long length, XFree(data); - return (winattr_t) { + return (winprop_t) { .data = NULL, .nitems = 0 }; } /** - * Free a winattr_t. + * Free a winprop_t. * - * @param pattr pointer to the winattr_t to free. + * @param pprop pointer to the winprop_t to free. */ static inline void -free_winattr(winattr_t *pattr) { +free_winprop(winprop_t *pprop) { // Empty the whole structure to avoid possible issues - if (pattr->data) { - XFree(pattr->data); - pattr->data = NULL; + if (pprop->data) { + XFree(pprop->data); + pprop->data = NULL; + } + pprop->nitems = 0; +} + +/** + * Stop listening for events on a particular window. + */ +static inline void +win_ev_stop(session_t *ps, win *w) { + // Will get BadWindow if the window is destroyed + set_ignore_next(ps); + XSelectInput(ps->dpy, w->id, 0); + + if (w->client_win) { + set_ignore_next(ps); + XSelectInput(ps->dpy, w->client_win, 0); + } + + if (ps->shape_exists) { + set_ignore_next(ps); + XShapeSelectInput(ps->dpy, w->id, 0); } - pattr->nitems = 0; } /** * Get the children of a window. * - * @param dpy Display to use + * @param session_t current session * @param w window to check * @param children [out] an array of child window IDs * @param nchildren [out] number of children * @return 1 if successful, 0 otherwise */ -static inline Bool -wid_get_children(Display *dpy, Window w, +static inline bool +wid_get_children(session_t *ps, Window w, Window **children, unsigned *nchildren) { Window troot, tparent; - if (!XQueryTree(dpy, w, &troot, &tparent, children, nchildren)) { + if (!XQueryTree(ps->dpy, w, &troot, &tparent, children, nchildren)) { *nchildren = 0; - return False; + return false; } - return True; + return true; } /** * Check if a window is bounding-shaped. */ -static inline Bool -wid_bounding_shaped(Display *dpy, Window wid) { - if (shape_exists) { - Bool bounding_shaped = False; - Bool clip_shaped; +static inline bool +wid_bounding_shaped(const session_t *ps, Window wid) { + if (ps->shape_exists) { + Bool bounding_shaped = False, clip_shaped = False; int x_bounding, y_bounding, x_clip, y_clip; unsigned int w_bounding, h_bounding, w_clip, h_clip; - XShapeQueryExtents(dpy, wid, &bounding_shaped, + XShapeQueryExtents(ps->dpy, wid, &bounding_shaped, &x_bounding, &y_bounding, &w_bounding, &h_bounding, &clip_shaped, &x_clip, &y_clip, &w_clip, &h_clip); return bounding_shaped; } - - return False; + + return false; } /** - * Determine if a window change affects reg_ignore and set - * reg_ignore_expire accordingly. + * Determine if a window change affects reg_ignore and set + * reg_ignore_expire accordingly. */ static inline void -update_reg_ignore_expire(const win *w) { +update_reg_ignore_expire(session_t *ps, const win *w) { if (w->to_paint && WINDOW_SOLID == w->mode) - reg_ignore_expire = True; + ps->reg_ignore_expire = true; } /** * Check whether a window has WM frames. */ -static inline bool +static inline bool __attribute__((const)) win_has_frame(const win *w) { return w->top_width || w->left_width || w->right_width || w->bottom_width; @@ -984,261 +1259,261 @@ win_has_frame(const win *w) { * It's not using w->border_size for performance measures. */ static inline bool -win_is_fullscreen(const win *w) { - return (w->a.x <= 0 && w->a.y <= 0 && (w->a.x + w->widthb) >= root_width - && (w->a.y + w->heightb) >= root_height && !w->bounding_shaped); +win_is_fullscreen(session_t *ps, const win *w) { + return (w->a.x <= 0 && w->a.y <= 0 + && (w->a.x + w->widthb) >= ps->root_width + && (w->a.y + w->heightb) >= ps->root_height + && !w->bounding_shaped); } static void -win_rounded_corners(Display *dpy, win *w); +win_rounded_corners(session_t *ps, win *w); static bool -win_match_once(win *w, const wincond *cond); +win_match_once(win *w, const wincond_t *cond); static bool -win_match(win *w, wincond *condlst, wincond * *cache); +win_match(win *w, wincond_t *condlst, wincond_t * *cache); -static Bool -condlst_add(wincond **pcondlst, const char *pattern); +static bool +condlst_add(wincond_t **pcondlst, const char *pattern); static long -determine_evmask(Display *dpy, Window wid, win_evmode_t mode); +determine_evmask(session_t *ps, Window wid, win_evmode_t mode); static win * -find_win(Window id); +find_win(session_t *ps, Window id); static win * -find_toplevel(Window id); +find_toplevel(session_t *ps, Window id); static win * -find_toplevel2(Display *dpy, Window wid); +find_toplevel2(session_t *ps, Window wid); static win * -recheck_focus(Display *dpy); +recheck_focus(session_t *ps); static Picture -root_tile_f(Display *dpy); +root_tile_f(session_t *ps); static void -paint_root(Display *dpy); +paint_root(session_t *ps, Picture tgt_buffer); static XserverRegion -win_extents(Display *dpy, win *w); +win_extents(session_t *ps, win *w); static XserverRegion -border_size(Display *dpy, win *w); +border_size(session_t *ps, win *w); static Window -find_client_win(Display *dpy, Window w); +find_client_win(session_t *ps, Window w); static void -get_frame_extents(Display *dpy, win *w, Window client); +get_frame_extents(session_t *ps, win *w, Window client); static win * -paint_preprocess(Display *dpy, win *list); +paint_preprocess(session_t *ps, win *list); static void -paint_all(Display *dpy, XserverRegion region, win *t); +paint_all(session_t *ps, XserverRegion region, win *t); static void -add_damage(Display *dpy, XserverRegion damage); +add_damage(session_t *ps, XserverRegion damage); static void -repair_win(Display *dpy, win *w); +repair_win(session_t *ps, win *w); -static wintype -get_wintype_prop(Display * dpy, Window w); +static wintype_t +wid_get_prop_wintype(session_t *ps, Window w); static void -map_win(Display *dpy, Window id, - unsigned long sequence, Bool fade, - Bool override_redirect); +map_win(session_t *ps, Window id, bool override_redirect); static void -finish_map_win(Display *dpy, win *w); +finish_map_win(session_t *ps, win *w); static void -finish_unmap_win(Display *dpy, win *w); +finish_unmap_win(session_t *ps, win *w); static void -unmap_callback(Display *dpy, win *w); +unmap_callback(session_t *ps, win *w); static void -unmap_win(Display *dpy, Window id, Bool fade); +unmap_win(session_t *ps, Window id); static opacity_t -wid_get_opacity_prop(Display *dpy, Window wid, opacity_t def); +wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); static double -get_opacity_percent(Display *dpy, win *w); +get_opacity_percent(win *w); static void -determine_mode(Display *dpy, win *w); +determine_mode(session_t *ps, win *w); static void -calc_opacity(Display *dpy, win *w, Bool refetch_prop); +calc_opacity(session_t *ps, win *w, bool refetch_prop); static void -calc_dim(Display *dpy, win *w); +calc_dim(session_t *ps, win *w); static inline void -set_focused(Display *dpy, win *w, Bool focused) { +set_focused(session_t *ps, win *w, bool focused) { w->focused = focused; - calc_opacity(dpy, w, False); - calc_dim(dpy, w); + calc_opacity(ps, w, false); + calc_dim(ps, w); } static void -determine_fade(Display *dpy, win *w); +determine_fade(session_t *ps, win *w); static void -win_update_shape_raw(Display *dpy, win *w); +win_update_shape_raw(session_t *ps, win *w); static void -win_update_shape(Display *dpy, win *w); +win_update_shape(session_t *ps, win *w); static void -win_update_attr_shadow_raw(Display *dpy, win *w); +win_update_attr_shadow_raw(session_t *ps, win *w); static void -win_update_attr_shadow(Display *dpy, win *w); +win_update_attr_shadow(session_t *ps, win *w); static void -determine_shadow(Display *dpy, win *w); +determine_shadow(session_t *ps, win *w); static void -calc_win_size(Display *dpy, win *w); +calc_win_size(session_t *ps, win *w); static void -calc_shadow_geometry(Display *dpy, win *w); +calc_shadow_geometry(session_t *ps, win *w); static void -mark_client_win(Display *dpy, win *w, Window client); +mark_client_win(session_t *ps, win *w, Window client); static void -add_win(Display *dpy, Window id, Window prev, Bool override_redirect); +add_win(session_t *ps, Window id, Window prev, bool override_redirect); static void -restack_win(Display *dpy, win *w, Window new_above); +restack_win(session_t *ps, win *w, Window new_above); static void -configure_win(Display *dpy, XConfigureEvent *ce); +configure_win(session_t *ps, XConfigureEvent *ce); static void -circulate_win(Display *dpy, XCirculateEvent *ce); +circulate_win(session_t *ps, XCirculateEvent *ce); static void -finish_destroy_win(Display *dpy, Window id); +finish_destroy_win(session_t *ps, Window id); static void -destroy_callback(Display *dpy, win *w); +destroy_callback(session_t *ps, win *w); static void -destroy_win(Display *dpy, Window id, Bool fade); +destroy_win(session_t *ps, Window id); static void -damage_win(Display *dpy, XDamageNotifyEvent *de); +damage_win(session_t *ps, XDamageNotifyEvent *de); static int error(Display *dpy, XErrorEvent *ev); static void -expose_root(Display *dpy, XRectangle *rects, int nrects); +expose_root(session_t *ps, XRectangle *rects, int nrects); -static Bool -wid_get_text_prop(Display *dpy, Window wid, Atom prop, +static bool +wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr); -static Bool -wid_get_name(Display *dpy, Window w, char **name); +static bool +wid_get_name(session_t *ps, Window w, char **name); static int -win_get_name(Display *dpy, win *w); +win_get_name(session_t *ps, win *w); -static Bool -win_get_class(Display *dpy, win *w); +static bool +win_get_class(session_t *ps, win *w); #ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev); static const char * -ev_name(XEvent *ev); +ev_name(session_t *ps, XEvent *ev); static Window -ev_window(XEvent *ev); +ev_window(session_t *ps, XEvent *ev); #endif -static void +static void __attribute__ ((noreturn)) usage(void); static void -register_cm(Bool want_glxct); +register_cm(session_t *ps, bool want_glxct); inline static void -ev_focus_in(XFocusChangeEvent *ev); +ev_focus_in(session_t *ps, XFocusChangeEvent *ev); inline static void -ev_focus_out(XFocusChangeEvent *ev); +ev_focus_out(session_t *ps, XFocusChangeEvent *ev); inline static void -ev_create_notify(XCreateWindowEvent *ev); +ev_create_notify(session_t *ps, XCreateWindowEvent *ev); inline static void -ev_configure_notify(XConfigureEvent *ev); +ev_configure_notify(session_t *ps, XConfigureEvent *ev); inline static void -ev_destroy_notify(XDestroyWindowEvent *ev); +ev_destroy_notify(session_t *ps, XDestroyWindowEvent *ev); inline static void -ev_map_notify(XMapEvent *ev); +ev_map_notify(session_t *ps, XMapEvent *ev); inline static void -ev_unmap_notify(XUnmapEvent *ev); +ev_unmap_notify(session_t *ps, XUnmapEvent *ev); inline static void -ev_reparent_notify(XReparentEvent *ev); +ev_reparent_notify(session_t *ps, XReparentEvent *ev); inline static void -ev_circulate_notify(XCirculateEvent *ev); +ev_circulate_notify(session_t *ps, XCirculateEvent *ev); inline static void -ev_expose(XExposeEvent *ev); +ev_expose(session_t *ps, XExposeEvent *ev); static void -update_ewmh_active_win(Display *dpy); +update_ewmh_active_win(session_t *ps); inline static void -ev_property_notify(XPropertyEvent *ev); +ev_property_notify(session_t *ps, XPropertyEvent *ev); inline static void -ev_damage_notify(XDamageNotifyEvent *ev); +ev_damage_notify(session_t *ps, XDamageNotifyEvent *ev); inline static void -ev_shape_notify(XShapeEvent *ev); +ev_shape_notify(session_t *ps, XShapeEvent *ev); /** * Get a region of the screen size. */ inline static XserverRegion -get_screen_region(Display *dpy) { +get_screen_region(session_t *ps) { XRectangle r; r.x = 0; r.y = 0; - r.width = root_width; - r.height = root_height; - return XFixesCreateRegion(dpy, &r, 1); + r.width = ps->root_width; + r.height = ps->root_height; + return XFixesCreateRegion(ps->dpy, &r, 1); } /** * Copies a region */ inline static XserverRegion -copy_region(Display *dpy, XserverRegion oldregion) { - XserverRegion region = XFixesCreateRegion(dpy, NULL, 0); +copy_region(const session_t *ps, XserverRegion oldregion) { + XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); - XFixesCopyRegion(dpy, region, oldregion); + XFixesCopyRegion(ps->dpy, region, oldregion); return region; } @@ -1247,9 +1522,9 @@ copy_region(Display *dpy, XserverRegion oldregion) { * Dump a region. */ static inline void -dump_region(Display *dpy, XserverRegion region) { +dump_region(const session_t *ps, XserverRegion region) { int nrects = 0, i; - XRectangle *rects = XFixesFetchRegion(dpy, region, &nrects); + XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); if (!rects) return; @@ -1265,11 +1540,14 @@ dump_region(Display *dpy, XserverRegion region) { * * Keith Packard said this is slow: * http://lists.freedesktop.org/archives/xorg/2007-November/030467.html + * + * @param ps current session + * @param region region to check for */ -static inline Bool -is_region_empty(Display *dpy, XserverRegion region) { +static inline bool +is_region_empty(const session_t *ps, XserverRegion region) { int nrects = 0; - XRectangle *rects = XFixesFetchRegion(dpy, region, &nrects); + XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); XFree(rects); @@ -1279,36 +1557,49 @@ is_region_empty(Display *dpy, XserverRegion region) { /** * Add a window to damaged area. * - * @param dpy display in use + * @param ps current session * @param w struct _win element representing the window */ static inline void -add_damage_win(Display *dpy, win *w) { +add_damage_win(session_t *ps, win *w) { if (w->extents) { - add_damage(dpy, copy_region(dpy, w->extents)); + add_damage(ps, copy_region(ps, w->extents)); } } #if defined(DEBUG_EVENTS) || defined(DEBUG_RESTACK) static bool -ev_window_name(Display *dpy, Window wid, char **name); +ev_window_name(session_t *ps, Window wid, char **name); #endif inline static void -ev_handle(XEvent *ev); +ev_handle(session_t *ps, XEvent *ev); static void fork_after(void); #ifdef CONFIG_LIBCONFIG +/** + * Wrapper of libconfig's config_lookup_int. + * + * To convert int value config_lookup_bool + * returns to bool. + */ static inline void -lcfg_lookup_bool(const config_t *config, const char *path, Bool *value) { +lcfg_lookup_bool(const config_t *config, const char *path, + bool *value) { int ival; if (config_lookup_bool(config, path, &ival)) *value = ival; } +/** + * Wrapper of libconfig's config_lookup_int. + * + * To deal with the different value types config_lookup_int + * returns in libconfig-1.3 and libconfig-1.4. + */ static inline int lcfg_lookup_int(const config_t *config, const char *path, int *value) { #ifndef CONFIG_LIBCONFIG_LEGACY @@ -1328,54 +1619,66 @@ static FILE * open_config_file(char *cpath, char **path); static void -parse_config(char *cpath, struct options_tmp *pcfgtmp); +parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp); #endif static void -get_cfg(int argc, char *const *argv); +get_cfg(session_t *ps, int argc, char *const *argv); static void -get_atoms(void); +init_atoms(session_t *ps); static void -update_refresh_rate(Display *dpy); +update_refresh_rate(session_t *ps); -static Bool -sw_opti_init(void); +static bool +sw_opti_init(session_t *ps); static int -evpoll(struct pollfd *fd, int timeout); +evpoll(session_t *ps, int timeout); -static Bool -vsync_drm_init(void); +static bool +vsync_drm_init(session_t *ps); #ifdef CONFIG_VSYNC_DRM static int -vsync_drm_wait(void); +vsync_drm_wait(session_t *ps); #endif -static Bool -vsync_opengl_init(void); +static bool +vsync_opengl_init(session_t *ps); #ifdef CONFIG_VSYNC_OPENGL static void -vsync_opengl_wait(void); +vsync_opengl_wait(session_t *ps); #endif static void -vsync_wait(void); +vsync_wait(session_t *ps); + +static void +init_alpha_picts(session_t *ps); static void -init_alpha_picts(Display *dpy); +init_dbe(session_t *ps); static void -init_dbe(void); +init_overlay(session_t *ps); + +static void +redir_start(session_t *ps); + +static void +redir_stop(session_t *ps); + +static session_t * +session_init(session_t *ps_old, int argc, char **argv); static void -init_overlay(void); +session_destroy(session_t *ps); static void -redir_start(Display *dpy); +session_run(session_t *ps); static void -redir_stop(Display *dpy); +reset_enable(int __attribute__((unused)) signum); From f126bf72f869a647db9e7e434b72b269eeffa48e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 21 Nov 2012 09:15:49 +0800 Subject: [PATCH 086/178] Bug fix: Wrong file path being displayed on config file parsing error - Fix a bug that causes wrong file path being displayed on configuration file parsing error. - Continue dropping unused parameters and silence clang warnings. - Add conky shadow exclusion rule to compton.sample.conf. --- compton.c | 64 +++++++++++++++++++++++++++++++------------------------ compton.h | 35 ++++++++++++++++++------------ 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/compton.c b/compton.c index e10c4c2d2..6e2784f9e 100644 --- a/compton.c +++ b/compton.c @@ -970,7 +970,7 @@ root_tile_f(session_t *ps) { XInternAtom(ps->dpy, background_props_str[p], false), 1L, XA_PIXMAP, 32); if (prop.nitems) { - pixmap = *((long *) prop.data); + pixmap = *prop.data.p32; fill = false; free_winprop(&prop); break; @@ -1174,7 +1174,7 @@ get_frame_extents(session_t *ps, win *w, Window client) { 4L, XA_CARDINAL, 32); if (4 == prop.nitems) { - long *extents = (long *) prop.data; + const long * const extents = prop.data.p32; w->left_width = extents[0]; w->right_width = extents[1]; w->top_width = extents[2]; @@ -1192,8 +1192,9 @@ get_frame_extents(session_t *ps, win *w, Window client) { */ static inline Picture get_alpha_pict_d(session_t *ps, double o) { - assert((lround(normalize_d(o) / ps->o.alpha_step)) <= lround(1.0 / ps->o.alpha_step)); - return ps->alpha_picts[lround(normalize_d(o) / ps->o.alpha_step)]; + assert((round(normalize_d(o) / ps->o.alpha_step)) <= round(1.0 / ps->o.alpha_step)); + return ps->alpha_picts[(int) (round(normalize_d(o) + / ps->o.alpha_step))]; } /** @@ -1754,7 +1755,7 @@ wid_get_prop_wintype(session_t *ps, Window wid) { for (i = 0; i < prop.nitems; ++i) { for (j = 1; j < NUM_WINTYPES; ++j) { - if (ps->atoms_wintypes[j] == (Atom) ((long *) prop.data)[i]) { + if (ps->atoms_wintypes[j] == (Atom) prop.data.p32[i]) { free_winprop(&prop); return j; } @@ -1767,7 +1768,7 @@ wid_get_prop_wintype(session_t *ps, Window wid) { } static void -map_win(session_t *ps, Window id, bool override_redirect) { +map_win(session_t *ps, Window id) { win *w = find_win(ps, id); // Don't care about window mapping if it's an InputOnly window @@ -1938,7 +1939,7 @@ wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def) { XA_CARDINAL, 32); if (prop.nitems) - val = *((long *) prop.data); + val = *prop.data.p32; free_winprop(&prop); @@ -2105,7 +2106,7 @@ win_update_attr_shadow_raw(session_t *ps, win *w) { w->attr_shadow = -1; } else { - w->attr_shadow = *((long *) prop.data); + w->attr_shadow = *prop.data.p32; } free_winprop(&prop); @@ -2216,7 +2217,7 @@ mark_client_win(session_t *ps, win *w, Window client) { } static void -add_win(session_t *ps, Window id, Window prev, bool override_redirect) { +add_win(session_t *ps, Window id, Window prev) { // Reject overlay window and already added windows if (id == ps->overlay || find_win(ps, id)) { return; @@ -2311,7 +2312,7 @@ add_win(session_t *ps, Window id, Window prev, bool override_redirect) { *p = new; if (new->a.map_state == IsViewable) { - map_win(ps, id, override_redirect); + map_win(ps, id); } } @@ -2550,7 +2551,7 @@ damage_win(session_t *ps, XDamageNotifyEvent *de) { * Xlib error handler function. */ static int -error(Display *dpy, XErrorEvent *ev) { +error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { session_t * const ps = ps_g; int o; @@ -2922,7 +2923,7 @@ ev_focus_out(session_t *ps, XFocusChangeEvent *ev) { inline static void ev_create_notify(session_t *ps, XCreateWindowEvent *ev) { assert(ev->parent == ps->root); - add_win(ps, ev->window, 0, ev->override_redirect); + add_win(ps, ev->window, 0); } inline static void @@ -2943,7 +2944,7 @@ ev_destroy_notify(session_t *ps, XDestroyWindowEvent *ev) { inline static void ev_map_notify(session_t *ps, XMapEvent *ev) { - map_win(ps, ev->window, ev->override_redirect); + map_win(ps, ev->window); } inline static void @@ -2954,7 +2955,7 @@ ev_unmap_notify(session_t *ps, XUnmapEvent *ev) { inline static void ev_reparent_notify(session_t *ps, XReparentEvent *ev) { if (ev->parent == ps->root) { - add_win(ps, ev->window, 0, ev->override_redirect); + add_win(ps, ev->window, 0); } else { destroy_win(ps, ev->window); // Reset event mask in case something wrong happens @@ -3019,7 +3020,7 @@ update_ewmh_active_win(session_t *ps) { } // Search for the window - Window wid = *((long *) prop.data); + Window wid = *prop.data.p32; win *w = NULL; free_winprop(&prop); @@ -3144,7 +3145,8 @@ ev_shape_notify(session_t *ps, XShapeEvent *ev) { * Handle ScreenChangeNotify events from X RandR extension. */ static void -ev_screen_change_notify(session_t *ps, XRRScreenChangeNotifyEvent *ev) { +ev_screen_change_notify(session_t *ps, + XRRScreenChangeNotifyEvent __attribute__((unused)) *ev) { if (!ps->o.refresh_rate) { update_refresh_rate(ps); if (!ps->refresh_rate) { @@ -3270,7 +3272,7 @@ ev_handle(session_t *ps, XEvent *ev) { */ static void usage(void) { - fputs( + const static char *usage_text = "compton (development version)\n" "usage: compton [options]\n" "Options:\n" @@ -3394,8 +3396,8 @@ usage(void) { " could be a series of flags. Currently the only defined\n" " flag is \"i\" (ignore case).\n" "\n" - " is the actual pattern string.\n" - , stderr); + " is the actual pattern string.\n"; + fputs(usage_text , stderr); exit(1); } @@ -3642,9 +3644,17 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { config_init(&cfg); #ifndef CONFIG_LIBCONFIG_LEGACY - char *parent = dirname(path); - if (parent) - config_set_include_dir(&cfg, parent); + { + // dirname() could modify the original string, thus we must pass a + // copy + char *path2 = mstrcpy(path); + char *parent = dirname(path2); + + if (parent) + config_set_include_dir(&cfg, parent); + + free(path2); + } #endif if (CONFIG_FALSE == config_read(&cfg, f)) { @@ -4367,7 +4377,7 @@ vsync_wait(session_t *ps) { static void init_alpha_picts(session_t *ps) { int i; - int num = lround(1.0 / ps->o.alpha_step) + 1; + int num = round(1.0 / ps->o.alpha_step) + 1; ps->alpha_picts = malloc(sizeof(Picture) * num); @@ -4620,8 +4630,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .atoms_wintypes = { 0 } }; - int i; - // Allocate a session and copy default values into it session_t *ps = malloc(sizeof(session_t)); memcpy(ps, &s_def, sizeof(session_t)); @@ -4812,8 +4820,8 @@ session_init(session_t *ps_old, int argc, char **argv) { XQueryTree(ps->dpy, ps->root, &root_return, &parent_return, &children, &nchildren); - for (i = 0; i < nchildren; i++) { - add_win(ps, children[i], i ? children[i-1] : None, false); + for (unsigned i = 0; i < nchildren; i++) { + add_win(ps, children[i], i ? children[i-1] : None); } XFree(children); @@ -4867,7 +4875,7 @@ session_destroy(session_t *ps) { // Free alpha_picts { - const int max = lround(1.0 / ps->o.alpha_step) + 1; + const int max = round(1.0 / ps->o.alpha_step) + 1; for (int i = 0; i < max; ++i) free_picture(ps, &ps->alpha_picts[i]); free(ps->alpha_picts); diff --git a/compton.h b/compton.h index 51f59eb98..ed3250065 100644 --- a/compton.h +++ b/compton.h @@ -149,7 +149,13 @@ typedef enum { } winmode; typedef struct { - unsigned char *data; + // All pointers have the same length, right? + // I wanted to use anonymous union but it's a GNU extension... + union { + unsigned char *p8; + short *p16; + long *p32; + } data; unsigned long nitems; } winprop_t; @@ -397,7 +403,7 @@ typedef struct { /// Currently used refresh rate. short refresh_rate; /// Interval between refresh in nanoseconds. - unsigned long refresh_intv; + long refresh_intv; /// Nanosecond offset of the first painting. long paint_tm_offset; @@ -844,13 +850,13 @@ timeval_subtract(struct timeval *result, struct timeval *y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_usec < y->tv_usec) { - int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { - int nsec = (x->tv_usec - y->tv_usec) / 1000000; + long nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } @@ -879,13 +885,13 @@ timespec_subtract(struct timespec *result, struct timespec *y) { /* Perform the carry for the later subtraction by updating y. */ if (x->tv_nsec < y->tv_nsec) { - int nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; + long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; y->tv_nsec -= NS_PER_SEC * nsec; y->tv_sec += nsec; } if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { - int nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; + long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; y->tv_nsec += NS_PER_SEC * nsec; y->tv_sec -= nsec; } @@ -1036,7 +1042,8 @@ get_time_ms(void) { gettimeofday(&tv, NULL); - return tv.tv_sec * 1000 + tv.tv_usec / 1000; + return (unsigned long) tv.tv_sec * 1000 + + (unsigned long) tv.tv_usec / 1000; } static int @@ -1144,7 +1151,7 @@ wid_get_prop(const session_t *ps, Window w, Atom atom, long length, rtype, &type, &format, &nitems, &after, &data)) { if (type == rtype && format == rformat) { return (winprop_t) { - .data = data, + .data.p8 = data, .nitems = nitems }; } @@ -1153,7 +1160,7 @@ wid_get_prop(const session_t *ps, Window w, Atom atom, long length, XFree(data); return (winprop_t) { - .data = NULL, + .data.p8 = NULL, .nitems = 0 }; } @@ -1166,9 +1173,9 @@ wid_get_prop(const session_t *ps, Window w, Atom atom, long length, static inline void free_winprop(winprop_t *pprop) { // Empty the whole structure to avoid possible issues - if (pprop->data) { - XFree(pprop->data); - pprop->data = NULL; + if (pprop->data.p8) { + XFree(pprop->data.p8); + pprop->data.p8 = NULL; } pprop->nitems = 0; } @@ -1327,7 +1334,7 @@ static wintype_t wid_get_prop_wintype(session_t *ps, Window w); static void -map_win(session_t *ps, Window id, bool override_redirect); +map_win(session_t *ps, Window id); static void finish_map_win(session_t *ps, win *w); @@ -1391,7 +1398,7 @@ static void mark_client_win(session_t *ps, win *w, Window client); static void -add_win(session_t *ps, Window id, Window prev, bool override_redirect); +add_win(session_t *ps, Window id, Window prev); static void restack_win(session_t *ps, win *w, Window new_above); From 722252851570be312d369767cce119eac63db63c Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 27 Nov 2012 00:02:18 +0800 Subject: [PATCH 087/178] Bug fix: --use-ewmh-active-win causes wrong focus state in Awesome - Fix a bug that causes wrong focus detection result in Awesome and maybe other window managers, when --use-ewmh-active-win is enabled and _NET_ACTIVE_WINDOW changes before the newly-focused window is mapped. - Fix a typo that causes more than one window to stay focused after a window destruction with --use-ewmh-active-win. - Fix a bug that find_clientwin() incorrectly returns a window when window ID 0 is passed to it. - Check for window ID 0 in update_ewmh_active_win(). --- compton.c | 13 +++++++++++-- compton.h | 7 +++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/compton.c b/compton.c index 6e2784f9e..045c536c7 100644 --- a/compton.c +++ b/compton.c @@ -849,6 +849,9 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { */ static win * find_win(session_t *ps, Window id) { + if (!id) + return NULL; + win *w; for (w = ps->list; w; w = w->next) { @@ -867,6 +870,9 @@ find_win(session_t *ps, Window id) { */ static win * find_toplevel(session_t *ps, Window id) { + if (!id) + return NULL; + win *w; for (w = ps->list; w; w = w->next) { @@ -1775,6 +1781,9 @@ map_win(session_t *ps, Window id) { if (!w || InputOnly == w->a.class) return; w->focused = false; + if (ps->o.use_ewmh_active_win && w == ps->active_win) + w->focused = true; + w->a.map_state = IsViewable; // Call XSelectInput() before reading properties so that no property @@ -2484,7 +2493,7 @@ finish_destroy_win(session_t *ps, Window id) { *prev = w->next; // Clear active_win if it's pointing to the destroyed window - if (ps->active_win) + if (w == ps->active_win) ps->active_win = NULL; free_win_res(ps, w); @@ -3024,7 +3033,7 @@ update_ewmh_active_win(session_t *ps) { win *w = NULL; free_winprop(&prop); - if (!(w = find_toplevel(ps, wid))) + if (wid && !(w = find_toplevel(ps, wid))) if (!(w = find_win(ps, wid))) w = find_toplevel2(ps, wid); diff --git a/compton.h b/compton.h index ed3250065..de4d84e5e 100644 --- a/compton.h +++ b/compton.h @@ -378,8 +378,11 @@ typedef struct { // === Window related === /// Linked list of all windows. struct _win *list; - /// Current active window. Used by EWMH _NET_ACTIVE_WINDOW focus - /// detection. + /// Pointer to win of current active window. Used by + /// EWMH _NET_ACTIVE_WINDOW focus detection. In theory, + /// it's more reliable to store the window ID directly here, just in + /// case the WM does something extraordinary, but caching the pointer + /// means another layer of complexity. struct _win *active_win; // === Shadow/dimming related === From c3c0b95c6420a71ae399b36b363bf4ff7d30f9b9 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 28 Nov 2012 11:44:00 +0800 Subject: [PATCH 088/178] Feature #65: --focus-exclude - Add --focus-exclude, which should be used with a list of conditions to set certain windows to always be considered focused. - Change focus determination process, with a newly added w->focused_real that reflects whether the window is actually focused, while w->focused represents whether compton considers it focused. The primary difference is now when a window considered focused because of --mark-wmwin-focused or --mark-ovredir-focused receives a FocusOut event, it's still considered focused by compton. - Change opacity target and dim state calculation so that it's done at most once every paint. - Split window opacity property fetching from calc_opacity() to a new function win_update_opacity_prop(). - Silence a warning in wid_get_prop_wintype(). - Rename a few functions. Code clean-up. - My time is very limited currently, few tests are done, so this commit may very well introduce bugs. - Known issue: Dim picture opacity does not change based on window opacity, causing somehow annoying effects when a window fades off. --- compton.c | 178 +++++++++++++++++++++++++++++------------------------- compton.h | 62 +++++++++++++++++-- 2 files changed, 151 insertions(+), 89 deletions(-) diff --git a/compton.c b/compton.c index 045c536c7..4026a583b 100644 --- a/compton.c +++ b/compton.c @@ -948,7 +948,7 @@ recheck_focus(session_t *ps) { // And we set the focus state and opacity here if (w) { - set_focused(ps, w, true); + win_set_focused(ps, w, true); return w; } @@ -1241,6 +1241,12 @@ paint_preprocess(session_t *ps, win *list) { if (ps->reg_ignore_expire) free_region(ps, &w->reg_ignore); + // Update window opacity target and dim state if asked + if (WFLAG_OPCT_CHANGE & w->flags) { + calc_opacity(ps, w); + calc_dim(ps, w); + } + // Run fading run_fade(ps, w, steps); @@ -1754,13 +1760,13 @@ repair_win(session_t *ps, win *w) { static wintype_t wid_get_prop_wintype(session_t *ps, Window wid) { - int i, j; + int i; set_ignore_next(ps); winprop_t prop = wid_get_prop(ps, wid, ps->atom_win_type, 32L, XA_ATOM, 32); for (i = 0; i < prop.nitems; ++i) { - for (j = 1; j < NUM_WINTYPES; ++j) { + for (wintype_t j = 1; j < NUM_WINTYPES; ++j) { if (ps->atoms_wintypes[j] == (Atom) prop.data.p32[i]) { free_winprop(&prop); return j; @@ -1780,9 +1786,10 @@ map_win(session_t *ps, Window id) { // Don't care about window mapping if it's an InputOnly window if (!w || InputOnly == w->a.class) return; - w->focused = false; - if (ps->o.use_ewmh_active_win && w == ps->active_win) - w->focused = true; + w->focused_real = false; + if (ps->o.track_focus && ps->o.use_ewmh_active_win + && w == ps->active_win) + w->focused_real = true; w->a.map_state = IsViewable; @@ -1841,23 +1848,22 @@ map_win(session_t *ps, Window id) { win_get_class(ps, w); } - if (ps->o.track_focus) { - // Occasionally compton does not seem able to get a FocusIn event from - // a window just mapped. I suspect it's a timing issue again when the - // XSelectInput() is called too late. We have to recheck the focused - // window here. It makes no sense if we are using EWMH - // _NET_ACTIVE_WINDOW. - if (!ps->o.use_ewmh_active_win) - recheck_focus(ps); - - // Consider a window without client window a WM window and mark it - // focused if mark_wmwin_focused is on, or it's over-redirected and - // mark_ovredir_focused is on - if ((ps->o.mark_wmwin_focused && !w->client_win) - || (ps->o.mark_ovredir_focused && w->id == w->client_win)) - w->focused = true; + // Occasionally compton does not seem able to get a FocusIn event from + // a window just mapped. I suspect it's a timing issue again when the + // XSelectInput() is called too late. We have to recheck the focused + // window here. It makes no sense if we are using EWMH + // _NET_ACTIVE_WINDOW. + if (ps->o.track_focus && !ps->o.use_ewmh_active_win) { + recheck_focus(ps); } + // Update window focus state + win_update_focused(ps, w); + + // Update opacity and dim state + win_update_opacity_prop(ps, w); + w->flags |= WFLAG_OPCT_CHANGE; + // Check for _COMPTON_SHADOW if (ps->o.respect_prop_shadow) win_update_attr_shadow_raw(ps, w); @@ -1865,9 +1871,6 @@ map_win(session_t *ps, Window id) { // Many things above could affect shadow determine_shadow(ps, w); - // Fading in - calc_opacity(ps, w, true); - // Set fading state if (ps->o.no_fading_openclose) { set_fade_callback(ps, w, finish_map_win, true); @@ -1880,8 +1883,6 @@ map_win(session_t *ps, Window id) { determine_fade(ps, w); } - calc_dim(ps, w); - w->damaged = 1; @@ -1931,7 +1932,7 @@ unmap_win(session_t *ps, Window id) { w->a.map_state = IsUnmapped; // Fading out - w->opacity_tgt = 0; + w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); if (ps->o.no_fading_openclose) w->fade = false; @@ -2006,35 +2007,23 @@ determine_mode(session_t *ps, win *w) { * refetched */ static void -calc_opacity(session_t *ps, win *w, bool refetch_prop) { - opacity_t opacity; - - // Do nothing for unmapped window, calc_opacity() will be called - // when it's mapped - // I suppose I need not to check for IsUnviewable here? - if (IsViewable != w->a.map_state) return; - - // Do not refetch the opacity window attribute unless necessary, this - // is probably an expensive operation in some cases - if (refetch_prop) { - w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); - if (!ps->o.detect_client_opacity || !w->client_win - || w->id == w->client_win) - w->opacity_prop_client = OPAQUE; - else - w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, - OPAQUE); - } +calc_opacity(session_t *ps, win *w) { + opacity_t opacity = OPAQUE; - if (OPAQUE == (opacity = w->opacity_prop) - && OPAQUE == (opacity = w->opacity_prop_client)) { - opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; - } + if (w->destroyed || IsViewable != w->a.map_state) + opacity = 0; + else { + // Try obeying opacity property and window type opacity firstly + if (OPAQUE == (opacity = w->opacity_prop) + && OPAQUE == (opacity = w->opacity_prop_client)) { + opacity = ps->o.wintype_opacity[w->window_type] * OPAQUE; + } - // Respect inactive_opacity in some cases - if (ps->o.inactive_opacity && is_normal_win(w) && false == w->focused - && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { - opacity = ps->o.inactive_opacity; + // Respect inactive_opacity in some cases + if (ps->o.inactive_opacity && is_normal_win(w) && false == w->focused + && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { + opacity = ps->o.inactive_opacity; + } } w->opacity_tgt = opacity; @@ -2047,6 +2036,10 @@ static void calc_dim(session_t *ps, win *w) { bool dim; + // Make sure we do nothing if the window is unmapped / destroyed + if (w->destroyed || IsViewable != w->a.map_state) + return; + if (ps->o.inactive_dim && is_normal_win(w) && !(w->focused)) { dim = true; } else { @@ -2273,6 +2266,7 @@ add_win(session_t *ps, Window id, Window prev) { new->class_general = NULL; new->cache_sblst = NULL; new->cache_fblst = NULL; + new->cache_fcblst = NULL; new->bounding_shaped = false; new->rounded_corners = false; @@ -2298,6 +2292,7 @@ add_win(session_t *ps, Window id, Window prev) { new->frame_alpha_pict = None; new->dim = false; new->focused = false; + new->focused_real = false; new->destroyed = false; new->need_configure = false; new->window_type = WINTYPE_UNKNOWN; @@ -2517,7 +2512,7 @@ destroy_win(session_t *ps, Window id) { w->destroyed = true; // Fading out the window - w->opacity_tgt = 0; + w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, destroy_callback, false); } } @@ -2909,7 +2904,7 @@ ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { // To deal with events sent from windows just destroyed if (!w) return; - set_focused(ps, w, true); + win_set_focused(ps, w, true); } inline static void @@ -2926,7 +2921,7 @@ ev_focus_out(session_t *ps, XFocusChangeEvent *ev) { // To deal with events sent from windows just destroyed if (!w) return; - set_focused(ps, w, false); + win_set_focused(ps, w, false); } inline static void @@ -3039,10 +3034,9 @@ update_ewmh_active_win(session_t *ps) { // Mark the window focused if (w) { - if (!w->focused) - set_focused(ps, w, true); + win_set_focused(ps, w, true); if (ps->active_win && w != ps->active_win) - set_focused(ps, ps->active_win, false); + win_set_focused(ps, ps->active_win, false); ps->active_win = w; } } @@ -3079,7 +3073,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, OPAQUE); if (w) { - calc_opacity(ps, w, false); + w->flags |= WFLAG_OPCT_CHANGE; } } @@ -3097,8 +3091,10 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (ps->o.track_wdata && (ps->atom_name == ev->atom || ps->atom_name_ewmh == ev->atom)) { win *w = find_toplevel(ps, ev->window); - if (w && 1 == win_get_name(ps, w)) + if (w && 1 == win_get_name(ps, w)) { determine_shadow(ps, w); + win_update_focused(ps, w); + } } // If class changes @@ -3107,6 +3103,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (w) { win_get_class(ps, w); determine_shadow(ps, w); + win_update_focused(ps, w); } } @@ -3354,7 +3351,6 @@ usage(void) { " Detect _NET_WM_OPACITY on client windows, useful for window\n" " managers not passing _NET_WM_OPACITY of client windows to frame\n" " windows.\n" - "\n" "--refresh-rate val\n" " Specify refresh rate of the screen. If not specified or 0, compton\n" " will try detecting this with X RandR extension.\n" @@ -3390,6 +3386,9 @@ usage(void) { " Unredirect all windows if a full-screen opaque window is\n" " detected, to maximize performance for full-screen windows.\n" " Experimental.\n" + "--focus-exclude condition\n" + " Specify a list of conditions of windows that should always be\n" + " considered focused.\n" "\n" "Format of a condition:\n" "\n" @@ -3632,6 +3631,28 @@ parse_vsync(session_t *ps, const char *optarg) { } } +/** + * Parse a condition list in configuration file. + */ +static void +parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, + const char *name) { + config_setting_t *setting = config_lookup(pcfg, name); + if (setting) { + // Parse an array of options + if (config_setting_is_array(setting)) { + int i = config_setting_length(setting); + while (i--) { + condlst_add(pcondlst, config_setting_get_string_elem(setting, i)); + } + } + // Treat it as a single pattern if it's a string + else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { + condlst_add(pcondlst, config_setting_get_string(setting)); + } + } +} + /** * Parse a configuration file from default location. */ @@ -3763,25 +3784,9 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { lcfg_lookup_bool(&cfg, "unredir-if-possible", &ps->o.unredir_if_possible); // --shadow-exclude - { - config_setting_t *setting = - config_lookup(&cfg, "shadow-exclude"); - if (setting) { - // Parse an array of shadow-exclude - if (config_setting_is_array(setting)) { - int i = config_setting_length(setting); - while (i--) { - condlst_add(&ps->o.shadow_blacklist, - config_setting_get_string_elem(setting, i)); - } - } - // Treat it as a single pattern if it's a string - else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { - condlst_add(&ps->o.shadow_blacklist, - config_setting_get_string(setting)); - } - } - } + parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude"); + // --focus-exclude + parse_cfg_condlst(&cfg, &ps->o.focus_blacklist, "focus-exclude"); // Wintype settings { wintype_t i; @@ -3835,6 +3840,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "use-ewmh-active-win", no_argument, NULL, 276 }, { "respect-prop-shadow", no_argument, NULL, 277 }, { "unredir-if-possible", no_argument, NULL, 278 }, + { "focus-exclude", required_argument, NULL, 279 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4036,6 +4042,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --unredir-if-possible ps->o.unredir_if_possible = true; break; + case 279: + // --focus-exclude + condlst_add(&ps->o.focus_blacklist, optarg); + break; default: usage(); } @@ -4082,7 +4092,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) { } // Determine whether we need to track window name and class - if (ps->o.shadow_blacklist || ps->o.fade_blacklist) + if (ps->o.shadow_blacklist || ps->o.fade_blacklist + || ps->o.focus_blacklist) ps->o.track_wdata = true; } @@ -4564,6 +4575,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_dim = 0.0, .alpha_step = 0.03, .use_ewmh_active_win = false, + .focus_blacklist = NULL, .track_focus = false, .track_wdata = false, diff --git a/compton.h b/compton.h index de4d84e5e..00f6349e1 100644 --- a/compton.h +++ b/compton.h @@ -116,6 +116,8 @@ #define WFLAG_SIZE_CHANGE 0x0001 // Window size/position is changed #define WFLAG_POS_CHANGE 0x0002 +// Window opacity / dim state changed +#define WFLAG_OPCT_CHANGE 0x0004 /** * Types @@ -290,8 +292,12 @@ typedef struct { double inactive_dim; /// Step for pregenerating alpha pictures. 0.01 - 1.0. double alpha_step; + + // === Focus related === /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. bool use_ewmh_active_win; + /// A list of windows always to be considered focused. + wincond_t *focus_blacklist; // === Calculated === /// Whether compton needs to track focus changes. @@ -512,8 +518,10 @@ typedef struct _win { XserverRegion extents; // Type of the window. wintype_t window_type; - /// Whether the window is focused. + /// Whether the window is to be considered focused. bool focused; + /// Whether the window is actually focused. + bool focused_real; /// Whether the window has been destroyed. bool destroyed; /// Cached width/height of the window including border. @@ -531,6 +539,7 @@ typedef struct _win { char *class_general; wincond_t *cache_sblst; wincond_t *cache_fblst; + wincond_t *cache_fcblst; // Opacity-related members /// Current window opacity. @@ -1354,6 +1363,20 @@ unmap_win(session_t *ps, Window id); static opacity_t wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); +/** + * Reread opacity property of a window. + */ +static inline void +win_update_opacity_prop(session_t *ps, win *w) { + w->opacity_prop = wid_get_opacity_prop(ps, w->id, OPAQUE); + if (!ps->o.detect_client_opacity || !w->client_win + || w->id == w->client_win) + w->opacity_prop_client = OPAQUE; + else + w->opacity_prop_client = wid_get_opacity_prop(ps, w->client_win, + OPAQUE); +} + static double get_opacity_percent(win *w); @@ -1361,16 +1384,39 @@ static void determine_mode(session_t *ps, win *w); static void -calc_opacity(session_t *ps, win *w, bool refetch_prop); +calc_opacity(session_t *ps, win *w); static void calc_dim(session_t *ps, win *w); +/** + * Update focused state of a window. + */ +static inline void +win_update_focused(session_t *ps, win *w) { + bool focused_old = w->focused; + + w->focused = w->focused_real; + + // Consider a window without client window a WM window and mark it + // focused if mark_wmwin_focused is on, or it's over-redirected and + // mark_ovredir_focused is on + if ((ps->o.mark_wmwin_focused && !w->client_win) + || (ps->o.mark_ovredir_focused && w->id == w->client_win) + || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) + w->focused = true; + + if (w->focused != focused_old) + w->flags |= WFLAG_OPCT_CHANGE; +} + +/** + * Set real focused state of a window. + */ static inline void -set_focused(session_t *ps, win *w, bool focused) { - w->focused = focused; - calc_opacity(ps, w, false); - calc_dim(ps, w); +win_set_focused(session_t *ps, win *w, bool focused) { + w->focused_real = focused; + win_update_focused(ps, w); } static void @@ -1628,6 +1674,10 @@ lcfg_lookup_int(const config_t *config, const char *path, int *value) { static FILE * open_config_file(char *cpath, char **path); +static void +parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, + const char *name); + static void parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp); #endif From 1a1df74595934717760cb4fb6904cad2e0106f8f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 5 Dec 2012 12:43:34 +0800 Subject: [PATCH 089/178] Bug fix: Dynamic inactive dim & client window changes - Make inactive_dim opacity change according to window opacity by default, so it looks better when fading. --inactive-dim-fixed restores the old behavior. - Make client window default to the window itself. Add w->wmwin to represent whether the window looks like a WM window instead. A side effect is, the window type of WM windows are changed from unknown to normal, making it necessary to use --mark-wmwin-active to avoid considering them unfocused. - Add check of PointerRoot to recheck_focus() to avoid BadWindow errors when the root window is focused. - Add a few macros to simplify debugging code. - Add DEBUG_FRAME. --- compton.c | 98 +++++++++++++++++++++++++++++++++++++------------------ compton.h | 47 ++++++++++++++------------ 2 files changed, 93 insertions(+), 52 deletions(-) diff --git a/compton.c b/compton.c index 4026a583b..39ac3e7a0 100644 --- a/compton.c +++ b/compton.c @@ -15,6 +15,13 @@ // #define MSTR_(s) #s // #define MSTR(s) MSTR_(s) +#define printf_dbg(format, ...) \ + printf(format, ## __VA_ARGS__); \ + fflush(stdout) + +#define printf_dbgf(format, ...) \ + printf_dbg("%s" format, __func__, ## __VA_ARGS__) + // Use #s here to prevent macro expansion /// Macro used for shortening some debugging code. #define CASESTRRET(s) case s: return #s @@ -497,9 +504,9 @@ solid_picture(session_t *ps, bool argb, double a, } c.alpha = a * 0xffff; - c.red = r * 0xffff; + c.red = r * 0xffff; c.green = g * 0xffff; - c.blue = b * 0xffff; + c.blue = b * 0xffff; XRenderFillRectangle(ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); XFreePixmap(ps->dpy, pixmap); @@ -941,6 +948,9 @@ recheck_focus(session_t *ps) { XGetInputFocus(ps->dpy, &wid, &revert_to); + if (!wid || PointerRoot == wid) + return NULL; + // Fallback to the old method if find_toplevel() fails if (!(w = find_toplevel(ps, wid))) { w = find_toplevel2(ps, wid); @@ -989,7 +999,7 @@ root_tile_f(session_t *ps) { fill = true; } - pa.repeat = true; + pa.repeat = True; picture = XRenderCreatePicture( ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis), CPRepeat, &pa); @@ -1190,6 +1200,11 @@ get_frame_extents(session_t *ps, win *w, Window client) { update_reg_ignore_expire(ps, w); } +#ifdef DEBUG_FRAME + printf_dbgf("(%#010lx): %d, %d, %d, %d\n", w->id, + w->left_width, w->right_width, w->top_width, w->bottom_width); +#endif + free_winprop(&prop); } @@ -1323,6 +1338,16 @@ paint_preprocess(session_t *ps, win *list) { } w->shadow_alpha_pict = get_alpha_pict_d(ps, w->shadow_opacity); + + // Dimming preprocessing + if (w->dim) { + double dim_opacity = ps->o.inactive_dim; + if (!ps->o.inactive_dim_fixed) + dim_opacity *= get_opacity_percent(w); + + w->dim_alpha_pict = get_alpha_pict_d(ps, dim_opacity); + } + } if ((to_paint && WINDOW_SOLID == w->mode) @@ -1512,9 +1537,9 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { #undef COMP_BDR // Dimming the window if needed - if (w->dim) { - XRenderComposite(ps->dpy, PictOpOver, ps->dim_picture, None, - tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + if (w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) { + XRenderComposite(ps->dpy, PictOpOver, ps->black_picture, + w->dim_alpha_pict, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } } @@ -1802,41 +1827,43 @@ map_win(session_t *ps, Window id) { XShapeSelectInput(ps->dpy, id, ShapeNotifyMask); } + // Make sure the XSelectInput() requests are sent + XSync(ps->dpy, False); + // Detect client window here instead of in add_win() as the client // window should have been prepared at this point if (!w->client_win) { - Window cw = 0; + w->wmwin = false; + + Window cw = None; // Always recursively look for a window with WM_STATE, as Fluxbox // sets override-redirect flags on all frame windows. cw = find_client_win(ps, w->id); #ifdef DEBUG_CLIENTWIN - printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); + if (cw) + printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); #endif - // Set a window's client window to itself only if we didn't find a - // client window and the window has override-redirect flag - if (!cw && w->a.override_redirect) { + // Set a window's client window to itself if we couldn't find a + // client window + if (!cw) { cw = w->id; + w->wmwin = !w->a.override_redirect; #ifdef DEBUG_CLIENTWIN - printf("find_client_win(%#010lx): client self (override-redirected)\n", w->id); + printf("find_client_win(%#010lx): client self (%s)\n", w->id, + (w->wmwin ? "wmwin": "override-redirected")); #endif } - if (cw) { - mark_client_win(ps, w, cw); - } + mark_client_win(ps, w, cw); } else { // Re-mark client window here mark_client_win(ps, w, w->client_win); } - // Workaround for _NET_WM_WINDOW_TYPE for Openbox menus, which is - // set on a non-override-redirect window with no WM_STATE either - if (!w->client_win && WINTYPE_UNKNOWN == w->window_type) - w->window_type = wid_get_prop_wintype(ps, w->id); + assert(w->client_win); #ifdef DEBUG_WINTYPE - printf("map_win(%#010lx): type %s\n", - w->id, WINTYPES[w->window_type]); + printf_dbgf("(%#010lx): type %s\n", w->id, WINTYPES[w->window_type]); #endif // Detect if the window is shaped or has rounded corners @@ -2290,7 +2317,10 @@ add_win(session_t *ps, Window id, Window prev) { new->alpha_pict = None; new->frame_opacity = 1.0; new->frame_alpha_pict = None; + new->dim = false; + new->dim_alpha_pict = None; + new->focused = false; new->focused_real = false; new->destroyed = false; @@ -2307,6 +2337,7 @@ add_win(session_t *ps, Window id, Window prev) { new->bottom_width = 0; new->client_win = 0; + new->wmwin = false; new->flags = 0; @@ -2358,7 +2389,7 @@ restack_win(session_t *ps, win *w, Window new_above) { bool to_free; win* c = ps->list; - printf("restack_win(%#010lx, %#010lx): " + printf_dbgf("(%#010lx, %#010lx): " "Window stack modified. Current stack:\n", w->id, new_above); for (; c; c = c->next) { @@ -2665,7 +2696,7 @@ wid_get_name(session_t *ps, Window wid, char **name) { && text_prop.value)) { // set_ignore_next(ps); #ifdef DEBUG_WINDATA - printf("wid_get_name(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); + printf_dbgf("(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); #endif if (!(XGetWMName(ps->dpy, wid, &text_prop) && text_prop.value)) { @@ -2716,7 +2747,7 @@ win_get_name(session_t *ps, win *w) { free(name_old); #ifdef DEBUG_WINDATA - printf("win_get_name(%#010lx): client = %#010lx, name = \"%s\", " + printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", " "ret = %d\n", w->id, w->client_win, w->name, ret); #endif @@ -2755,7 +2786,7 @@ win_get_class(session_t *ps, win *w) { XFreeStringList(strlst); #ifdef DEBUG_WINDATA - printf("win_get_class(%#010lx): client = %#010lx, " + printf_dbgf("(%#010lx): client = %#010lx, " "instance = \"%s\", general = \"%s\"\n", w->id, w->client_win, w->class_instance, w->class_general); #endif @@ -3389,6 +3420,8 @@ usage(void) { "--focus-exclude condition\n" " Specify a list of conditions of windows that should always be\n" " considered focused.\n" + "--inactive-dim-fixed\n" + " Use fixed inactive dim value.\n" "\n" "Format of a condition:\n" "\n" @@ -3783,6 +3816,8 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { // --unredir-if-possible lcfg_lookup_bool(&cfg, "unredir-if-possible", &ps->o.unredir_if_possible); + // --inactive-dim-fixed + lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &ps->o.inactive_dim_fixed); // --shadow-exclude parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude"); // --focus-exclude @@ -3841,6 +3876,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "respect-prop-shadow", no_argument, NULL, 277 }, { "unredir-if-possible", no_argument, NULL, 278 }, { "focus-exclude", required_argument, NULL, 279 }, + { "inactive-dim-fixed", no_argument, NULL, 280 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4046,6 +4082,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --focus-exclude condlst_add(&ps->o.focus_blacklist, optarg); break; + case 280: + // --inactive-dim-fixed + ps->o.inactive_dim_fixed = true; + break; default: usage(); } @@ -4573,6 +4613,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .frame_opacity = 0.0, .detect_client_opacity = false, .inactive_dim = 0.0, + .inactive_dim_fixed = false, .alpha_step = 0.03, .use_ewmh_active_win = false, .focus_blacklist = NULL, @@ -4600,7 +4641,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .list = NULL, .active_win = NULL, .black_picture = None, - .dim_picture = None, .cshadow_picture = None, .gaussian_map = NULL, .cgsize = 0, @@ -4817,11 +4857,6 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue); } - // Generates a picture for inactive_dim - if (ps->o.inactive_dim) { - ps->dim_picture = solid_picture(ps, true, ps->o.inactive_dim, 0, 0, 0); - } - ps->all_damage = None; XGrabServer(ps->dpy); @@ -4943,7 +4978,6 @@ session_destroy(session_t *ps) { free_picture(ps, &ps->root_picture); // Free other X resources - free_picture(ps, &ps->dim_picture); free_picture(ps, &ps->root_tile); free_region(ps, &ps->screen_reg); free_region(ps, &ps->all_damage); diff --git a/compton.h b/compton.h index 00f6349e1..60cb52187 100644 --- a/compton.h +++ b/compton.h @@ -7,16 +7,17 @@ // === Options === // Debug options, enable them using -D in CFLAGS -// #define DEBUG_REPAINT 1 -// #define DEBUG_EVENTS 1 -// #define DEBUG_RESTACK 1 -// #define DEBUG_WINTYPE 1 -// #define DEBUG_CLIENTWIN 1 -// #define DEBUG_WINDATA 1 -// #define DEBUG_WINMATCH 1 -// #define DEBUG_REDIR 1 -// #define DEBUG_ALLOC_REG 1 -// #define MONITOR_REPAINT 1 +// #define DEBUG_REPAINT 1 +// #define DEBUG_EVENTS 1 +// #define DEBUG_RESTACK 1 +// #define DEBUG_WINTYPE 1 +// #define DEBUG_CLIENTWIN 1 +// #define DEBUG_WINDATA 1 +// #define DEBUG_WINMATCH 1 +// #define DEBUG_REDIR 1 +// #define DEBUG_ALLOC_REG 1 +// #define DEBUG_FRAME 1 +// #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled // by default @@ -119,9 +120,7 @@ // Window opacity / dim state changed #define WFLAG_OPCT_CHANGE 0x0004 -/** - * Types - */ +// === Types === typedef uint32_t opacity_t; @@ -290,6 +289,9 @@ typedef struct { bool detect_client_opacity; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; + /// Whether to use fixed inactive dim opacity, instead of deciding + /// based on window opacity. + bool inactive_dim_fixed; /// Step for pregenerating alpha pictures. 0.01 - 1.0. double alpha_step; @@ -394,8 +396,6 @@ typedef struct { // === Shadow/dimming related === /// 1x1 black Picture. Picture black_picture; - /// Picture used for dimming inactive windows. - Picture dim_picture; /// 1x1 Picture of the shadow color. Picture cshadow_picture; /// Gaussian map of shadow. @@ -507,7 +507,12 @@ typedef struct { typedef struct _win { struct _win *next; Window id; + /// ID of the top-level client window of the window. Window client_win; + /// Whether it looks like a WM window. We consider a window WM window if + /// it does not have a decedent with WM_STATE and it is not override- + /// redirected itself. + bool wmwin; Pixmap pixmap; XWindowAttributes a; winmode mode; @@ -594,6 +599,9 @@ typedef struct _win { // Dim-related members /// Whether the window is to be dimmed. bool dim; + /// Picture for dimming. Affected by user-specified inactive dim + /// opacity and window opacity. + Picture dim_alpha_pict; /// Window flags. Definitions above. int_fast16_t flags; @@ -1398,11 +1406,10 @@ win_update_focused(session_t *ps, win *w) { w->focused = w->focused_real; - // Consider a window without client window a WM window and mark it - // focused if mark_wmwin_focused is on, or it's over-redirected and - // mark_ovredir_focused is on - if ((ps->o.mark_wmwin_focused && !w->client_win) - || (ps->o.mark_ovredir_focused && w->id == w->client_win) + // Treat WM windows and override-redirected windows specially + if ((ps->o.mark_wmwin_focused && w->wmwin) + || (ps->o.mark_ovredir_focused + && w->id == w->client_win && !w->wmwin) || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) w->focused = true; From a7c05d20f4c4a1c3f6514b6690b3ff3fdb3fe621 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 5 Dec 2012 18:12:21 +0800 Subject: [PATCH 090/178] Feature: WM_WINDOW_ROLE matching - Add support of matching WM_WINDOW_ROLE value. Thanks to Vladimir A. Pavlov! - Thanks to Vladimir A. Pavlov for reporting the issues caused by missing client window, fixed in the last commit! - Fix a memory leak in wid_get_text_prop() and wid_get_name(). Xlib documentation did not mention how to free the value XGetTextProperty() returns, so my fix could lead to troubles. - Set focus out in unmap_win(), and add w->leader, to prepare for subsidiary window detection. - Abstract update of a single string window property to win_get_prop_str(). - Change wid_get_name() to rely on wid_get_text_prop() as much as possible. - Correct a typo in win_get_prop_str() that could cause unnecessary update of shadow state and window focus. --- compton.c | 119 +++++++++++++++++++++++++++++++++--------------------- compton.h | 59 ++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 48 deletions(-) diff --git a/compton.c b/compton.c index 39ac3e7a0..5817cb43c 100644 --- a/compton.c +++ b/compton.c @@ -10,22 +10,6 @@ #include "compton.h" -// === Macros === - -// #define MSTR_(s) #s -// #define MSTR(s) MSTR_(s) - -#define printf_dbg(format, ...) \ - printf(format, ## __VA_ARGS__); \ - fflush(stdout) - -#define printf_dbgf(format, ...) \ - printf_dbg("%s" format, __func__, ## __VA_ARGS__) - -// Use #s here to prevent macro expansion -/// Macro used for shortening some debugging code. -#define CASESTRRET(s) case s: return #s - // === Global constants === /// Name strings for window types. @@ -627,6 +611,9 @@ win_match_once(win *w, const wincond_t *cond) { case CONDTGT_CLASSG: target = w->class_general; break; + case CONDTGT_ROLE: + target = w->role; + break; } if (!target) { @@ -736,6 +723,9 @@ condlst_add(wincond_t **pcondlst, const char *pattern) { case 'g': cond->target = CONDTGT_CLASSG; break; + case 'r': + cond->target = CONDTGT_ROLE; + break; default: printf("Pattern \"%s\": Target \"%c\" invalid.\n", pattern, pattern[0]); @@ -1849,7 +1839,7 @@ map_win(session_t *ps, Window id) { cw = w->id; w->wmwin = !w->a.override_redirect; #ifdef DEBUG_CLIENTWIN - printf("find_client_win(%#010lx): client self (%s)\n", w->id, + printf("find_client_win(%#010lx): client self (%s)\n", w->id, (w->wmwin ? "wmwin": "override-redirected")); #endif } @@ -1873,6 +1863,7 @@ map_win(session_t *ps, Window id) { if (ps->o.track_wdata) { win_get_name(ps, w); win_get_class(ps, w); + win_get_role(ps, w); } // Occasionally compton does not seem able to get a FocusIn event from @@ -1958,6 +1949,9 @@ unmap_win(session_t *ps, Window id) { w->a.map_state = IsUnmapped; + // Set focus out + win_set_focused(ps, w, false); + // Fading out w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); @@ -2291,6 +2285,7 @@ add_win(session_t *ps, Window id, Window prev) { new->name = NULL; new->class_instance = NULL; new->class_general = NULL; + new->role = NULL; new->cache_sblst = NULL; new->cache_fblst = NULL; new->cache_fcblst = NULL; @@ -2323,6 +2318,7 @@ add_win(session_t *ps, Window id, Window prev) { new->focused = false; new->focused_real = false; + new->leader = None; new->destroyed = false; new->need_configure = false; new->window_type = WINTYPE_UNKNOWN; @@ -2665,7 +2661,7 @@ expose_root(session_t *ps, XRectangle *rects, int nrects) { static bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr) { - XTextProperty text_prop; + XTextProperty text_prop = { NULL, None, 0, 0 }; if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value)) return false; @@ -2676,9 +2672,11 @@ wid_get_text_prop(session_t *ps, Window wid, Atom prop, *pnstr = 0; if (*pstrlst) XFreeStringList(*pstrlst); + XFree(text_prop.value); return false; } + XFree(text_prop.value); return true; } @@ -2687,14 +2685,11 @@ wid_get_text_prop(session_t *ps, Window wid, Atom prop, */ static bool wid_get_name(session_t *ps, Window wid, char **name) { - XTextProperty text_prop; + XTextProperty text_prop = { NULL, None, 0, 0 }; char **strlst = NULL; int nstr = 0; - // set_ignore_next(ps); - if (!(XGetTextProperty(ps->dpy, wid, &text_prop, ps->atom_name_ewmh) - && text_prop.value)) { - // set_ignore_next(ps); + if (!(wid_get_text_prop(ps, wid, ps->atom_name_ewmh, &strlst, &nstr))) { #ifdef DEBUG_WINDATA printf_dbgf("(%#010lx): _NET_WM_NAME unset, falling back to WM_NAME.\n", wid); #endif @@ -2702,15 +2697,37 @@ wid_get_name(session_t *ps, Window wid, char **name) { if (!(XGetWMName(ps->dpy, wid, &text_prop) && text_prop.value)) { return false; } + if (Success != + XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) + || !nstr || !strlst) { + if (strlst) + XFreeStringList(strlst); + XFree(text_prop.value); + return false; + } + XFree(text_prop.value); } - if (Success != - XmbTextPropertyToTextList(ps->dpy, &text_prop, &strlst, &nstr) - || !nstr || !strlst) { - if (strlst) - XFreeStringList(strlst); + + *name = mstrcpy(strlst[0]); + + XFreeStringList(strlst); + + return true; +} + +/** + * Get the role of a window from window ID. + */ +static bool +wid_get_role(session_t *ps, Window wid, char **role) { + char **strlst = NULL; + int nstr = 0; + + if (!wid_get_text_prop(ps, wid, ps->atom_role, &strlst, &nstr)) { return false; } - *name = mstrcpy(strlst[0]); + + *role = mstrcpy(strlst[0]); XFreeStringList(strlst); @@ -2718,38 +2735,34 @@ wid_get_name(session_t *ps, Window wid, char **name) { } /** - * Retrieve the name of a window and update its win + * Retrieve a string property of a window and update its win * structure. */ static int -win_get_name(session_t *ps, win *w) { - bool ret; - char *name_old = w->name; +win_get_prop_str(session_t *ps, win *w, char **tgt, + bool (*func_wid_get_prop_str)(session_t *ps, Window wid, char **tgt)) { + int ret = -1; + char *prop_old = *tgt; // Can't do anything if there's no client window if (!w->client_win) return false; - // Get the name - ret = wid_get_name(ps, w->client_win, &w->name); + // Get the property + ret = func_wid_get_prop_str(ps, w->client_win, tgt); - // Return -1 if wid_get_name() failed, 0 if name didn't change, 1 if - // it changes + // Return -1 if func_wid_get_prop_str() failed, 0 if the property + // doesn't change, 1 if it changes if (!ret) ret = -1; - else if (name_old && !strcmp(w->name, name_old)) + else if (prop_old && !strcmp(*tgt, prop_old)) ret = 0; else ret = 1; - // Keep the old name if there's no new one - if (w->name != name_old) - free(name_old); - -#ifdef DEBUG_WINDATA - printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", " - "ret = %d\n", w->id, w->client_win, w->name, ret); -#endif + // Keep the old property if there's no new one + if (*tgt != prop_old) + free(prop_old); return ret; } @@ -3138,6 +3151,15 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { } } + // If role changes + if (ps->o.track_wdata && ps->atom_role == ev->atom) { + win *w = find_toplevel(ps, ev->window); + if (w && 1 == win_get_role(ps, w)) { + determine_shadow(ps, w); + win_update_focused(ps, w); + } + } + // If _COMPTON_SHADOW changes if (ps->o.respect_prop_shadow && ps->atom_compton_shadow == ev->atom) { win *w = find_win(ps, ev->window); @@ -3428,7 +3450,8 @@ usage(void) { " condition = :[]:\n" "\n" " is one of \"n\" (window name), \"i\" (window class\n" - " instance), and \"g\" (window general class)\n" + " instance), \"g\" (window general class), and \"r\"\n" + " (window role).\n" "\n" " is one of \"e\" (exact match), \"a\" (match anywhere),\n" " \"s\" (match from start), \"w\" (wildcard), and \"p\" (PCRE\n" @@ -4148,6 +4171,7 @@ init_atoms(session_t *ps) { ps->atom_name = XA_WM_NAME; ps->atom_name_ewmh = XInternAtom(ps->dpy, "_NET_WM_NAME", False); ps->atom_class = XA_WM_CLASS; + ps->atom_role = XInternAtom(ps->dpy, "WM_WINDOW_ROLE", False); ps->atom_transient = XA_WM_TRANSIENT_FOR; ps->atom_ewmh_active_win = XInternAtom(ps->dpy, "_NET_ACTIVE_WINDOW", False); ps->atom_compton_shadow = XInternAtom(ps->dpy, "_COMPTON_SHADOW", False); @@ -4684,6 +4708,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .atom_name = None, .atom_name_ewmh = None, .atom_class = None, + .atom_role = None, .atom_transient = None, .atom_ewmh_active_win = None, .atom_compton_shadow = None, diff --git a/compton.h b/compton.h index 60cb52187..9bc5bd091 100644 --- a/compton.h +++ b/compton.h @@ -120,6 +120,22 @@ // Window opacity / dim state changed #define WFLAG_OPCT_CHANGE 0x0004 +// === Macros === + +// #define MSTR_(s) #s +// #define MSTR(s) MSTR_(s) + +#define printf_dbg(format, ...) \ + printf(format, ## __VA_ARGS__); \ + fflush(stdout) + +#define printf_dbgf(format, ...) \ + printf_dbg("%s" format, __func__, ## __VA_ARGS__) + +// Use #s here to prevent macro expansion +/// Macro used for shortening some debugging code. +#define CASESTRRET(s) case s: return #s + // === Types === typedef uint32_t opacity_t; @@ -169,6 +185,7 @@ enum wincond_target { CONDTGT_NAME, CONDTGT_CLASSI, CONDTGT_CLASSG, + CONDTGT_ROLE, }; enum wincond_type { @@ -491,6 +508,8 @@ typedef struct { Atom atom_name_ewmh; /// Atom of property WM_CLASS. Atom atom_class; + /// Atom of property WM_WINDOW_ROLE. + Atom atom_role; /// Atom of property WM_TRANSIENT_FOR. Atom atom_transient; /// Atom of property _NET_ACTIVE_WINDOW. @@ -505,7 +524,10 @@ typedef struct { /// Structure representing a top-level window compton manages. typedef struct _win { + // Next structure in the linked list. struct _win *next; + + // ID of the top-level frame window. Window id; /// ID of the top-level client window of the window. Window client_win; @@ -527,6 +549,8 @@ typedef struct _win { bool focused; /// Whether the window is actually focused. bool focused_real; + /// Leader window ID of the window. + Window leader; /// Whether the window has been destroyed. bool destroyed; /// Cached width/height of the window including border. @@ -539,9 +563,14 @@ typedef struct _win { bool to_paint; // Blacklist related members + /// Name of the window. char *name; + /// Window instance class of the window. char *class_instance; + /// Window general class of the window. char *class_general; + /// WM_WINDOW_ROLE value of the window. + char *role; wincond_t *cache_sblst; wincond_t *cache_fblst; wincond_t *cache_fcblst; @@ -1490,8 +1519,36 @@ wid_get_text_prop(session_t *ps, Window wid, Atom prop, static bool wid_get_name(session_t *ps, Window w, char **name); +static bool +wid_get_role(session_t *ps, Window w, char **role); + static int -win_get_name(session_t *ps, win *w); +win_get_prop_str(session_t *ps, win *w, char **tgt, + bool (*func_wid_get_prop_str)(session_t *ps, Window wid, char **tgt)); + +static inline int +win_get_name(session_t *ps, win *w) { + int ret = win_get_prop_str(ps, w, &w->name, wid_get_name); + +#ifdef DEBUG_WINDATA + printf_dbgf("(%#010lx): client = %#010lx, name = \"%s\", " + "ret = %d\n", w->id, w->client_win, w->name, ret); +#endif + + return ret; +} + +static inline int +win_get_role(session_t *ps, win *w) { + int ret = win_get_prop_str(ps, w, &w->role, wid_get_role); + +#ifdef DEBUG_WINDATA + printf_dbgf("(%#010lx): client = %#010lx, role = \"%s\", " + "ret = %d\n", w->id, w->client_win, w->role, ret); +#endif + + return ret; +} static bool win_get_class(session_t *ps, win *w); From bfbf991af95a40ee5b6421da25b1e2e35e6002cc Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 7 Dec 2012 22:38:10 +0800 Subject: [PATCH 091/178] Bug fix #68: Possible fix for failure in client window detection - Note I'm not in the best state today (bad cold & sleep-deprived). This commit is likely to introduce bugs. - Attempt to fix client window detection failures happening when compton searches for client window before it's ready. - Fix build failure with role is not freed on window destruction. --- compton.c | 203 ++++++++++++++++++++++++++++++++++++++++-------------- compton.h | 29 ++++++-- 2 files changed, 175 insertions(+), 57 deletions(-) diff --git a/compton.c b/compton.c index 5817cb43c..d06c0d79b 100644 --- a/compton.c +++ b/compton.c @@ -1143,7 +1143,7 @@ border_size(session_t *ps, win *w) { */ static Window find_client_win(session_t *ps, Window w) { - if (wid_has_attr(ps, w, ps->atom_client)) { + if (wid_has_prop(ps, w, ps->atom_client)) { return w; } @@ -1799,14 +1799,22 @@ map_win(session_t *ps, Window id) { win *w = find_win(ps, id); // Don't care about window mapping if it's an InputOnly window - if (!w || InputOnly == w->a.class) return; + // Try avoiding mapping a window twice + if (!w || InputOnly == w->a.class + || IsViewable == w->a.map_state) + return; + + assert(!w->focused_real); - w->focused_real = false; + w->a.map_state = IsViewable; + + // Set focused to false + bool focused_real = false; if (ps->o.track_focus && ps->o.use_ewmh_active_win && w == ps->active_win) - w->focused_real = true; + focused_real = true; + win_set_focused(ps, w, focused_real); - w->a.map_state = IsViewable; // Call XSelectInput() before reading properties so that no property // changes are lost @@ -1823,31 +1831,11 @@ map_win(session_t *ps, Window id) { // Detect client window here instead of in add_win() as the client // window should have been prepared at this point if (!w->client_win) { - w->wmwin = false; - - Window cw = None; - // Always recursively look for a window with WM_STATE, as Fluxbox - // sets override-redirect flags on all frame windows. - cw = find_client_win(ps, w->id); -#ifdef DEBUG_CLIENTWIN - if (cw) - printf("find_client_win(%#010lx): client %#010lx\n", w->id, cw); -#endif - // Set a window's client window to itself if we couldn't find a - // client window - if (!cw) { - cw = w->id; - w->wmwin = !w->a.override_redirect; -#ifdef DEBUG_CLIENTWIN - printf("find_client_win(%#010lx): client self (%s)\n", w->id, - (w->wmwin ? "wmwin": "override-redirected")); -#endif - } - mark_client_win(ps, w, cw); + win_recheck_client(ps, w); } else { // Re-mark client window here - mark_client_win(ps, w, w->client_win); + win_mark_client(ps, w, w->client_win); } assert(w->client_win); @@ -1947,11 +1935,11 @@ unmap_win(session_t *ps, Window id) { if (!w || IsUnmapped == w->a.map_state) return; - w->a.map_state = IsUnmapped; - // Set focus out win_set_focused(ps, w, false); + w->a.map_state = IsUnmapped; + // Fading out w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); @@ -2207,18 +2195,20 @@ calc_shadow_geometry(session_t *ps, win *w) { /** * Mark a window as the client window of another. * - * @param dpy display to use + * @param ps current session * @param w struct _win of the parent window * @param client window ID of the client window */ static void -mark_client_win(session_t *ps, win *w, Window client) { +win_mark_client(session_t *ps, win *w, Window client) { w->client_win = client; XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_CLIENT)); - // Get the frame width and monitor further frame width changes on client - // window if necessary + // Make sure the XSelectInput() requests are sent + XSync(ps->dpy, False); + + // Get frame widths if needed if (ps->o.frame_opacity) { get_frame_extents(ps, w, client); } @@ -2232,11 +2222,69 @@ mark_client_win(session_t *ps, win *w, Window client) { // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. if (WINTYPE_UNKNOWN == w->window_type) { if (w->a.override_redirect - || !wid_has_attr(ps, client, ps->atom_transient)) + || !wid_has_prop(ps, client, ps->atom_transient)) w->window_type = WINTYPE_NORMAL; else w->window_type = WINTYPE_DIALOG; } + + win_update_focused(ps, w); +} + +/** + * Unmark current client window of a window. + * + * @param ps current session + * @param w struct _win of the parent window + */ +static void +win_unmark_client(session_t *ps, win *w) { + Window client = w->client_win; + + w->client_win = None; + + // Recheck event mask + XSelectInput(ps->dpy, client, + determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)); +} + +/** + * Recheck client window of a window. + * + * @param ps current session + * @param w struct _win of the parent window + */ +static void +win_recheck_client(session_t *ps, win *w) { + // Initialize wmwin to false + w->wmwin = false; + + // Look for the client window + + // Always recursively look for a window with WM_STATE, as Fluxbox + // sets override-redirect flags on all frame windows. + Window cw = find_client_win(ps, w->id); +#ifdef DEBUG_CLIENTWIN + if (cw) + printf_dbgf("(%#010lx): client %#010lx\n", w->id, cw); +#endif + // Set a window's client window to itself if we couldn't find a + // client window + if (!cw) { + cw = w->id; + w->wmwin = !w->a.override_redirect; +#ifdef DEBUG_CLIENTWIN + printf_dbgf("(%#010lx): client self (%s)\n", w->id, + (w->wmwin ? "wmwin": "override-redirected")); +#endif + } + + // Unmark the old one + if (w->client_win && w->client_win != cw) + win_unmark_client(ps, w); + + // Mark the new one + win_mark_client(ps, w, cw); } static void @@ -2268,6 +2316,11 @@ add_win(session_t *ps, Window id, Window prev) { return; } + // Delay window mapping + int map_state = new->a.map_state; + assert(IsViewable == map_state || IsUnmapped == map_state); + new->a.map_state = IsUnmapped; + new->damaged = 0; new->to_paint = false; new->pixmap = None; @@ -2342,7 +2395,7 @@ add_win(session_t *ps, Window id, Window prev) { new->next = *p; *p = new; - if (new->a.map_state == IsViewable) { + if (map_state == IsViewable) { map_win(ps, id); } } @@ -2655,6 +2708,27 @@ expose_root(session_t *ps, XRectangle *rects, int nrects) { add_damage(ps, region); } +/** + * Get the value of a type-Window property of a window. + * + * @return the value if successful, 0 otherwise + */ +static Window +wid_get_prop_window(session_t *ps, Window wid, Atom aprop) { + // Get the attribute + Window p = None; + winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XA_WINDOW, 32); + + // Return it + if (prop.nitems) { + p = *prop.data.p32; + } + + free_winprop(&prop); + + return p; +} + /** * Get the value of a text property of a window. */ @@ -3006,15 +3080,32 @@ ev_reparent_notify(session_t *ps, XReparentEvent *ev) { add_win(ps, ev->window, 0); } else { destroy_win(ps, ev->window); + // Reset event mask in case something wrong happens XSelectInput(ps->dpy, ev->window, determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)); - /* - // Check if the window is a client window of another - win *w_top = find_toplevel2(ps, ev->window); - if (w_top && !(w_top->client_win)) { - mark_client_win(ps, w_top, ev->window); - } */ + + // Check if the window is an undetected client window + // Firstly, check if it's a known client window + if (!find_toplevel(ps, ev->window)) { + // If not, look for its frame window + win *w_top = find_toplevel2(ps, ev->parent); + // If found, and its frame may not have a correct client, continue + if (w_top && w_top->client_win == w_top->id) { + // If it has WM_STATE, mark it the client window + if (wid_has_prop(ps, ev->window, ps->atom_client)) { + w_top->wmwin = false; + win_unmark_client(ps, w_top); + win_mark_client(ps, w_top, ev->window); + } + // Otherwise, watch for WM_STATE on it + else { + XSelectInput(ps->dpy, ev->window, + determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) + | PropertyChangeMask); + } + } + } } } @@ -3059,18 +3150,10 @@ ev_expose(session_t *ps, XExposeEvent *ev) { */ static void update_ewmh_active_win(session_t *ps) { - // Get the attribute firstly - winprop_t prop = wid_get_prop(ps, ps->root, ps->atom_ewmh_active_win, - 1L, XA_WINDOW, 32); - if (!prop.nitems) { - free_winprop(&prop); - return; - } - // Search for the window - Window wid = *prop.data.p32; + Window wid = + wid_get_prop_window(ps, ps->root, ps->atom_ewmh_active_win); win *w = NULL; - free_winprop(&prop); if (wid && !(w = find_toplevel(ps, wid))) if (!(w = find_win(ps, wid))) @@ -3107,6 +3190,24 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { return; } + // If WM_STATE changes + if (ev->atom == ps->atom_client) { + // Check whether it could be a client window + if (!find_toplevel(ps, ev->window)) { + // Reset event mask anyway + XSelectInput(ps->dpy, ev->window, + determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)); + + win *w_top = find_toplevel2(ps, ev->window); + if (w_top && w_top->client_win == w_top->id + && wid_has_prop(ps, ev->window, ps->atom_client)) { + w_top->wmwin = false; + win_unmark_client(ps, w_top); + win_mark_client(ps, w_top, ev->window); + } + } + } + // If _NET_WM_OPACITY changes if (ev->atom == ps->atom_opacity) { win *w = NULL; diff --git a/compton.h b/compton.h index 9bc5bd091..6b4eed221 100644 --- a/compton.h +++ b/compton.h @@ -58,7 +58,10 @@ // For compatiblity with pattern); #ifdef CONFIG_REGEX_PCRE if (cond->regex_pcre_extra) - pcre_free_study(cond->regex_pcre_extra); + LPCRE_FREE_STUDY(cond->regex_pcre_extra); if (cond->regex_pcre) pcre_free(cond->regex_pcre); #endif @@ -1077,6 +1080,7 @@ free_win_res(session_t *ps, win *w) { free(w->name); free(w->class_instance); free(w->class_general); + free(w->role); } /** @@ -1149,15 +1153,15 @@ static inline bool is_normal_win(const win *w) { } /** - * Determine if a window has a specific attribute. + * Determine if a window has a specific property. * * @param session_t current session * @param w window to check - * @param atom atom of attribute to check + * @param atom atom of property to check * @return 1 if it has the attribute, 0 otherwise */ static inline bool -wid_has_attr(const session_t *ps, Window w, Atom atom) { +wid_has_prop(const session_t *ps, Window w, Atom atom) { Atom type = None; int format; unsigned long nitems, after; @@ -1451,6 +1455,10 @@ win_update_focused(session_t *ps, win *w) { */ static inline void win_set_focused(session_t *ps, win *w, bool focused) { + // Unmapped windows will have their focused state reset on map + if (IsUnmapped == w->a.map_state) + return; + w->focused_real = focused; win_update_focused(ps, w); } @@ -1480,7 +1488,13 @@ static void calc_shadow_geometry(session_t *ps, win *w); static void -mark_client_win(session_t *ps, win *w, Window client); +win_mark_client(session_t *ps, win *w, Window client); + +static void +win_unmark_client(session_t *ps, win *w); + +static void +win_recheck_client(session_t *ps, win *w); static void add_win(session_t *ps, Window id, Window prev); @@ -1516,6 +1530,9 @@ static bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr); +static Window +wid_get_prop_window(session_t *ps, Window wid, Atom aprop); + static bool wid_get_name(session_t *ps, Window w, char **name); From 94f292b9ff1cffd7fade3ba9de9f4ed8d83416db Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 8 Dec 2012 11:10:08 +0800 Subject: [PATCH 092/178] Bug fix #68: Second attempt to fix client window detection - Attempt to fix client window detection when WM_STATE property is not yet set when the window is mapped. Thanks to kinclma1 for reporting. - Apply stricter checks for whether window is mapped in determine_evmask() and win_mark_client(). --- compton.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/compton.c b/compton.c index d06c0d79b..b535383b0 100644 --- a/compton.c +++ b/compton.c @@ -825,14 +825,19 @@ condlst_add(wincond_t **pcondlst, const char *pattern) { static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { long evmask = NoEventMask; + win *w = NULL; - if (WIN_EVMODE_FRAME == mode || find_win(ps, wid)) { + // Check if it's a mapped frame window + if (WIN_EVMODE_FRAME == mode + || ((w = find_win(ps, wid)) && IsViewable == w->a.map_state)) { evmask |= PropertyChangeMask; if (ps->o.track_focus && !ps->o.use_ewmh_active_win) evmask |= FocusChangeMask; } - if (WIN_EVMODE_CLIENT == mode || find_toplevel(ps, wid)) { + // Check if it's a mapped client window + if (WIN_EVMODE_CLIENT == mode + || ((w = find_toplevel(ps, wid)) && IsViewable == w->a.map_state)) { if (ps->o.frame_opacity || ps->o.track_wdata || ps->o.detect_client_opacity) evmask |= PropertyChangeMask; @@ -2203,6 +2208,11 @@ static void win_mark_client(session_t *ps, win *w, Window client) { w->client_win = client; + // If the window isn't mapped yet, stop here, as the function will be + // called in map_win() + if (IsViewable != w->a.map_state) + return; + XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_CLIENT)); // Make sure the XSelectInput() requests are sent @@ -3090,8 +3100,10 @@ ev_reparent_notify(session_t *ps, XReparentEvent *ev) { if (!find_toplevel(ps, ev->window)) { // If not, look for its frame window win *w_top = find_toplevel2(ps, ev->parent); - // If found, and its frame may not have a correct client, continue - if (w_top && w_top->client_win == w_top->id) { + // If found, and the client window has not been determined, or its + // frame may not have a correct client, continue + if (w_top && (!w_top->client_win + || w_top->client_win == w_top->id)) { // If it has WM_STATE, mark it the client window if (wid_has_prop(ps, ev->window, ps->atom_client)) { w_top->wmwin = false; From 8d4a3f899f8efb03da4c1ab52234e463c45396df Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 10 Dec 2012 10:31:24 +0800 Subject: [PATCH 093/178] Feature: Wintype-based focus exclusion - Add "focus" to the wintypes settings in compton.conf, to mark windows of certain window types to be always focused. Replaces the ugly is_normal_win(). - Add a ON/OFF/UNSET switch_t type, but it's unused currently. - Mark client_win if the window client detection hasn't been performed when we detect WM_STATE's presence in ev_property_notify(), for performance. --- compton.c | 16 +++++++++++++--- compton.h | 37 +++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/compton.c b/compton.c index b535383b0..cdf321d54 100644 --- a/compton.c +++ b/compton.c @@ -2034,7 +2034,7 @@ calc_opacity(session_t *ps, win *w) { } // Respect inactive_opacity in some cases - if (ps->o.inactive_opacity && is_normal_win(w) && false == w->focused + if (ps->o.inactive_opacity && false == w->focused && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { opacity = ps->o.inactive_opacity; } @@ -2054,7 +2054,7 @@ calc_dim(session_t *ps, win *w) { if (w->destroyed || IsViewable != w->a.map_state) return; - if (ps->o.inactive_dim && is_normal_win(w) && !(w->focused)) { + if (ps->o.inactive_dim && !(w->focused)) { dim = true; } else { dim = false; @@ -3211,7 +3211,8 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)); win *w_top = find_toplevel2(ps, ev->window); - if (w_top && w_top->client_win == w_top->id + // Initialize client_win as early as possible + if (w_top && (!w_top->client_win || w_top->client_win == w_top->id) && wid_has_prop(ps, ev->window, ps->atom_client)) { w_top->wmwin = false; win_unmark_client(ps, w_top); @@ -3971,6 +3972,8 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { ps->o.wintype_shadow[i] = (bool) ival; if (config_setting_lookup_bool(setting, "fade", &ival)) ps->o.wintype_fade[i] = (bool) ival; + if (config_setting_lookup_bool(setting, "focus", &ival)) + ps->o.wintype_focus[i] = (bool) ival; config_setting_lookup_float(setting, "opacity", &ps->o.wintype_opacity[i]); } @@ -4752,6 +4755,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_dim = 0.0, .inactive_dim_fixed = false, .alpha_step = 0.03, + + .wintype_focus = { false }, .use_ewmh_active_win = false, .focus_blacklist = NULL, @@ -4836,6 +4841,11 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->ignore_tail = &ps->ignore_head; gettimeofday(&ps->time_start, NULL); + wintype_arr_enable(ps->o.wintype_focus); + ps->o.wintype_focus[WINTYPE_UNKNOWN] = false; + ps->o.wintype_focus[WINTYPE_NORMAL] = false; + ps->o.wintype_focus[WINTYPE_UTILITY] = false; + get_cfg(ps, argc, argv); // Inherit old Display if possible, primarily for resource leak checking diff --git a/compton.h b/compton.h index 6b4eed221..0ff235432 100644 --- a/compton.h +++ b/compton.h @@ -162,6 +162,13 @@ typedef enum { NUM_WINTYPES } wintype_t; +/// Enumeration type to represent switches. +typedef enum { + OFF, // false + ON, // true + UNSET +} switch_t; + typedef enum { WINDOW_SOLID, WINDOW_TRANS, @@ -266,6 +273,7 @@ typedef struct { bool vsync_aggressive; // === Shadow === + /// Enable/disable shadow for specific window types. bool wintype_shadow[NUM_WINTYPES]; /// Red, green and blue tone of the shadow. double shadow_red, shadow_green, shadow_blue; @@ -281,6 +289,7 @@ typedef struct { bool respect_prop_shadow; // === Fading === + /// Enable/disable fading for specific window types. bool wintype_fade[NUM_WINTYPES]; /// How much to fade in in a single fading step. opacity_t fade_in_step; @@ -293,6 +302,7 @@ typedef struct { wincond_t *fade_blacklist; // === Opacity === + /// Default opacity for specific window types double wintype_opacity[NUM_WINTYPES]; /// Default opacity for inactive windows. /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for @@ -316,6 +326,8 @@ typedef struct { double alpha_step; // === Focus related === + /// Consider windows of specific types to be always focused. + bool wintype_focus[NUM_WINTYPES]; /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. bool use_ewmh_active_win; /// A list of windows always to be considered focused. @@ -764,7 +776,7 @@ sub_unslong(unsigned long a, unsigned long b) { /** * Set a bool array of all wintypes to true. */ -static void +static inline void wintype_arr_enable(bool arr[]) { wintype_t i; @@ -773,6 +785,18 @@ wintype_arr_enable(bool arr[]) { } } +/** + * Set a switch_t array of all unset wintypes to true. + */ +static inline void +wintype_arr_enable_unset(switch_t arr[]) { + wintype_t i; + + for (i = 0; i < NUM_WINTYPES; ++i) + if (UNSET == arr[i]) + arr[i] = ON; +} + /** * Allocate the space and copy a string. */ @@ -1147,11 +1171,6 @@ static Picture solid_picture(session_t *ps, bool argb, double a, double r, double g, double b); -static inline bool is_normal_win(const win *w) { - return (WINTYPE_NORMAL == w->window_type - || WINTYPE_UTILITY == w->window_type); -} - /** * Determine if a window has a specific property. * @@ -1439,8 +1458,10 @@ win_update_focused(session_t *ps, win *w) { w->focused = w->focused_real; - // Treat WM windows and override-redirected windows specially - if ((ps->o.mark_wmwin_focused && w->wmwin) + // Use wintype_focus, and treat WM windows and override-redirected + // windows specially + if (ps->o.wintype_focus[w->window_type] + || (ps->o.mark_wmwin_focused && w->wmwin) || (ps->o.mark_ovredir_focused && w->id == w->client_win && !w->wmwin) || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) From 7b86cd790a3790f22494d4bc52e477f5e0341458 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 12 Dec 2012 12:01:51 +0800 Subject: [PATCH 094/178] Feature #65: Auxiliary window detection - Add auxiliary window detection (--detect-transient & --detect-client-leader). Thanks to SmilingHorse for inspiring me. The implementation is not too speed-efficient, and bugs are to be expected. - Known issue: auxiliary window detection may not work too well with windows that are never mapped, for example as client leader. --- compton.c | 176 +++++++++++++++++++++++++++++++++++++++++------------- compton.h | 156 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 285 insertions(+), 47 deletions(-) diff --git a/compton.c b/compton.c index cdf321d54..c1016769f 100644 --- a/compton.c +++ b/compton.c @@ -846,45 +846,6 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { return evmask; } -/** - * Find a window from window id in window linked list of the session. - */ -static win * -find_win(session_t *ps, Window id) { - if (!id) - return NULL; - - win *w; - - for (w = ps->list; w; w = w->next) { - if (w->id == id && !w->destroyed) - return w; - } - - return 0; -} - -/** - * Find out the WM frame of a client window using existing data. - * - * @param w window ID - * @return struct _win object of the found window, NULL if not found - */ -static win * -find_toplevel(session_t *ps, Window id) { - if (!id) - return NULL; - - win *w; - - for (w = ps->list; w; w = w->next) { - if (w->client_win == id && !w->destroyed) - return w; - } - - return NULL; -} - /** * Find out the WM frame of a client window by querying X. * @@ -1820,7 +1781,6 @@ map_win(session_t *ps, Window id) { focused_real = true; win_set_focused(ps, w, focused_real); - // Call XSelectInput() before reading properties so that no property // changes are lost XSelectInput(ps->dpy, id, determine_evmask(ps, id, WIN_EVMODE_FRAME)); @@ -2238,6 +2198,10 @@ win_mark_client(session_t *ps, win *w, Window client) { w->window_type = WINTYPE_DIALOG; } + // Get window group + if (ps->o.track_leader) + win_update_leader(ps, w); + win_update_focused(ps, w); } @@ -2382,6 +2346,7 @@ add_win(session_t *ps, Window id, Window prev) { new->focused = false; new->focused_real = false; new->leader = None; + new->cache_leader = None; new->destroyed = false; new->need_configure = false; new->window_type = WINTYPE_UNKNOWN; @@ -2739,6 +2704,91 @@ wid_get_prop_window(session_t *ps, Window wid, Atom aprop) { return p; } +/** + * Update leader of a window. + */ +static void +win_update_leader(session_t *ps, win *w) { + Window leader = None; + + // Read the leader properties + if (ps->o.detect_transient && !leader) + leader = wid_get_prop_window(ps, w->client_win, ps->atom_transient); + + if (ps->o.detect_client_leader && !leader) + leader = wid_get_prop_window(ps, w->client_win, ps->atom_client_leader); + + win_set_leader(ps, w, leader); +} + +/** + * Set leader of a window. + */ +static void +win_set_leader(session_t *ps, win *w, Window nleader) { + // If the leader changes + if (w->leader != nleader) { + Window cache_leader_old = win_get_leader(ps, w); + + w->leader = nleader; + + // Forcefully do this to deal with the case when a child window + // gets mapped before parent, or when the window is a waypoint + clear_cache_win_leaders(ps); + + // Update the old and new window group and active_leader if the window + // could affect their state. + Window cache_leader = win_get_leader(ps, w); + if (w->focused_real && cache_leader_old != cache_leader) { + ps->active_leader = cache_leader; + + group_update_focused(ps, cache_leader_old); + group_update_focused(ps, cache_leader); + } + // Otherwise, at most the window itself is affected + else { + win_update_focused(ps, w); + } + } +} + +/** + * Get the leader of a window. + * + * This function updates w->cache_leader if necessary. + */ +static Window +win_get_leader(session_t *ps, win *w) { + return win_get_leader_raw(ps, w, 0); +} + +/** + * Internal function of win_get_leader(). + */ +static Window +win_get_leader_raw(session_t *ps, win *w, int recursions) { + // Rebuild the cache if needed + if (!w->cache_leader && (w->client_win || w->leader)) { + // Leader defaults to client window + if (!(w->cache_leader = w->leader)) + w->cache_leader = w->client_win; + + // If the leader of this window isn't itself, look for its ancestors + if (w->cache_leader && w->cache_leader != w->client_win) { + win *wp = find_toplevel(ps, w->cache_leader); + if (wp) { + // Dead loop? + if (recursions > WIN_GET_LEADER_MAX_RECURSION) + return None; + + w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1); + } + } + } + + return w->cache_leader; +} + /** * Get the value of a text property of a window. */ @@ -3173,10 +3223,10 @@ update_ewmh_active_win(session_t *ps) { // Mark the window focused if (w) { - win_set_focused(ps, w, true); if (ps->active_win && w != ps->active_win) win_set_focused(ps, ps->active_win, false); ps->active_win = w; + win_set_focused(ps, w, true); } } @@ -3280,6 +3330,16 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (w) win_update_attr_shadow(ps, w); } + + // If a leader property changes + if ((ps->o.detect_transient && ps->atom_transient == ev->atom) + || (ps->o.detect_client_leader && ps->atom_client_leader == ev->atom)) { + win *w = find_toplevel(ps, ev->window); + if (w) { + win_update_leader(ps, w); + } + } + } inline static void @@ -3558,6 +3618,13 @@ usage(void) { " considered focused.\n" "--inactive-dim-fixed\n" " Use fixed inactive dim value.\n" + "--detect-transient\n" + " Use WM_TRANSIENT_FOR to group windows, and consider windows in\n" + " the same group focused at the same time.\n" + "--detect-client-leader\n" + " Use WM_CLIENT_LEADER to group windows, and consider windows in\n" + " the same group focused at the same time. WM_TRANSIENT_FOR has\n" + " higher priority if --detect-transient is enabled, too.\n" "\n" "Format of a condition:\n" "\n" @@ -3955,6 +4022,11 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { &ps->o.unredir_if_possible); // --inactive-dim-fixed lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &ps->o.inactive_dim_fixed); + // --detect-transient + lcfg_lookup_bool(&cfg, "detect-transient", &ps->o.detect_transient); + // --detect-client-leader + lcfg_lookup_bool(&cfg, "detect-client-leader", + &ps->o.detect_client_leader); // --shadow-exclude parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude"); // --focus-exclude @@ -4016,6 +4088,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "unredir-if-possible", no_argument, NULL, 278 }, { "focus-exclude", required_argument, NULL, 279 }, { "inactive-dim-fixed", no_argument, NULL, 280 }, + { "detect-transient", no_argument, NULL, 281 }, + { "detect-client-leader", no_argument, NULL, 282 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4225,6 +4299,14 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --inactive-dim-fixed ps->o.inactive_dim_fixed = true; break; + case 281: + // --detect-transient + ps->o.detect_transient = true; + break; + case 282: + // --detect-client-leader + ps->o.detect_client_leader = true; + break; default: usage(); } @@ -4274,6 +4356,12 @@ get_cfg(session_t *ps, int argc, char *const *argv) { if (ps->o.shadow_blacklist || ps->o.fade_blacklist || ps->o.focus_blacklist) ps->o.track_wdata = true; + + // Determine whether we track window grouping + if (ps->o.detect_transient || ps->o.detect_client_leader) { + ps->o.track_leader = true; + } + } /** @@ -4289,6 +4377,7 @@ init_atoms(session_t *ps) { ps->atom_class = XA_WM_CLASS; ps->atom_role = XInternAtom(ps->dpy, "WM_WINDOW_ROLE", False); ps->atom_transient = XA_WM_TRANSIENT_FOR; + ps->atom_client_leader = XInternAtom(ps->dpy, "WM_CLIENT_LEADER", False); ps->atom_ewmh_active_win = XInternAtom(ps->dpy, "_NET_ACTIVE_WINDOW", False); ps->atom_compton_shadow = XInternAtom(ps->dpy, "_COMPTON_SHADOW", False); @@ -4759,9 +4848,12 @@ session_init(session_t *ps_old, int argc, char **argv) { .wintype_focus = { false }, .use_ewmh_active_win = false, .focus_blacklist = NULL, + .detect_transient = false, + .detect_client_leader = false, .track_focus = false, .track_wdata = false, + .track_leader = false, }, .all_damage = None, @@ -4782,6 +4874,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .list = NULL, .active_win = NULL, + .active_leader = None, + .black_picture = None, .cshadow_picture = None, .gaussian_map = NULL, diff --git a/compton.h b/compton.h index 0ff235432..3b13b8f51 100644 --- a/compton.h +++ b/compton.h @@ -109,6 +109,7 @@ #define FADE_DELTA_TOLERANCE 0.2 #define SW_OPTI_TOLERANCE 1000 +#define WIN_GET_LEADER_MAX_RECURSION 20 #define NS_PER_SEC 1000000000L #define US_PER_SEC 1000000L @@ -332,12 +333,18 @@ typedef struct { bool use_ewmh_active_win; /// A list of windows always to be considered focused. wincond_t *focus_blacklist; + /// Whether to do window grouping with WM_TRANSIENT_FOR. + bool detect_transient; + /// Whether to do window grouping with WM_CLIENT_LEADER. + bool detect_client_leader; // === Calculated === /// Whether compton needs to track focus changes. bool track_focus; /// Whether compton needs to track window name and class. bool track_wdata; + /// Whether compton needs to track window leaders. + bool track_leader; } options_t; @@ -424,6 +431,9 @@ typedef struct { /// case the WM does something extraordinary, but caching the pointer /// means another layer of complexity. struct _win *active_win; + /// Window ID of leader window of currently active window. Used for + /// subsidiary window detection. + Window active_leader; // === Shadow/dimming related === /// 1x1 black Picture. @@ -527,6 +537,8 @@ typedef struct { Atom atom_role; /// Atom of property WM_TRANSIENT_FOR. Atom atom_transient; + /// Atom of property WM_CLIENT_LEADER. + Atom atom_client_leader; /// Atom of property _NET_ACTIVE_WINDOW. Atom atom_ewmh_active_win; /// Atom of property _COMPTON_SHADOW. @@ -566,6 +578,8 @@ typedef struct _win { bool focused_real; /// Leader window ID of the window. Window leader; + /// Cached topmost window ID of the window. + Window cache_leader; /// Whether the window has been destroyed. bool destroyed; /// Cached width/height of the window including border. @@ -1360,15 +1374,81 @@ condlst_add(wincond_t **pcondlst, const char *pattern); static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); -static win * -find_win(session_t *ps, Window id); +/** + * Find a window from window id in window linked list of the session. + */ +static inline win * +find_win(session_t *ps, Window id) { + if (!id) + return NULL; -static win * -find_toplevel(session_t *ps, Window id); + win *w; + + for (w = ps->list; w; w = w->next) { + if (w->id == id && !w->destroyed) + return w; + } + + return 0; +} + +/** + * Find out the WM frame of a client window using existing data. + * + * @param w window ID + * @return struct _win object of the found window, NULL if not found + */ +static inline win * +find_toplevel(session_t *ps, Window id) { + if (!id) + return NULL; + + for (win *w = ps->list; w; w = w->next) { + if (w->client_win == id && !w->destroyed) + return w; + } + + return NULL; +} + +/** + * Clear leader cache of all windows. + */ +static void +clear_cache_win_leaders(session_t *ps) { + for (win *w = ps->list; w; w = w->next) + w->cache_leader = None; +} static win * find_toplevel2(session_t *ps, Window wid); +static Window +win_get_leader(session_t *ps, win *w); + +static Window +win_get_leader_raw(session_t *ps, win *w, int recursions); + +/** + * Return whether a window group is really focused. + * + * @param leader leader window ID + * @return true if the window group is focused, false otherwise + */ +static inline bool +group_is_focused(session_t *ps, Window leader) { + if (!leader) + return false; + + for (win *w = ps->list; w; w = w->next) { + if (win_get_leader(ps, w) == leader && !w->destroyed + && w->focused_real) + return true; + } + + return false; +} + static win * recheck_focus(session_t *ps); @@ -1449,6 +1529,15 @@ calc_opacity(session_t *ps, win *w); static void calc_dim(session_t *ps, win *w); +static Window +wid_get_prop_window(session_t *ps, Window wid, Atom aprop); + +static void +win_update_leader(session_t *ps, win *w); + +static void +win_set_leader(session_t *ps, win *w, Window leader); + /** * Update focused state of a window. */ @@ -1467,10 +1556,35 @@ win_update_focused(session_t *ps, win *w) { || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) w->focused = true; + // If window grouping detection is enabled, mark the window active if + // its group is + if (ps->o.track_leader && ps->active_leader + && win_get_leader(ps, w) == ps->active_leader) { + w->focused = true; + } + if (w->focused != focused_old) w->flags |= WFLAG_OPCT_CHANGE; } +/** + * Run win_update_focused() on all windows with the same leader window. + * + * @param leader leader window ID + */ +static inline void +group_update_focused(session_t *ps, Window leader) { + if (!leader) + return; + + for (win *w = ps->list; w; w = w->next) { + if (win_get_leader(ps, w) == leader && !w->destroyed) + win_update_focused(ps, w); + } + + return; +} + /** * Set real focused state of a window. */ @@ -1480,8 +1594,38 @@ win_set_focused(session_t *ps, win *w, bool focused) { if (IsUnmapped == w->a.map_state) return; - w->focused_real = focused; - win_update_focused(ps, w); + if (w->focused_real != focused) { + w->focused_real = focused; + + // If window grouping detection is enabled + if (ps->o.track_leader && win_get_leader(ps, w)) { + Window leader = win_get_leader(ps, w); + + // If the window gets focused, replace the old active_leader + if (w->focused_real && leader != ps->active_leader) { + Window active_leader_old = ps->active_leader; + + ps->active_leader = leader; + + group_update_focused(ps, active_leader_old); + group_update_focused(ps, leader); + } + // If the group get unfocused, remove it from active_leader + else if (!w->focused_real && leader == ps->active_leader + && !group_is_focused(ps, leader)) { + ps->active_leader = None; + group_update_focused(ps, leader); + } + else { + // The window itself must be updated anyway + win_update_focused(ps, w); + } + } + // Otherwise, only update the window itself + else { + win_update_focused(ps, w); + } + } } static void From 844a51b45a7596c4dcf84068c0f39ea994248ceb Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 12 Dec 2012 12:34:56 +0800 Subject: [PATCH 095/178] Misc: Code cleanup - Move some long functions to ./src/compton.c . - Fix a small potential issue in win_set_focused() when a window with neither leader nor client window is focused. - Add DEBUG_LEADER. --- compton.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++------- compton.h | 86 +++++++++---------------------------------------------- 2 files changed, 88 insertions(+), 82 deletions(-) diff --git a/compton.c b/compton.c index c1016769f..a90538110 100644 --- a/compton.c +++ b/compton.c @@ -2704,6 +2704,76 @@ wid_get_prop_window(session_t *ps, Window wid, Atom aprop) { return p; } +/** + * Update focused state of a window. + */ +static void +win_update_focused(session_t *ps, win *w) { + bool focused_old = w->focused; + + w->focused = w->focused_real; + + // Use wintype_focus, and treat WM windows and override-redirected + // windows specially + if (ps->o.wintype_focus[w->window_type] + || (ps->o.mark_wmwin_focused && w->wmwin) + || (ps->o.mark_ovredir_focused + && w->id == w->client_win && !w->wmwin) + || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) + w->focused = true; + + // If window grouping detection is enabled, mark the window active if + // its group is + if (ps->o.track_leader && ps->active_leader + && win_get_leader(ps, w) == ps->active_leader) { + w->focused = true; + } + + if (w->focused != focused_old) + w->flags |= WFLAG_OPCT_CHANGE; +} + +/** + * Set real focused state of a window. + */ +static void +win_set_focused(session_t *ps, win *w, bool focused) { + // Unmapped windows will have their focused state reset on map + if (IsUnmapped == w->a.map_state) + return; + + if (w->focused_real != focused) { + w->focused_real = focused; + + // If window grouping detection is enabled + if (ps->o.track_leader) { + Window leader = win_get_leader(ps, w); + + // If the window gets focused, replace the old active_leader + if (w->focused_real && leader != ps->active_leader) { + Window active_leader_old = ps->active_leader; + + ps->active_leader = leader; + + group_update_focused(ps, active_leader_old); + group_update_focused(ps, leader); + } + // If the group get unfocused, remove it from active_leader + else if (!w->focused_real && leader && leader == ps->active_leader + && !group_is_focused(ps, leader)) { + ps->active_leader = None; + group_update_focused(ps, leader); + } + + // The window itself must be updated anyway + win_update_focused(ps, w); + } + // Otherwise, only update the window itself + else { + win_update_focused(ps, w); + } + } +} /** * Update leader of a window. */ @@ -2719,6 +2789,10 @@ win_update_leader(session_t *ps, win *w) { leader = wid_get_prop_window(ps, w->client_win, ps->atom_client_leader); win_set_leader(ps, w, leader); + +#ifdef DEBUG_LEADER + printf_dbgf("(%#010lx): client %#010lx, leader %#010lx, cache %#010lx\n", w->id, w->client_win, w->leader, win_get_leader(ps, w)); +#endif } /** @@ -2752,16 +2826,6 @@ win_set_leader(session_t *ps, win *w, Window nleader) { } } -/** - * Get the leader of a window. - * - * This function updates w->cache_leader if necessary. - */ -static Window -win_get_leader(session_t *ps, win *w) { - return win_get_leader_raw(ps, w, 0); -} - /** * Internal function of win_get_leader(). */ diff --git a/compton.h b/compton.h index 3b13b8f51..16272e6f5 100644 --- a/compton.h +++ b/compton.h @@ -17,6 +17,7 @@ // #define DEBUG_REDIR 1 // #define DEBUG_ALLOC_REG 1 // #define DEBUG_FRAME 1 +// #define DEBUG_LEADER 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled @@ -1423,12 +1424,19 @@ clear_cache_win_leaders(session_t *ps) { static win * find_toplevel2(session_t *ps, Window wid); -static Window -win_get_leader(session_t *ps, win *w); - static Window win_get_leader_raw(session_t *ps, win *w, int recursions); +/** + * Get the leader of a window. + * + * This function updates w->cache_leader if necessary. + */ +static inline Window +win_get_leader(session_t *ps, win *w) { + return win_get_leader_raw(ps, w, 0); +} + /** * Return whether a window group is really focused. * @@ -1538,34 +1546,8 @@ win_update_leader(session_t *ps, win *w); static void win_set_leader(session_t *ps, win *w, Window leader); -/** - * Update focused state of a window. - */ -static inline void -win_update_focused(session_t *ps, win *w) { - bool focused_old = w->focused; - - w->focused = w->focused_real; - - // Use wintype_focus, and treat WM windows and override-redirected - // windows specially - if (ps->o.wintype_focus[w->window_type] - || (ps->o.mark_wmwin_focused && w->wmwin) - || (ps->o.mark_ovredir_focused - && w->id == w->client_win && !w->wmwin) - || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) - w->focused = true; - - // If window grouping detection is enabled, mark the window active if - // its group is - if (ps->o.track_leader && ps->active_leader - && win_get_leader(ps, w) == ps->active_leader) { - w->focused = true; - } - - if (w->focused != focused_old) - w->flags |= WFLAG_OPCT_CHANGE; -} +static void +win_update_focused(session_t *ps, win *w); /** * Run win_update_focused() on all windows with the same leader window. @@ -1585,48 +1567,8 @@ group_update_focused(session_t *ps, Window leader) { return; } -/** - * Set real focused state of a window. - */ static inline void -win_set_focused(session_t *ps, win *w, bool focused) { - // Unmapped windows will have their focused state reset on map - if (IsUnmapped == w->a.map_state) - return; - - if (w->focused_real != focused) { - w->focused_real = focused; - - // If window grouping detection is enabled - if (ps->o.track_leader && win_get_leader(ps, w)) { - Window leader = win_get_leader(ps, w); - - // If the window gets focused, replace the old active_leader - if (w->focused_real && leader != ps->active_leader) { - Window active_leader_old = ps->active_leader; - - ps->active_leader = leader; - - group_update_focused(ps, active_leader_old); - group_update_focused(ps, leader); - } - // If the group get unfocused, remove it from active_leader - else if (!w->focused_real && leader == ps->active_leader - && !group_is_focused(ps, leader)) { - ps->active_leader = None; - group_update_focused(ps, leader); - } - else { - // The window itself must be updated anyway - win_update_focused(ps, w); - } - } - // Otherwise, only update the window itself - else { - win_update_focused(ps, w); - } - } -} +win_set_focused(session_t *ps, win *w, bool focused); static void determine_fade(session_t *ps, win *w); From 64a9b80f3b29af29c4b8fc7e6981ac1f321f768c Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 14 Dec 2012 20:32:46 +0800 Subject: [PATCH 096/178] Feature #69: Blur window background - Add window background blur support (--blur-background & --blur-background-frame), with X Render convolution filter. The performance sucks. The performance when the window is opaque but frame is transparent could be improved, but there are two possible ways and I'm hesitating. - Known issue: The blurring effect looks very ungraceful during fading. I could partially fix the problem, but it probably isn't easy to fix it completely. --- compton.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++- compton.h | 27 ++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/compton.c b/compton.c index a90538110..ceba178f2 100644 --- a/compton.c +++ b/compton.c @@ -1422,6 +1422,66 @@ win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) { w->shadow_width, w->shadow_height); } +/** + * Blur the background of a window. + */ +static inline void +win_blur_background(session_t *ps, win *w, Picture tgt_buffer, + XserverRegion reg_paint) { + // Convolution filter parameter (box blur) + // gaussian or binomial filters are definitely superior, yet looks + // like they aren't supported as of xorg-server-1.13.0 + const static XFixed convolution_blur[] = { + // Must convert to XFixed with XDoubleToFixed() + // Matrix size + XDoubleToFixed(3), XDoubleToFixed(3), + // Matrix + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + }; + // Extra pixels we have to get for the blur to work correctly. + const static int expand = 1; + + Pixmap tmp_pixmap = None; + Picture tmp_picture = None; + + int x = w->a.x; + int y = w->a.y; + int wid = w->widthb; + int hei = w->heightb; + + int xe = x - expand; + int ye = y - expand; + int wide = wid + expand * 2; + int heie = hei + expand * 2; + + // Directly copying from tgt_buffer does not work, so we create a + // Picture in the middle. We expand the region slightly, to make sure + // the blur on border pixels work correctly. + tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wide, heie, ps->depth); + if (!tmp_pixmap) + goto win_blur_background_err; + + tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap, + XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0); + if (!tmp_picture) + goto win_blur_background_err; + + // Copy the content to tmp_picture, then copy back. + // We lift the PictureClipRegion here, to get the expanded pixels. + XFixesSetPictureClipRegion(ps->dpy, tgt_buffer, 0, 0, None); + XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, xe, ye, 0, 0, 0, 0, wide, heie); + XFixesSetPictureClipRegion(ps->dpy, tgt_buffer, 0, 0, reg_paint); + XRenderSetPictureFilter(ps->dpy, tmp_picture, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); + XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, expand, expand, 0, 0, x, y, wid, hei); + xrfilter_reset(ps, tmp_picture); + +win_blur_background_err: + free_pixmap(ps, &tmp_pixmap); + free_picture(ps, &tmp_picture); +} + /** * Paint a window itself and dim it if asked. */ @@ -1610,7 +1670,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { // Detect if the region is empty before painting if (region == reg_paint || !is_region_empty(ps, reg_paint)) { - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, + reg_paint); win_paint_shadow(ps, w, ps->tgt_buffer); } @@ -1638,6 +1699,11 @@ paint_all(session_t *ps, XserverRegion region, win *t) { if (!is_region_empty(ps, reg_paint)) { XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); + // Blur window background + if ((ps->o.blur_background && WINDOW_SOLID != w->mode) + || (ps->o.blur_background_frame && w->frame_opacity)) { + win_blur_background(ps, w, ps->tgt_buffer, reg_paint); + } // Painting the window win_paint_win(ps, w, ps->tgt_buffer); @@ -1670,7 +1736,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { // DBE painting mode, only need to swap the buffer if (ps->o.dbe) { XdbeSwapInfo swap_info = { - .swap_window = (ps->o.paint_on_overlay ? ps->overlay: ps->root), + .swap_window = get_tgt_window(ps), // Is it safe to use XdbeUndefined? .swap_action = XdbeCopied }; @@ -3689,6 +3755,14 @@ usage(void) { " Use WM_CLIENT_LEADER to group windows, and consider windows in\n" " the same group focused at the same time. WM_TRANSIENT_FOR has\n" " higher priority if --detect-transient is enabled, too.\n" + "--blur-background\n" + " Blur background of semi-transparent / ARGB windows. Bad in\n" + " performance. The switch name may change without prior\n" + " notifications.\n" + "--blur-background-frame\n" + " Blur background of windows when the window frame is not opaque.\n" + " Implies --blur-background. Bad in performance. The switch name\n" + " may change.\n" "\n" "Format of a condition:\n" "\n" @@ -4095,6 +4169,11 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude"); // --focus-exclude parse_cfg_condlst(&cfg, &ps->o.focus_blacklist, "focus-exclude"); + // --blur-background + lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background); + // --blur-background-frame + lcfg_lookup_bool(&cfg, "blur-background-frame", + &ps->o.blur_background_frame); // Wintype settings { wintype_t i; @@ -4154,6 +4233,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "inactive-dim-fixed", no_argument, NULL, 280 }, { "detect-transient", no_argument, NULL, 281 }, { "detect-client-leader", no_argument, NULL, 282 }, + { "blur-background", no_argument, NULL, 283 }, + { "blur-background-frame", no_argument, NULL, 284 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4371,6 +4452,14 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --detect-client-leader ps->o.detect_client_leader = true; break; + case 283: + // --blur-background + ps->o.blur_background = true; + break; + case 284: + // --blur-background-frame + ps->o.blur_background_frame = true; + break; default: usage(); } @@ -4409,6 +4498,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) { ps->o.wintype_opacity[WINTYPE_POPUP_MENU] = cfgtmp.menu_opacity; } + // --blur-background-frame implies --blur-background + if (ps->o.blur_background_frame) + ps->o.blur_background = true; + // Other variables determined by options // Determine whether we need to track focus changes @@ -4785,6 +4878,32 @@ init_overlay(session_t *ps) { } } +/** + * Query needed X Render filters to check for their existence. + */ +static void +init_filters(session_t *ps) { + if (ps->o.blur_background || ps->o.blur_background_frame) { + // Query filters + XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps)); + if (pf) { + for (int i = 0; i < pf->nfilter; ++i) { + // Convolution filter + if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION)) + ps->xrfilter_convolution_exists = true; + } + } + XFree(pf); + + // Turn features off if any required filter is not present + if (!ps->xrfilter_convolution_exists) { + fprintf(stderr, "X Render convolution filter unsupported by your X server. Background blur disabled.\n"); + ps->o.blur_background = false; + ps->o.blur_background_frame = false; + } + } +} + /** * Redirect all windows. */ @@ -4908,6 +5027,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_dim = 0.0, .inactive_dim_fixed = false, .alpha_step = 0.03, + .blur_background = false, + .blur_background_frame = false, .wintype_focus = { false }, .use_ewmh_active_win = false, @@ -4977,6 +5098,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .glx_event = 0, .glx_error = 0, .dbe_exists = false, + .xrfilter_convolution_exists = false, .atom_opacity = None, .atom_frame_extents = None, @@ -5152,6 +5274,8 @@ session_init(session_t *ps_old, int argc, char **argv) { } } + init_filters(ps); + ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0); // Generates another Picture for shadows if the color is modified by diff --git a/compton.h b/compton.h index 16272e6f5..8aecaa762 100644 --- a/compton.h +++ b/compton.h @@ -116,6 +116,10 @@ #define US_PER_SEC 1000000L #define MS_PER_SEC 1000 +#define XRFILTER_CONVOLUTION "convolution" +#define XRFILTER_GUASSIAN "gaussian" +#define XRFILTER_BINOMIAL "binomial" + // Window flags // Window size is changed @@ -326,6 +330,11 @@ typedef struct { bool inactive_dim_fixed; /// Step for pregenerating alpha pictures. 0.01 - 1.0. double alpha_step; + /// Whether to blur background of semi-transparent / ARGB windows. + bool blur_background; + /// Whether to blur background when the window frame is not opaque. + /// Implies blur_background. + bool blur_background_frame; // === Focus related === /// Consider windows of specific types to be always focused. @@ -519,6 +528,8 @@ typedef struct { #endif /// Whether X DBE extension exists. bool dbe_exists; + /// Whether X Render convolution filter exists. + bool xrfilter_convolution_exists; // === Atoms === /// Atom of property _NET_WM_OPACITY. @@ -778,6 +789,22 @@ set_ignore_next(session_t *ps) { static int should_ignore(session_t *ps, unsigned long sequence); +/** + * Return the painting target window. + */ +static inline Window +get_tgt_window(session_t *ps) { + return ps->o.paint_on_overlay ? ps->overlay: ps->root; +} + +/** + * Reset filter on a Picture. + */ +static inline void +xrfilter_reset(session_t *ps, Picture p) { + XRenderSetPictureFilter(ps->dpy, p, "Nearest", NULL, 0); +} + /** * Subtract two unsigned long values. * From f4edcb2ec1618b71f09a6b93cdd123583cf3a8c4 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 15 Dec 2012 20:07:45 +0800 Subject: [PATCH 097/178] Improvement: Dynamic blur strength & improved frame blur performance - Remove the region expansion design in win_blur_background(). I must be sleep-walking when I wrote that! - Improve performance of blurring when a window is opaque but its frame is transparent. - Adjust blur strength according to window opacity. --blur-background-fixed restores the old behavior. - Add "use_offset" parameter to a few functions for convenience. Code clean-up. --- compton.c | 99 ++++++++++++++++++++++++++++++++++--------------------- compton.h | 11 ++++++- 2 files changed, 72 insertions(+), 38 deletions(-) diff --git a/compton.c b/compton.c index ceba178f2..eff195f61 100644 --- a/compton.c +++ b/compton.c @@ -545,7 +545,7 @@ win_rounded_corners(session_t *ps, win *w) { // Fetch its bounding region if (!w->border_size) - w->border_size = border_size(ps, w); + w->border_size = border_size(ps, w, true); // Quit if border_size() returns None if (!w->border_size) @@ -991,11 +991,11 @@ paint_root(session_t *ps, Picture tgt_buffer) { * Get a rectangular region a window occupies, excluding shadow. */ static XserverRegion -win_get_region(session_t *ps, win *w) { +win_get_region(session_t *ps, win *w, bool use_offset) { XRectangle r; - r.x = w->a.x; - r.y = w->a.y; + r.x = (use_offset ? w->a.x: 0); + r.y = (use_offset ? w->a.y: 0); r.width = w->widthb; r.height = w->heightb; @@ -1006,11 +1006,11 @@ win_get_region(session_t *ps, win *w) { * Get a rectangular region a window occupies, excluding frame and shadow. */ static XserverRegion -win_get_region_noframe(session_t *ps, win *w) { +win_get_region_noframe(session_t *ps, win *w, bool use_offset) { XRectangle r; - r.x = w->a.x + w->a.border_width + w->left_width; - r.y = w->a.y + w->a.border_width + w->top_width; + r.x = (use_offset ? w->a.x: 0) + w->a.border_width + w->left_width; + r.y = (use_offset ? w->a.y: 0) + w->a.border_width + w->top_width; r.width = max_i(w->a.width - w->left_width - w->right_width, 0); r.height = max_i(w->a.height - w->top_width - w->bottom_width, 0); @@ -1069,9 +1069,9 @@ win_extents(session_t *ps, win *w) { * Retrieve the bounding shape of a window. */ static XserverRegion -border_size(session_t *ps, win *w) { +border_size(session_t *ps, win *w, bool use_offset) { // Start with the window rectangular region - XserverRegion fin = win_get_region(ps, w); + XserverRegion fin = win_get_region(ps, w, use_offset); // Only request for a bounding region if the window is shaped if (w->bounding_shaped) { @@ -1089,10 +1089,12 @@ border_size(session_t *ps, win *w) { if (!border) return fin; - // Translate the region to the correct place - XFixesTranslateRegion(ps->dpy, border, - w->a.x + w->a.border_width, - w->a.y + w->a.border_width); + if (use_offset) { + // Translate the region to the correct place + XFixesTranslateRegion(ps->dpy, border, + w->a.x + w->a.border_width, + w->a.y + w->a.border_width); + } // Intersect the bounding region we got with the window rectangle, to // make sure the bounding region is not bigger than the window @@ -1249,7 +1251,7 @@ paint_preprocess(session_t *ps, win *list) { if (to_paint) { // Fetch bounding region if (!w->border_size) { - w->border_size = border_size(ps, w); + w->border_size = border_size(ps, w, true); } // Fetch window extents @@ -1326,10 +1328,10 @@ paint_preprocess(session_t *ps, win *list) { if (w->border_size) w->reg_ignore = copy_region(ps, w->border_size); else - w->reg_ignore = win_get_region(ps, w); + w->reg_ignore = win_get_region(ps, w, true); } else { - w->reg_ignore = win_get_region_noframe(ps, w); + w->reg_ignore = win_get_region_noframe(ps, w, true); if (w->border_size) XFixesIntersectRegion(ps->dpy, w->reg_ignore, w->reg_ignore, w->border_size); @@ -1428,20 +1430,20 @@ win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) { static inline void win_blur_background(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint) { + const static int convolution_blur_size = 3; // Convolution filter parameter (box blur) // gaussian or binomial filters are definitely superior, yet looks // like they aren't supported as of xorg-server-1.13.0 - const static XFixed convolution_blur[] = { + XFixed convolution_blur[] = { // Must convert to XFixed with XDoubleToFixed() // Matrix size - XDoubleToFixed(3), XDoubleToFixed(3), + XDoubleToFixed(convolution_blur_size), + XDoubleToFixed(convolution_blur_size), // Matrix XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), }; - // Extra pixels we have to get for the blur to work correctly. - const static int expand = 1; Pixmap tmp_pixmap = None; Picture tmp_picture = None; @@ -1451,15 +1453,9 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, int wid = w->widthb; int hei = w->heightb; - int xe = x - expand; - int ye = y - expand; - int wide = wid + expand * 2; - int heie = hei + expand * 2; - // Directly copying from tgt_buffer does not work, so we create a - // Picture in the middle. We expand the region slightly, to make sure - // the blur on border pixels work correctly. - tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wide, heie, ps->depth); + // Picture in the middle. + tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wid, hei, ps->depth); if (!tmp_pixmap) goto win_blur_background_err; @@ -1468,14 +1464,31 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, if (!tmp_picture) goto win_blur_background_err; - // Copy the content to tmp_picture, then copy back. - // We lift the PictureClipRegion here, to get the expanded pixels. - XFixesSetPictureClipRegion(ps->dpy, tgt_buffer, 0, 0, None); - XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, xe, ye, 0, 0, 0, 0, wide, heie); - XFixesSetPictureClipRegion(ps->dpy, tgt_buffer, 0, 0, reg_paint); - XRenderSetPictureFilter(ps->dpy, tmp_picture, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); - XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, expand, expand, 0, 0, x, y, wid, hei); - xrfilter_reset(ps, tmp_picture); + // Adjust blur strength according to window opacity, to make it appear + // better during fading + if (!ps->o.blur_background_fixed) { + double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0); + convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(pct * 8.0 / (1.1 - pct)); + } + + // Minimize the region we try to blur, if the window itself is not + // opaque, only the frame is. + if (WINDOW_SOLID == w->mode && w->frame_opacity) { + XserverRegion reg_all = border_size(ps, w, false); + XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); + XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); + XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_noframe, 0, 0); + free_region(ps, ®_all); + free_region(ps, ®_noframe); + } + + // Copy the content to tmp_picture, then copy back. The filter must + // be applied on tgt_buffer, to get the nearby pixels outside the + // window. + XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); + XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei); + xrfilter_reset(ps, tgt_buffer); + XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); win_blur_background_err: free_pixmap(ps, &tmp_pixmap); @@ -3492,7 +3505,7 @@ ev_shape_notify(session_t *ps, XShapeEvent *ev) { // Mark the old border_size as damaged add_damage(ps, w->border_size); - w->border_size = border_size(ps, w); + w->border_size = border_size(ps, w, true); // Mark the new border_size as damaged add_damage(ps, copy_region(ps, w->border_size)); @@ -3763,6 +3776,9 @@ usage(void) { " Blur background of windows when the window frame is not opaque.\n" " Implies --blur-background. Bad in performance. The switch name\n" " may change.\n" + "--blur-background-fixed\n" + " Use fixed blur strength instead of adjusting according to window\n" + " opacity.\n" "\n" "Format of a condition:\n" "\n" @@ -4174,6 +4190,9 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { // --blur-background-frame lcfg_lookup_bool(&cfg, "blur-background-frame", &ps->o.blur_background_frame); + // --blur-background-fixed + lcfg_lookup_bool(&cfg, "blur-background-fixed", + &ps->o.blur_background_fixed); // Wintype settings { wintype_t i; @@ -4235,6 +4254,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "detect-client-leader", no_argument, NULL, 282 }, { "blur-background", no_argument, NULL, 283 }, { "blur-background-frame", no_argument, NULL, 284 }, + { "blur-background-fixed", no_argument, NULL, 285 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4460,6 +4480,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --blur-background-frame ps->o.blur_background_frame = true; break; + case 285: + // --blur-background-fixed + ps->o.blur_background_fixed = true; + break; default: usage(); } @@ -5029,6 +5053,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .alpha_step = 0.03, .blur_background = false, .blur_background_frame = false, + .blur_background_fixed = false, .wintype_focus = { false }, .use_ewmh_active_win = false, diff --git a/compton.h b/compton.h index 8aecaa762..66ed3961b 100644 --- a/compton.h +++ b/compton.h @@ -335,6 +335,9 @@ typedef struct { /// Whether to blur background when the window frame is not opaque. /// Implies blur_background. bool blur_background_frame; + /// Whether to use fixed blur strength instead of adjusting according + /// to window opacity. + bool blur_background_fixed; // === Focus related === /// Consider windows of specific types to be always focused. @@ -1493,11 +1496,17 @@ root_tile_f(session_t *ps); static void paint_root(session_t *ps, Picture tgt_buffer); +static XserverRegion +win_get_region(session_t *ps, win *w, bool use_offset); + +static XserverRegion +win_get_region_noframe(session_t *ps, win *w, bool use_offset); + static XserverRegion win_extents(session_t *ps, win *w); static XserverRegion -border_size(session_t *ps, win *w); +border_size(session_t *ps, win *w, bool use_offset); static Window find_client_win(session_t *ps, Window w); From 03ffecb4a0de95300fce6419abda3177db016f0b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 8 Jan 2013 08:50:58 +0800 Subject: [PATCH 098/178] Improvement #74: Use libevent for main loop - Use libevent for main loop. I will explain the reasons in #56 later. The preferred libevent version is 2.x, yet 1.4.x should work as well. - As a result, compton now should build fine on *BSD. Thanks to DachiChang for the FreeBSD build issue report. - Another consequence is we now use microsecond-level timing all the way. Nanosecond-level code will be dropped soon. Start using long instead of unsigned long to represent time in milliseconds, as both can't hold the full epoch time in ms, anyway, and a signed type requires less care in subtraction. Wrap the epoch time in ms to 15 days. - Fix broken NO_VSYNC_DRM and NO_VSYNC_OPENGL compile-time options. - Use git revision number for versioning in Makefile, and other small improvements. - Reorganize struct _win. Drop unused w->damaged_sequence. w->damaged is turned to bool. - Add type and format to winprop_t, as preparation for the new condition format. - Add w->shadow_force and w->focus_force, to prepare for D-Bus support. - Rename wid_get_prop() to wid_get_prop_adv() with more options. Add wrapper function wid_get_prop(). - Add some extra helper functions, for D-Bus support later. - Make some functions return a bool value to indicate if it's successful. - Modify add_win(), use a static const structure to initialize the new struct _win. - Add some helper macros, like printf_err(f)(q). Make some errors fatal. - Rename some types, constants, and functions. Code clean-up. - Check for time disorder in paint_preprocess() when calculating fading steps. - Rename evpoll() to swopti_handle_timeout(), and partially rewrite it. - Make -h / --help legal. - Known issue: compton segfaults on FreeBSD with nvidia-drivers, unless NO_VSYNC_OPENGL is used. Will look into it later. Thamls to DachiChang for reporting. --- compton.c | 519 +++++++++++++++++++++++++++++++++--------------------- compton.h | 314 +++++++++++++++++++++++++-------- 2 files changed, 561 insertions(+), 272 deletions(-) diff --git a/compton.c b/compton.c index eff195f61..770712658 100644 --- a/compton.c +++ b/compton.c @@ -1196,15 +1196,19 @@ paint_preprocess(session_t *ps, win *list) { bool is_highest = true; // Fading step calculation - unsigned steps = (sub_unslong(get_time_ms(), ps->fade_time) - + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta; + time_ms_t steps = ((get_time_ms() - ps->fade_time) + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta; + if (steps < 0L) { + // Time disorder + ps->fade_time = get_time_ms(); + steps = 0; + } ps->fade_time += steps * ps->o.fade_delta; XserverRegion last_reg_ignore = None; for (w = list; w; w = next) { bool to_paint = true; - const winmode mode_old = w->mode; + const winmode_t mode_old = w->mode; // In case calling the fade callback function destroys this window next = w->next; @@ -1274,7 +1278,7 @@ paint_preprocess(session_t *ps, win *list) { else w->frame_opacity = 0.0; - if (w->to_paint && WINDOW_SOLID == mode_old + if (w->to_paint && WMODE_SOLID == mode_old && (0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) ps->reg_ignore_expire = true; } @@ -1308,8 +1312,8 @@ paint_preprocess(session_t *ps, win *list) { } - if ((to_paint && WINDOW_SOLID == w->mode) - != (w->to_paint && WINDOW_SOLID == mode_old)) + if ((to_paint && WMODE_SOLID == w->mode) + != (w->to_paint && WMODE_SOLID == mode_old)) ps->reg_ignore_expire = true; // Add window to damaged area if its painting status changes @@ -1323,7 +1327,7 @@ paint_preprocess(session_t *ps, win *list) { // If the window is solid, we add the window region to the // ignored region - if (WINDOW_SOLID == w->mode) { + if (WMODE_SOLID == w->mode) { if (!w->frame_opacity) { if (w->border_size) w->reg_ignore = copy_region(ps, w->border_size); @@ -1352,7 +1356,7 @@ paint_preprocess(session_t *ps, win *list) { if (is_highest && to_paint) { is_highest = false; - if (WINDOW_SOLID == w->mode + if (WMODE_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) && win_is_fullscreen(ps, w)) ps->unredir_possible = true; @@ -1473,7 +1477,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. - if (WINDOW_SOLID == w->mode && w->frame_opacity) { + if (WMODE_SOLID == w->mode && w->frame_opacity) { XserverRegion reg_all = border_size(ps, w, false); XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); @@ -1506,7 +1510,7 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { int hei = w->heightb; Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); - int op = (w->mode == WINDOW_SOLID ? PictOpSrc: PictOpOver); + int op = (w->mode == WMODE_SOLID ? PictOpSrc: PictOpOver); if (!w->frame_opacity) { XRenderComposite(ps->dpy, op, w->picture, alpha_mask, @@ -1713,7 +1717,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { if (!is_region_empty(ps, reg_paint)) { XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); // Blur window background - if ((ps->o.blur_background && WINDOW_SOLID != w->mode) + if ((ps->o.blur_background && WMODE_SOLID != w->mode) || (ps->o.blur_background_frame && w->frame_opacity)) { win_blur_background(ps, w, ps->tgt_buffer, reg_paint); } @@ -1815,7 +1819,7 @@ repair_win(session_t *ps, win *w) { XFixesSubtractRegion(ps->dpy, parts, parts, w->prev_trans->reg_ignore); add_damage(ps, parts); - w->damaged = 1; + w->damaged = true; } static wintype_t @@ -1916,7 +1920,7 @@ map_win(session_t *ps, Window id) { // Check for _COMPTON_SHADOW if (ps->o.respect_prop_shadow) - win_update_attr_shadow_raw(ps, w); + win_update_prop_shadow_raw(ps, w); // Many things above could affect shadow determine_shadow(ps, w); @@ -1933,7 +1937,7 @@ map_win(session_t *ps, Window id) { determine_fade(ps, w); } - w->damaged = 1; + w->damaged = true; /* if any configure events happened while @@ -1952,7 +1956,7 @@ finish_map_win(session_t *ps, win *w) { static void finish_unmap_win(session_t *ps, win *w) { - w->damaged = 0; + w->damaged = false; update_reg_ignore_expire(ps, w); @@ -2016,7 +2020,7 @@ get_opacity_percent(win *w) { static void determine_mode(session_t *ps, win *w) { - winmode mode; + winmode_t mode = WMODE_SOLID; XRenderPictFormat *format; /* if trans prop == -1 fall back on previous tests */ @@ -2029,11 +2033,11 @@ determine_mode(session_t *ps, win *w) { if (format && format->type == PictTypeDirect && format->direct.alphaMask) { - mode = WINDOW_ARGB; + mode = WMODE_ARGB; } else if (w->opacity != OPAQUE) { - mode = WINDOW_TRANS; + mode = WMODE_TRANS; } else { - mode = WINDOW_SOLID; + mode = WMODE_SOLID; } w->mode = mode; @@ -2153,15 +2157,15 @@ win_update_shape(session_t *ps, win *w) { * The property must be set on the outermost window, usually the WM frame. */ static void -win_update_attr_shadow_raw(session_t *ps, win *w) { +win_update_prop_shadow_raw(session_t *ps, win *w) { winprop_t prop = wid_get_prop(ps, w->id, ps->atom_compton_shadow, 1, XA_CARDINAL, 32); if (!prop.nitems) { - w->attr_shadow = -1; + w->prop_shadow = -1; } else { - w->attr_shadow = *prop.data.p32; + w->prop_shadow = *prop.data.p32; } free_winprop(&prop); @@ -2172,12 +2176,12 @@ win_update_attr_shadow_raw(session_t *ps, win *w) { * things. */ static void -win_update_attr_shadow(session_t *ps, win *w) { - long attr_shadow_old = w->attr_shadow; +win_update_prop_shadow(session_t *ps, win *w) { + long attr_shadow_old = w->prop_shadow; - win_update_attr_shadow_raw(ps, w); + win_update_prop_shadow_raw(ps, w); - if (w->attr_shadow != attr_shadow_old) + if (w->prop_shadow != attr_shadow_old) determine_shadow(ps, w); } @@ -2189,11 +2193,13 @@ static void determine_shadow(session_t *ps, win *w) { bool shadow_old = w->shadow; - w->shadow = (ps->o.wintype_shadow[w->window_type] - && !win_match(w, ps->o.shadow_blacklist, &w->cache_sblst) - && !(ps->o.shadow_ignore_shaped && w->bounding_shaped - && !w->rounded_corners) - && !(ps->o.respect_prop_shadow && 0 == w->attr_shadow)); + w->shadow = (UNSET == w->shadow_force ? + (ps->o.wintype_shadow[w->window_type] + && !win_match(w, ps->o.shadow_blacklist, &w->cache_sblst) + && !(ps->o.shadow_ignore_shaped && w->bounding_shaped + && !w->rounded_corners) + && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)) + : w->shadow_force); // Window extents need update on shadow state change if (w->shadow != shadow_old) { @@ -2340,18 +2346,98 @@ win_recheck_client(session_t *ps, win *w) { win_mark_client(ps, w, cw); } -static void +static bool add_win(session_t *ps, Window id, Window prev) { + const static win win_def = { + .next = NULL, + .prev_trans = NULL, + + .id = None, + .a = { }, + .mode = WMODE_TRANS, + .damaged = false, + .damage = None, + .pixmap = None, + .picture = None, + .border_size = None, + .extents = None, + .flags = 0, + .need_configure = false, + .queue_configure = { }, + .reg_ignore = None, + .destroyed = false, + .widthb = 0, + .heightb = 0, + .bounding_shaped = false, + .rounded_corners = false, + .to_paint = false, + + .client_win = None, + .window_type = WINTYPE_UNKNOWN, + .wmwin = false, + .leader = None, + .cache_leader = None, + + .focused = false, + .focused_force = UNSET, + .focused_real = false, + + .name = NULL, + .class_instance = NULL, + .class_general = NULL, + .role = NULL, + .cache_sblst = NULL, + .cache_fblst = NULL, + .cache_fcblst = NULL, + + .opacity = 0, + .opacity_tgt = 0, + .opacity_prop = OPAQUE, + .opacity_prop_client = OPAQUE, + .alpha_pict = None, + + .fade = false, + .fade_callback = NULL, + + .frame_opacity = 0.0, + .frame_alpha_pict = None, + .left_width = 0, + .right_width = 0, + .top_width = 0, + .bottom_width = 0, + + .shadow = false, + .shadow_force = UNSET, + .shadow_opacity = 0.0, + .shadow_dx = 0, + .shadow_dy = 0, + .shadow_width = 0, + .shadow_height = 0, + .shadow_pict = None, + .shadow_alpha_pict = None, + .prop_shadow = -1, + + .dim = false, + .dim_alpha_pict = None, + }; + // Reject overlay window and already added windows if (id == ps->overlay || find_win(ps, id)) { - return; + return false; } + // Allocate and initialize the new win structure win *new = malloc(sizeof(win)); - win **p; - if (!new) return; + if (!new) { + printf_errf("(%#010lx): Failed to allocate memory for the new window.", id); + return false; + } + + memcpy(new, &win_def, sizeof(win)); + // Find window insertion point + win **p = NULL; if (prev) { for (p = &ps->list; *p; p = &(*p)->next) { if ((*p)->id == prev && !(*p)->destroyed) @@ -2361,12 +2447,15 @@ add_win(session_t *ps, Window id, Window prev) { p = &ps->list; } + // Fill structure new->id = id; set_ignore_next(ps); if (!XGetWindowAttributes(ps->dpy, id, &new->a)) { + // Failed to get window attributes. Which probably means, the window + // is gone already. free(new); - return; + return false; } // Delay window mapping @@ -2374,76 +2463,12 @@ add_win(session_t *ps, Window id, Window prev) { assert(IsViewable == map_state || IsUnmapped == map_state); new->a.map_state = IsUnmapped; - new->damaged = 0; - new->to_paint = false; - new->pixmap = None; - new->picture = None; - - if (new->a.class == InputOnly) { - new->damage_sequence = 0; - new->damage = None; - } else { - new->damage_sequence = NextRequest(ps->dpy); + // Create Damage for window + if (InputOutput == new->a.class) { set_ignore_next(ps); new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); } - new->name = NULL; - new->class_instance = NULL; - new->class_general = NULL; - new->role = NULL; - new->cache_sblst = NULL; - new->cache_fblst = NULL; - new->cache_fcblst = NULL; - new->bounding_shaped = false; - new->rounded_corners = false; - - new->border_size = None; - new->reg_ignore = None; - new->extents = None; - new->shadow = false; - new->shadow_opacity = 0.0; - new->shadow_pict = None; - new->shadow_alpha_pict = None; - new->shadow_dx = 0; - new->shadow_dy = 0; - new->shadow_width = 0; - new->shadow_height = 0; - new->opacity = 0; - new->opacity_tgt = 0; - new->opacity_prop = OPAQUE; - new->opacity_prop_client = OPAQUE; - new->fade = false; - new->fade_callback = NULL; - new->alpha_pict = None; - new->frame_opacity = 1.0; - new->frame_alpha_pict = None; - - new->dim = false; - new->dim_alpha_pict = None; - - new->focused = false; - new->focused_real = false; - new->leader = None; - new->cache_leader = None; - new->destroyed = false; - new->need_configure = false; - new->window_type = WINTYPE_UNKNOWN; - new->mode = WINDOW_TRANS; - new->attr_shadow = -1; - - new->prev_trans = NULL; - - new->left_width = 0; - new->right_width = 0; - new->top_width = 0; - new->bottom_width = 0; - - new->client_win = 0; - new->wmwin = false; - - new->flags = 0; - calc_win_size(ps, new); new->next = *p; @@ -2452,6 +2477,8 @@ add_win(session_t *ps, Window id, Window prev) { if (map_state == IsViewable) { map_win(ps, id); } + + return true; } static void @@ -2790,22 +2817,27 @@ static void win_update_focused(session_t *ps, win *w) { bool focused_old = w->focused; - w->focused = w->focused_real; - - // Use wintype_focus, and treat WM windows and override-redirected - // windows specially - if (ps->o.wintype_focus[w->window_type] - || (ps->o.mark_wmwin_focused && w->wmwin) - || (ps->o.mark_ovredir_focused - && w->id == w->client_win && !w->wmwin) - || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) - w->focused = true; - - // If window grouping detection is enabled, mark the window active if - // its group is - if (ps->o.track_leader && ps->active_leader - && win_get_leader(ps, w) == ps->active_leader) { - w->focused = true; + if (UNSET != w->focused_force) { + w->focused = w->focused_force; + } + else { + w->focused = w->focused_real; + + // Use wintype_focus, and treat WM windows and override-redirected + // windows specially + if (ps->o.wintype_focus[w->window_type] + || (ps->o.mark_wmwin_focused && w->wmwin) + || (ps->o.mark_ovredir_focused + && w->id == w->client_win && !w->wmwin) + || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) + w->focused = true; + + // If window grouping detection is enabled, mark the window active if + // its group is + if (ps->o.track_leader && ps->active_leader + && win_get_leader(ps, w) == ps->active_leader) { + w->focused = true; + } } if (w->focused != focused_old) @@ -3471,7 +3503,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (ps->o.respect_prop_shadow && ps->atom_compton_shadow == ev->atom) { win *w = find_win(ps, ev->window); if (w) - win_update_attr_shadow(ps, w); + win_update_prop_shadow(ps, w); } // If a leader property changes @@ -3649,7 +3681,7 @@ ev_handle(session_t *ps, XEvent *ev) { static void usage(void) { const static char *usage_text = - "compton (development version)\n" + "compton (" COMPTON_VERSION ")\n" "usage: compton [options]\n" "Options:\n" "\n" @@ -3888,15 +3920,16 @@ register_cm(session_t *ps, bool want_glxct) { /** * Fork program to background and disable all I/O streams. */ -static void +static bool fork_after(void) { - if (getppid() == 1) return; + if (getppid() == 1) + return true; int pid = fork(); - if (pid == -1) { - fprintf(stderr, "Fork failed\n"); - return; + if (-1 == pid) { + printf_errf("(): fork() failed."); + return false; } if (pid > 0) _exit(0); @@ -3906,11 +3939,17 @@ fork_after(void) { // Mainly to suppress the _FORTIFY_SOURCE warning bool success = freopen("/dev/null", "r", stdin); success = freopen("/dev/null", "w", stdout) && success; - if (!success) - fprintf(stderr, "fork_after(): freopen() failed."); + if (!success) { + printf_errf("(): freopen() failed."); + return false; + } success = freopen("/dev/null", "w", stderr); - if (!success) - fprintf(stderr, "fork_after(): freopen() failed."); + if (!success) { + printf_errf("(): freopen() failed."); + return false; + } + + return true; } #ifdef CONFIG_LIBCONFIG @@ -4059,7 +4098,7 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { f = open_config_file(cpath, &path); if (!f) { if (cpath) - printf("Failed to read the specified configuration file.\n"); + printf_errfq(1, "(): Failed to read the specified configuration file."); return; } @@ -4223,8 +4262,9 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { */ static void get_cfg(session_t *ps, int argc, char *const *argv) { - const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:scnfFCaSzGb"; + const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb"; const static struct option longopts[] = { + { "help", no_argument, NULL, 'h' }, { "config", required_argument, NULL, 256 }, { "shadow-red", required_argument, NULL, 257 }, { "shadow-green", required_argument, NULL, 258 }, @@ -4255,6 +4295,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "blur-background", no_argument, NULL, 283 }, { "blur-background-frame", no_argument, NULL, 284 }, { "blur-background-fixed", no_argument, NULL, 285 }, + { "dbus", no_argument, NULL, 286 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4303,6 +4344,9 @@ get_cfg(session_t *ps, int argc, char *const *argv) { (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { switch (o) { // Short options + case 'h': + usage(); + break; case 'd': ps->o.display = mstrcpy(optarg); break; @@ -4484,6 +4528,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --blur-background-fixed ps->o.blur_background_fixed = true; break; + case 286: + // --dbus + ps->o.dbus = true; + break; default: usage(); } @@ -4609,7 +4657,7 @@ update_refresh_rate(session_t *ps) { XRRFreeScreenConfigInfo(randr_info); if (ps->refresh_rate) - ps->refresh_intv = NS_PER_SEC / ps->refresh_rate; + ps->refresh_intv = US_PER_SEC / ps->refresh_rate; else ps->refresh_intv = 0; } @@ -4620,12 +4668,12 @@ update_refresh_rate(session_t *ps) { * @return true for success, false otherwise */ static bool -sw_opti_init(session_t *ps) { +swopti_init(session_t *ps) { // Prepare refresh rate // Check if user provides one ps->refresh_rate = ps->o.refresh_rate; if (ps->refresh_rate) - ps->refresh_intv = NS_PER_SEC / ps->refresh_rate; + ps->refresh_intv = US_PER_SEC / ps->refresh_rate; // Auto-detect refresh rate otherwise if (!ps->refresh_rate && ps->randr_exists) { @@ -4661,61 +4709,62 @@ lceil_ntimes(long dividend, long divisor) { } /** - * Wait for events until next paint. + * Modify a struct timeval timeout value to render at a fixed pace. * - * Optionally use refresh-rate based optimization to reduce painting. - * - * @param fd struct pollfd used for poll() - * @param timeout second timeout (fading timeout) - * @return > 0 if we get some events, 0 if timeout is reached, < 0 on - * problems + * @param ps current session + * @param[in,out] ptv pointer to the timeout */ -static int -evpoll(session_t *ps, int timeout) { - struct pollfd ufd = { - .fd = ConnectionNumber(ps->dpy), - .events = POLLIN - }; - - // Always wait infinitely if asked so, to minimize CPU usage - if (timeout < 0) { - int ret = poll(&ufd, 1, timeout); - // Reset ps->fade_time so the fading steps during idling are not counted - ps->fade_time = get_time_ms(); - return ret; - } - - // Just do a poll() if we are not using optimization - if (!ps->o.sw_opti) - return poll(&ufd, 1, timeout); - - // Convert the old timeout to struct timespec - struct timespec next_paint_tmout = { - .tv_sec = timeout / MS_PER_SEC, - .tv_nsec = timeout % MS_PER_SEC * (NS_PER_SEC / MS_PER_SEC) - }; +static void +swopti_handle_timeout(session_t *ps, struct timeval *ptv) { + if (!ptv) + return; - // Get the nanosecond offset of the time when the we reach the timeout + // Get the microsecond offset of the time when the we reach the timeout // I don't think a 32-bit long could overflow here. - long target_relative_offset = (next_paint_tmout.tv_nsec + get_time_timespec().tv_nsec - ps->paint_tm_offset) % NS_PER_SEC; - if (target_relative_offset < 0) - target_relative_offset += NS_PER_SEC; + long offset = (ptv->tv_usec + get_time_timeval().tv_usec - ps->paint_tm_offset) % ps->refresh_intv; + if (offset < 0) + offset += ps->refresh_intv; - assert(target_relative_offset >= 0); + assert(offset >= 0 && offset < ps->refresh_intv); // If the target time is sufficiently close to a refresh time, don't add // an offset, to avoid certain blocking conditions. - if ((target_relative_offset % NS_PER_SEC) < SW_OPTI_TOLERANCE) - return poll(&ufd, 1, timeout); + if (offset < SWOPTI_TOLERANCE + || offset > ps->refresh_intv - SWOPTI_TOLERANCE) + return; // Add an offset so we wait until the next refresh after timeout - next_paint_tmout.tv_nsec += lceil_ntimes(target_relative_offset, ps->refresh_intv) - target_relative_offset; - if (next_paint_tmout.tv_nsec > NS_PER_SEC) { - next_paint_tmout.tv_nsec -= NS_PER_SEC; - ++next_paint_tmout.tv_sec; + ptv->tv_usec += ps->refresh_intv - offset; + if (ptv->tv_usec > US_PER_SEC) { + ptv->tv_usec -= US_PER_SEC; + ++ptv->tv_sec; + } +} + +/** + * Libevent callback function to handle X events. + */ +static void +evcallback_x(evutil_socket_t fd, short what, void *arg) { + session_t *ps = ps_g; + + // Sometimes poll() returns 1 but no events are actually read, + // causing XNextEvent() to block, I have no idea what's wrong, so we + // check for the number of events here + if (XEventsQueued(ps->dpy, QueuedAfterReading)) { + XEvent ev = { }; + + XNextEvent(ps->dpy, &ev); + ev_handle(ps, &ev); + ps->ev_received = true; } +} - return ppoll(&ufd, 1, &next_paint_tmout, NULL); +/** + * NULL libevent callback function. + */ +static void +evcallback_null(evutil_socket_t fd, short what, void *arg) { } /** @@ -4981,6 +5030,45 @@ redir_stop(session_t *ps) { } } +/** + * Main loop. + */ +static bool +mainloop(session_t *ps) { + bool infinite_wait = false; + + // Process existing events + if (XEventsQueued(ps->dpy, QueuedAfterReading)) { + evcallback_x(ConnectionNumber(ps->dpy), 0, NULL); + return true; + } + + // Add timeout + if (ps->ev_received || !ps->idling) { + struct timeval tv = ms_to_tv(ps->ev_received ? 0: fade_timeout(ps)); + if (ps->o.sw_opti) + swopti_handle_timeout(ps, &tv); + assert(tv.tv_sec >= 0 && tv.tv_usec >= 0); + if (timeval_isempty(tv)) + return false; + evtimer_add(ps->ev_tmout, &tv); + } + else { + infinite_wait = true; + } + + // Run libevent main loop + if (event_base_loop(ps->ev_base, EVLOOP_ONCE)) + printf_errfq(1, "(): Unexpected error when running event loop."); + + evtimer_del(ps->ev_tmout); + + if (infinite_wait) + ps->fade_time = get_time_ms(); + + return true; +} + /** * Initialize a session. * @@ -5097,11 +5185,15 @@ session_init(session_t *ps_old, int argc, char **argv) { .refresh_intv = 0UL, .paint_tm_offset = 0L, +#ifdef CONFIG_VSYNC_DRM .drm_fd = 0, +#endif +#ifdef CONFIG_VSYNC_OPENGL .glx_context = None, .glx_get_video_sync = NULL, .glx_wait_video_sync = NULL, +#endif .xfixes_event = 0, .xfixes_error = 0, @@ -5119,9 +5211,11 @@ session_init(session_t *ps_old, int argc, char **argv) { .randr_exists = 0, .randr_event = 0, .randr_error = 0, +#ifdef CONFIG_VSYNC_OPENGL .glx_exists = false, .glx_event = 0, .glx_error = 0, +#endif .dbe_exists = false, .xrfilter_convolution_exists = false, @@ -5255,7 +5349,7 @@ session_init(session_t *ps_old, int argc, char **argv) { // Initialize software optimization if (ps->o.sw_opti) - ps->o.sw_opti = sw_opti_init(ps); + ps->o.sw_opti = swopti_init(ps); // Initialize DRM/OpenGL VSync if ((VSYNC_DRM == ps->o.vsync && !vsync_drm_init(ps)) @@ -5269,8 +5363,6 @@ session_init(session_t *ps_old, int argc, char **argv) { if (ps->o.dbe) init_dbe(ps); - if (ps->o.fork_after_register) fork_after(); - init_atoms(ps); init_alpha_picts(ps); @@ -5313,6 +5405,24 @@ session_init(session_t *ps_old, int argc, char **argv) { } ps->all_damage = None; + + // Build event base + if (!(ps->ev_base = +#ifndef CONFIG_LIBEVENT_LEGACY + event_base_new() +#else + event_init() +#endif + )) + printf_errfq(1, "(): Failed to build event base."); + if (!(ps->ev_x = EVENT_NEW(ps->ev_base, ConnectionNumber(ps->dpy), + EV_READ | EV_PERSIST, evcallback_x, NULL))) + printf_errfq(1, "(): Failed to build event."); + if (event_add(ps->ev_x, NULL)) + printf_errfq(1, "(): Failed to add event."); + if (!(ps->ev_tmout = evtimer_new(ps->ev_base, evcallback_null, NULL))) + printf_errfq(1, "(): Failed to build event."); + XGrabServer(ps->dpy); redir_start(ps); @@ -5345,6 +5455,18 @@ session_init(session_t *ps_old, int argc, char **argv) { XUngrabServer(ps->dpy); + // Fork to background, if asked + if (ps->o.fork_after_register) { + if (!fork_after()) { + session_destroy(ps); + return NULL; + } + + // Reinitialize event base + if (event_reinit(ps->ev_base) < 0) + printf_errfq(1, "Failed to reinitialize event base."); + } + // Free the old session if (ps_old) free(ps_old); @@ -5447,10 +5569,12 @@ session_destroy(session_t *ps) { XDestroyWindow(ps->dpy, ps->reg_win); ps->reg_win = None; } +#ifdef CONFIG_VSYNC_OPENGL if (ps->glx_context) { glXDestroyContext(ps->dpy, ps->glx_context); ps->glx_context = None; } +#endif // Free double buffer if (ps->root_dbe) { @@ -5458,11 +5582,13 @@ session_destroy(session_t *ps) { ps->root_dbe = None; } +#ifdef CONFIG_VSYNC_DRM // Close file opened for DRM VSync if (ps->drm_fd) { close(ps->drm_fd); ps->drm_fd = 0; } +#endif // Release overlay window if (ps->overlay) { @@ -5470,6 +5596,11 @@ session_destroy(session_t *ps) { ps->overlay = None; } + // Free libevent things + event_free(ps->ev_x); + event_free(ps->ev_tmout); + event_base_free(ps->ev_base); + // Flush all events XSync(ps->dpy, True); @@ -5487,7 +5618,7 @@ session_run(session_t *ps) { win *t; if (ps->o.sw_opti) - ps->paint_tm_offset = get_time_timespec().tv_nsec; + ps->paint_tm_offset = get_time_timeval().tv_usec; ps->reg_ignore_expire = true; @@ -5503,22 +5634,10 @@ session_run(session_t *ps) { // Main loop while (!ps->reset) { - bool ev_received = false; - - while (XEventsQueued(ps->dpy, QueuedAfterReading) - || (evpoll(ps, - (ev_received ? 0: (ps->idling ? -1: fade_timeout(ps)))) > 0)) { - // Sometimes poll() returns 1 but no events are actually read, - // causing XNextEvent() to block, I have no idea what's wrong, so we - // check for the number of events here - if (XEventsQueued(ps->dpy, QueuedAfterReading)) { - XEvent ev; - - XNextEvent(ps->dpy, &ev); - ev_handle(ps, &ev); - ev_received = true; - } - } + ps->ev_received = false; + + while (mainloop(ps)) + continue; // idling will be turned off during paint_preprocess() if needed ps->idling = true; @@ -5530,7 +5649,7 @@ session_run(session_t *ps) { free_region(ps, &ps->all_damage); if (ps->all_damage && !is_region_empty(ps, ps->all_damage)) { - static int paint; + static int paint = 0; paint_all(ps, ps->all_damage, t); ps->reg_ignore_expire = false; paint++; @@ -5577,6 +5696,10 @@ main(int argc, char **argv) { session_t *ps_old = ps_g; while (1) { ps_g = session_init(ps_old, argc, argv); + if (!ps_g) { + printf_errf("Failed to create new session."); + return 1; + } session_run(ps_g); ps_old = ps_g; session_destroy(ps_g); diff --git a/compton.h b/compton.h index 66ed3961b..c7d65d30c 100644 --- a/compton.h +++ b/compton.h @@ -54,6 +54,21 @@ #include #include +// libevent +#ifndef CONFIG_LIBEVENT_LEGACY +#include +#else +#include +typedef int evutil_socket_t; +typedef void(* event_callback_fn)(evutil_socket_t, short, void *); +#define event_free(ev) (event_del((ev)), free((ev))) +#endif + +#ifndef evtimer_new +#define evtimer_new(b, cb, arg) EVENT_NEW((b), -1, 0, (cb), (arg)) +#endif + +// libpcre #ifdef CONFIG_REGEX_PCRE #include @@ -72,6 +87,10 @@ #include #endif +#ifdef CONFIG_DBUS +#include +#endif + #include #include #include @@ -109,9 +128,11 @@ #define REGISTER_PROP "_NET_WM_CM_S" #define FADE_DELTA_TOLERANCE 0.2 -#define SW_OPTI_TOLERANCE 1000 +#define SWOPTI_TOLERANCE 1000 #define WIN_GET_LEADER_MAX_RECURSION 20 +#define SEC_WRAP (15L * 24L * 60L * 60L) + #define NS_PER_SEC 1000000000L #define US_PER_SEC 1000000L #define MS_PER_SEC 1000 @@ -134,10 +155,27 @@ // #define MSTR_(s) #s // #define MSTR(s) MSTR_(s) +/// Print out an error message. +#define printf_err(format, ...) \ + fprintf(stderr, format "\n", ## __VA_ARGS__) + +/// Print out an error message with function name. +#define printf_errf(format, ...) \ + printf_err("%s" format, __func__, ## __VA_ARGS__) + +/// Print out an error message with function name, and quit with a +/// specific exit code. +#define printf_errfq(code, format, ...) { \ + printf_err("%s" format, __func__, ## __VA_ARGS__); \ + exit(code); \ +} + +/// Print out a debug message. #define printf_dbg(format, ...) \ printf(format, ## __VA_ARGS__); \ fflush(stdout) +/// Print out a debug message with function name. #define printf_dbgf(format, ...) \ printf_dbg("%s" format, __func__, ## __VA_ARGS__) @@ -148,6 +186,7 @@ // === Types === typedef uint32_t opacity_t; +typedef long time_ms_t; typedef enum { WINTYPE_UNKNOWN, @@ -175,12 +214,14 @@ typedef enum { UNSET } switch_t; +/// Enumeration type of window painting mode. typedef enum { - WINDOW_SOLID, - WINDOW_TRANS, - WINDOW_ARGB -} winmode; + WMODE_TRANS, + WMODE_SOLID, + WMODE_ARGB +} winmode_t; +/// Structure representing Window property value. typedef struct { // All pointers have the same length, right? // I wanted to use anonymous union but it's a GNU extension... @@ -190,6 +231,8 @@ typedef struct { long *p32; } data; unsigned long nitems; + Atom type; + int format; } winprop_t; typedef struct _ignore { @@ -263,6 +306,8 @@ typedef struct { /// Whether to unredirect all windows if a full-screen opaque window /// is detected. bool unredir_if_possible; + /// Whether to enable D-Bus support. + bool dbus; /// Whether to work under synchronized mode for debugging. bool synchronize; @@ -301,7 +346,8 @@ typedef struct { opacity_t fade_in_step; /// How much to fade out in a single fading step. opacity_t fade_out_step; - unsigned long fade_delta; + /// Fading time delta. In milliseconds. + time_ms_t fade_delta; /// Whether to disable fading on window open/close. bool no_fading_openclose; /// Fading blacklist. A linked list of conditions. @@ -401,6 +447,17 @@ typedef struct { // === Operation related === /// Program options. options_t o; + /// Libevent event base. + struct event_base *ev_base; + /// Libevent event for X connection. + struct event *ev_x; + /// Libevent event for timeout. + struct event *ev_tmout; + /// Whether we have received an event in this cycle. + bool ev_received; + /// Whether the program is idling. I.e. no fading, no potential window + /// changes. + bool idling; /// Program start time. struct timeval time_start; /// The region needs to painted on next paint. @@ -414,11 +471,8 @@ typedef struct { Picture *alpha_picts; /// Whether all reg_ignore of windows should expire in this paint. bool reg_ignore_expire; - /// Whether the program is idling. I.e. no fading, no potential window - /// changes. - bool idling; /// Time of last fading. In milliseconds. - unsigned long fade_time; + time_ms_t fade_time; /// Head pointer of the error ignore linked list. ignore_t *ignore_head; /// Pointer to the next member of tail element of the error @@ -566,35 +620,42 @@ typedef struct { /// Structure representing a top-level window compton manages. typedef struct _win { - // Next structure in the linked list. + /// Pointer to the next structure in the linked list. struct _win *next; + /// Pointer to the next higher window to paint. + struct _win *prev_trans; - // ID of the top-level frame window. + // Core members + /// ID of the top-level frame window. Window id; - /// ID of the top-level client window of the window. - Window client_win; - /// Whether it looks like a WM window. We consider a window WM window if - /// it does not have a decedent with WM_STATE and it is not override- - /// redirected itself. - bool wmwin; - Pixmap pixmap; + /// Window attributes. XWindowAttributes a; - winmode mode; - int damaged; + /// Window painting mode. + winmode_t mode; + /// Whether the window has been damaged at least once. + bool damaged; + /// Damage of the window. Damage damage; + /// NameWindowPixmap of the window. + Pixmap pixmap; + /// Picture of the window. Picture picture; + /// Bounding shape of the window. XserverRegion border_size; + /// Region of the whole window, shadow region included. XserverRegion extents; - // Type of the window. - wintype_t window_type; - /// Whether the window is to be considered focused. - bool focused; - /// Whether the window is actually focused. - bool focused_real; - /// Leader window ID of the window. - Window leader; - /// Cached topmost window ID of the window. - Window cache_leader; + /// Window flags. Definitions above. + int_fast16_t flags; + /// Whether there's a pending ConfigureNotify happening + /// when the window is unmapped. + bool need_configure; + /// Queued ConfigureNotify when the window is unmapped. + XConfigureEvent queue_configure; + /// Region to be ignored when painting. Basically the region where + /// higher opaque windows will paint upon. Depends on window frame + /// opacity state, window geometry, window mapped/unmapped state, + /// window mode, of this and all higher windows. + XserverRegion reg_ignore; /// Whether the window has been destroyed. bool destroyed; /// Cached width/height of the window including border. @@ -606,6 +667,28 @@ typedef struct _win { /// Whether this window is to be painted. bool to_paint; + // Client window related members + /// ID of the top-level client window of the window. + Window client_win; + /// Type of the window. + wintype_t window_type; + /// Whether it looks like a WM window. We consider a window WM window if + /// it does not have a decedent with WM_STATE and it is not override- + /// redirected itself. + bool wmwin; + /// Leader window ID of the window. + Window leader; + /// Cached topmost window ID of the window. + Window cache_leader; + + // Focus-related members + /// Whether the window is to be considered focused. + bool focused; + /// Override value of window focus state. Set by D-Bus method calls. + switch_t focused_force; + /// Whether the window is actually focused. + bool focused_real; + // Blacklist related members /// Name of the window. char *name; @@ -649,8 +732,10 @@ typedef struct _win { unsigned int left_width, right_width, top_width, bottom_width; // Shadow-related members - /// Whether a window has shadow. Affected by window type. + /// Whether a window has shadow. Calculated. bool shadow; + /// Override value of window shadow state. Set by D-Bus method calls. + switch_t shadow_force; /// Opacity of the shadow. Affected by window opacity and frame opacity. double shadow_opacity; /// X offset of shadow. Affected by commandline argument. @@ -667,7 +752,7 @@ typedef struct _win { Picture shadow_alpha_pict; /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for /// none. - long attr_shadow; + long prop_shadow; // Dim-related members /// Whether the window is to be dimmed. @@ -675,24 +760,6 @@ typedef struct _win { /// Picture for dimming. Affected by user-specified inactive dim /// opacity and window opacity. Picture dim_alpha_pict; - - /// Window flags. Definitions above. - int_fast16_t flags; - - unsigned long damage_sequence; /* sequence when damage was created */ - - /// Whether there's a pending ConfigureNotify happening - /// when the window is unmapped. - bool need_configure; - /// Queued ConfigureNotify when the window is unmapped. - XConfigureEvent queue_configure; - /// Region to be ignored when painting. Basically the region where - /// higher opaque windows will paint upon. Depends on window frame - /// opacity state, window geometry, window mapped/unmapped state, - /// window mode, of this and all higher windows. - XserverRegion reg_ignore; - - struct _win *prev_trans; } win; /// Temporary structure used for communication between @@ -773,6 +840,22 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, // == Functions == +/** + * Wrapper of libevent event_new(), for compatibility with libevent-1\.x. + */ +static inline struct event * +EVENT_NEW(struct event_base *base, evutil_socket_t fd, + short what, event_callback_fn cb, void *arg) { +#ifndef CONFIG_LIBEVENT_LEGACY + return event_new(base, fd, what, cb, arg); +#else + struct event *pev = malloc(sizeof(struct event)); + if (pev) + event_set(pev, fd, what, cb, arg); + return pev; +#endif +} + // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline @@ -784,6 +867,9 @@ discard_ignore(session_t *ps, unsigned long sequence); static void set_ignore(session_t *ps, unsigned long sequence); +/** + * Ignore X errors caused by next X request. + */ static inline void set_ignore_next(session_t *ps) { set_ignore(ps, NextRequest(ps->dpy)); @@ -792,6 +878,14 @@ set_ignore_next(session_t *ps) { static int should_ignore(session_t *ps, unsigned long sequence); +/** + * Wrapper of XInternAtom() for convience. + */ +static inline Atom +get_atom(session_t *ps, char *atom_name) { + return XInternAtom(ps->dpy, atom_name, False); +} + /** * Return the painting target window. */ @@ -957,6 +1051,14 @@ array_wid_exists(const Window *arr, int count, Window wid) { return false; } +/** + * Return whether a struct timeval value is empty. + */ +static inline bool +timeval_isempty(struct timeval tv) { + return tv.tv_sec <= 0 && tv.tv_usec <= 0; +} + /* * Subtracting two struct timeval values. * @@ -1026,6 +1128,19 @@ timespec_subtract(struct timespec *result, return x->tv_sec < y->tv_sec; } +/** + * Get current time in struct timeval. + */ +static inline struct timeval __attribute__((const)) +get_time_timeval(void) { + struct timeval tv = { 0, 0 }; + + gettimeofday(&tv, NULL); + + // Return a time of all 0 if the call fails + return tv; +} + /** * Get current time in struct timespec. * @@ -1154,18 +1269,25 @@ free_win_res(session_t *ps, win *w) { /** * Get current system clock in milliseconds. - * - * The return type must be unsigned long because so many milliseconds have - * passed since the epoch. */ -static unsigned long +static inline time_ms_t get_time_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); - return (unsigned long) tv.tv_sec * 1000 - + (unsigned long) tv.tv_usec / 1000; + return tv.tv_sec % SEC_WRAP * 1000 + tv.tv_usec / 1000; +} + +/** + * Convert time from milliseconds to struct timeval. + */ +static inline struct timeval +ms_to_tv(int timeout) { + return (struct timeval) { + .tv_sec = timeout / MS_PER_SEC, + .tv_usec = timeout % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC) + }; } static int @@ -1255,33 +1377,68 @@ wid_has_prop(const session_t *ps, Window w, Atom atom) { * @return a winprop_t structure containing the attribute * and number of items. A blank one on failure. */ +// TODO: Move to compton.c static winprop_t -wid_get_prop(const session_t *ps, Window w, Atom atom, long length, - Atom rtype, int rformat) { +wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, + long length, Atom rtype, int rformat) { Atom type = None; int format = 0; unsigned long nitems = 0, after = 0; unsigned char *data = NULL; - // Use two if statements to deal with the sequence point issue. - if (Success == XGetWindowProperty(ps->dpy, w, atom, 0L, length, False, - rtype, &type, &format, &nitems, &after, &data)) { - if (type == rtype && format == rformat) { + if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length, + False, rtype, &type, &format, &nitems, &after, &data) + && nitems && (AnyPropertyType == type || type == rtype) + && (!format || format == rformat) + && (8 == format || 16 == format || 32 == format)) { return (winprop_t) { .data.p8 = data, - .nitems = nitems + .nitems = nitems, + .type = type, + .format = format, }; - } } XFree(data); return (winprop_t) { .data.p8 = NULL, - .nitems = 0 + .nitems = 0, + .type = AnyPropertyType, + .format = 0 }; } +/** + * Wrapper of wid_get_prop_adv(). + */ +static inline winprop_t +wid_get_prop(const session_t *ps, Window wid, Atom atom, long length, + Atom rtype, int rformat) { + return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat); +} + +/** + * Get the numeric property value from a win_prop_t. + */ +static inline long +winprop_get_int(winprop_t prop) { + long tgt = 0; + + if (!prop.nitems) + return 0; + + switch (prop.format) { + case 8: tgt = *(prop.data.p8); break; + case 16: tgt = *(prop.data.p16); break; + case 32: tgt = *(prop.data.p32); break; + default: assert(0); + break; + } + + return tgt; +} + /** * Free a winprop_t. * @@ -1364,7 +1521,7 @@ wid_bounding_shaped(const session_t *ps, Window wid) { */ static inline void update_reg_ignore_expire(session_t *ps, const win *w) { - if (w->to_paint && WINDOW_SOLID == w->mode) + if (w->to_paint && WMODE_SOLID == w->mode) ps->reg_ignore_expire = true; } @@ -1616,10 +1773,10 @@ static void win_update_shape(session_t *ps, win *w); static void -win_update_attr_shadow_raw(session_t *ps, win *w); +win_update_prop_shadow_raw(session_t *ps, win *w); static void -win_update_attr_shadow(session_t *ps, win *w); +win_update_prop_shadow(session_t *ps, win *w); static void determine_shadow(session_t *ps, win *w); @@ -1639,7 +1796,7 @@ win_unmark_client(session_t *ps, win *w); static void win_recheck_client(session_t *ps, win *w); -static void +static bool add_win(session_t *ps, Window id, Window prev); static void @@ -1855,7 +2012,7 @@ ev_window_name(session_t *ps, Window wid, char **name); inline static void ev_handle(session_t *ps, XEvent *ev); -static void +static bool fork_after(void); #ifdef CONFIG_LIBCONFIG @@ -1916,10 +2073,16 @@ static void update_refresh_rate(session_t *ps); static bool -sw_opti_init(session_t *ps); +swopti_init(session_t *ps); -static int -evpoll(session_t *ps, int timeout); +static void +swopti_handle_timeout(session_t *ps, struct timeval *ptv); + +static void +evcallback_x(evutil_socket_t fd, short what, void *arg); + +static void +evcallback_null(evutil_socket_t fd, short what, void *arg); static bool vsync_drm_init(session_t *ps); @@ -1955,6 +2118,9 @@ redir_start(session_t *ps); static void redir_stop(session_t *ps); +static bool +mainloop(session_t *ps); + static session_t * session_init(session_t *ps_old, int argc, char **argv); From 6e74af148e53a29734d0cfdcddc26211f5a5ff90 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 9 Jan 2013 20:25:01 +0800 Subject: [PATCH 099/178] Bug fix #77 incorrect border_width handling & #73 window data issue - (Hopefully) fix all incorrect handling of w->a.border_width in compton (#77). Thanks to baskerville for reporting. - Attempt to fix #73 by correcting a mistake that window data is fetched from the wrong function. Thanks to zakkak. - Add git commit/tag detection to Makefile for automatic versioning. - Change -lGL linking order, to fix a segmentation fault caused by something in nvidia-drivers under FreeBSD, mentioned in #74. Thanks for the report from DachiChang. - Link to -levent_core instead of -levent in Makefile. We might move to libev soon, though. - Increase SWOPTI_TOLERANCE to handle the extraordinary delay of kqueue() under FreeBSD. Thanks for DachiChang's report. - Add helper function dump_drawable() for debugging. - Replace XInternAtom() calls with get_atom(). - Remove -lrt as it's unneeded. --- compton.c | 112 +++++++++++++++++++++++++++--------------------------- compton.h | 27 ++++++++++--- 2 files changed, 79 insertions(+), 60 deletions(-) diff --git a/compton.c b/compton.c index 770712658..e1b8ade72 100644 --- a/compton.c +++ b/compton.c @@ -939,7 +939,7 @@ root_tile_f(session_t *ps) { // Get the values of background attributes for (p = 0; background_props_str[p]; p++) { winprop_t prop = wid_get_prop(ps, ps->root, - XInternAtom(ps->dpy, background_props_str[p], false), + get_atom(ps, background_props_str[p]), 1L, XA_PIXMAP, 32); if (prop.nitems) { pixmap = *prop.data.p32; @@ -1517,10 +1517,11 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } else { - int t = w->top_width; - int l = w->left_width; - int b = w->bottom_width; - int r = w->right_width; + // Painting parameters + const int t = w->a.border_width + w->top_width; + const int l = w->a.border_width + w->left_width; + const int b = w->a.border_width + w->bottom_width; + const int r = w->a.border_width + w->right_width; #define COMP_BDR(cx, cy, cwid, chei) \ XRenderComposite(ps->dpy, PictOpOver, w->picture, w->frame_alpha_pict, \ @@ -1895,13 +1896,6 @@ map_win(session_t *ps, Window id) { // Detect if the window is shaped or has rounded corners win_update_shape_raw(ps, w); - // Get window name and class if we are tracking them - if (ps->o.track_wdata) { - win_get_name(ps, w); - win_get_class(ps, w); - win_get_role(ps, w); - } - // Occasionally compton does not seem able to get a FocusIn event from // a window just mapped. I suspect it's a timing issue again when the // XSelectInput() is called too late. We have to recheck the focused @@ -2287,6 +2281,14 @@ win_mark_client(session_t *ps, win *w, Window client) { if (ps->o.track_leader) win_update_leader(ps, w); + // Get window name and class if we are tracking them + if (ps->o.track_wdata) { + win_get_name(ps, w); + win_get_class(ps, w); + win_get_role(ps, w); + } + + // Update window focus state win_update_focused(ps, w); } @@ -2586,7 +2588,8 @@ configure_win(session_t *ps, XConfigureEvent *ce) { // If window geometry did not change, don't free extents here if (w->a.x != ce->x || w->a.y != ce->y - || w->a.width != ce->width || w->a.height != ce->height) { + || w->a.width != ce->width || w->a.height != ce->height + || w->a.border_width != ce->border_width) { free_region(ps, &w->extents); free_region(ps, &w->border_size); } @@ -2594,7 +2597,8 @@ configure_win(session_t *ps, XConfigureEvent *ce) { w->a.x = ce->x; w->a.y = ce->y; - if (w->a.width != ce->width || w->a.height != ce->height) { + if (w->a.width != ce->width || w->a.height != ce->height + || w->a.border_width != ce->border_width) { free_pixmap(ps, &w->pixmap); free_picture(ps, &w->picture); } @@ -3415,8 +3419,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { else { // Destroy the root "image" if the wallpaper probably changed for (int p = 0; background_props_str[p]; p++) { - if (ev->atom == - XInternAtom(ps->dpy, background_props_str[p], false)) { + if (ev->atom == get_atom(ps, background_props_str[p])) { root_damaged(ps); break; } @@ -3911,7 +3914,7 @@ register_cm(session_t *ps, bool want_glxct) { buf = malloc(len); snprintf(buf, len, REGISTER_PROP"%d", ps->scr); - a = XInternAtom(ps->dpy, buf, false); + a = get_atom(ps, buf); free(buf); XSetSelectionOwner(ps->dpy, a, ps->reg_win, 0); @@ -4598,49 +4601,48 @@ get_cfg(session_t *ps, int argc, char *const *argv) { */ static void init_atoms(session_t *ps) { - ps->atom_opacity = XInternAtom(ps->dpy, "_NET_WM_WINDOW_OPACITY", False); - ps->atom_frame_extents = XInternAtom(ps->dpy, "_NET_FRAME_EXTENTS", False); - ps->atom_client = XInternAtom(ps->dpy, "WM_STATE", False); + ps->atom_opacity = get_atom(ps, "_NET_WM_WINDOW_OPACITY"); + ps->atom_frame_extents = get_atom(ps, "_NET_FRAME_EXTENTS"); + ps->atom_client = get_atom(ps, "WM_STATE"); ps->atom_name = XA_WM_NAME; - ps->atom_name_ewmh = XInternAtom(ps->dpy, "_NET_WM_NAME", False); + ps->atom_name_ewmh = get_atom(ps, "_NET_WM_NAME"); ps->atom_class = XA_WM_CLASS; - ps->atom_role = XInternAtom(ps->dpy, "WM_WINDOW_ROLE", False); + ps->atom_role = get_atom(ps, "WM_WINDOW_ROLE"); ps->atom_transient = XA_WM_TRANSIENT_FOR; - ps->atom_client_leader = XInternAtom(ps->dpy, "WM_CLIENT_LEADER", False); - ps->atom_ewmh_active_win = XInternAtom(ps->dpy, "_NET_ACTIVE_WINDOW", False); - ps->atom_compton_shadow = XInternAtom(ps->dpy, "_COMPTON_SHADOW", False); + ps->atom_client_leader = get_atom(ps, "WM_CLIENT_LEADER"); + ps->atom_ewmh_active_win = get_atom(ps, "_NET_ACTIVE_WINDOW"); + ps->atom_compton_shadow = get_atom(ps, "_COMPTON_SHADOW"); - ps->atom_win_type = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE", False); + ps->atom_win_type = get_atom(ps, "_NET_WM_WINDOW_TYPE"); ps->atoms_wintypes[WINTYPE_UNKNOWN] = 0; - ps->atoms_wintypes[WINTYPE_DESKTOP] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_DESKTOP", False); - ps->atoms_wintypes[WINTYPE_DOCK] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_DOCK", False); - ps->atoms_wintypes[WINTYPE_TOOLBAR] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_TOOLBAR", False); - ps->atoms_wintypes[WINTYPE_MENU] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_MENU", False); - ps->atoms_wintypes[WINTYPE_UTILITY] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_UTILITY", False); - ps->atoms_wintypes[WINTYPE_SPLASH] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_SPLASH", False); - ps->atoms_wintypes[WINTYPE_DIALOG] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_DIALOG", False); - ps->atoms_wintypes[WINTYPE_NORMAL] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_NORMAL", False); - ps->atoms_wintypes[WINTYPE_DROPDOWN_MENU] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False); - ps->atoms_wintypes[WINTYPE_POPUP_MENU] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_POPUP_MENU", False); - ps->atoms_wintypes[WINTYPE_TOOLTIP] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_TOOLTIP", False); - ps->atoms_wintypes[WINTYPE_NOTIFY] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); - ps->atoms_wintypes[WINTYPE_COMBO] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_COMBO", False); - ps->atoms_wintypes[WINTYPE_DND] = XInternAtom(ps->dpy, - "_NET_WM_WINDOW_TYPE_DND", False); + ps->atoms_wintypes[WINTYPE_DESKTOP] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_DESKTOP"); + ps->atoms_wintypes[WINTYPE_DOCK] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_DOCK"); + ps->atoms_wintypes[WINTYPE_TOOLBAR] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_TOOLBAR"); + ps->atoms_wintypes[WINTYPE_MENU] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_MENU"); + ps->atoms_wintypes[WINTYPE_UTILITY] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_UTILITY"); + ps->atoms_wintypes[WINTYPE_SPLASH] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_SPLASH"); + ps->atoms_wintypes[WINTYPE_DIALOG] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_DIALOG"); + ps->atoms_wintypes[WINTYPE_NORMAL] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_NORMAL"); + ps->atoms_wintypes[WINTYPE_DROPDOWN_MENU] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"); + ps->atoms_wintypes[WINTYPE_POPUP_MENU] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_POPUP_MENU"); + ps->atoms_wintypes[WINTYPE_TOOLTIP] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_TOOLTIP"); + ps->atoms_wintypes[WINTYPE_NOTIFY] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_NOTIFICATION"); + ps->atoms_wintypes[WINTYPE_COMBO] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_COMBO"); + ps->atoms_wintypes[WINTYPE_DND] = get_atom(ps, + "_NET_WM_WINDOW_TYPE_DND"); } /** diff --git a/compton.h b/compton.h index c7d65d30c..9bf25e240 100644 --- a/compton.h +++ b/compton.h @@ -128,7 +128,7 @@ typedef void(* event_callback_fn)(evutil_socket_t, short, void *); #define REGISTER_PROP "_NET_WM_CM_S" #define FADE_DELTA_TOLERANCE 0.2 -#define SWOPTI_TOLERANCE 1000 +#define SWOPTI_TOLERANCE 3000 #define WIN_GET_LEADER_MAX_RECURSION 20 #define SEC_WRAP (15L * 24L * 60L * 60L) @@ -879,10 +879,10 @@ static int should_ignore(session_t *ps, unsigned long sequence); /** - * Wrapper of XInternAtom() for convience. + * Wrapper of XInternAtom() for convenience. */ static inline Atom -get_atom(session_t *ps, char *atom_name) { +get_atom(session_t *ps, const char *atom_name) { return XInternAtom(ps->dpy, atom_name, False); } @@ -1530,8 +1530,25 @@ update_reg_ignore_expire(session_t *ps, const win *w) { */ static inline bool __attribute__((const)) win_has_frame(const win *w) { - return w->top_width || w->left_width || w->right_width - || w->bottom_width; + return w->a.border_width + || w->top_width || w->left_width || w->right_width || w->bottom_width; +} + +/** + * Dump an drawable's info. + */ +static inline void +dump_drawable(session_t *ps, Drawable drawable) { + Window rroot = None; + int x = 0, y = 0; + unsigned width = 0, height = 0, border = 0, depth = 0; + if (XGetGeometry(ps->dpy, drawable, &rroot, &x, &y, &width, &height, + &border, &depth)) { + printf_dbgf("(%#010lx): x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u\n", drawable, x, y, width, height, border, depth); + } + else { + printf_dbgf("(%#010lx): Failed\n", drawable); + } } /** From 5871e8f4029d3a0dd532e2b4cc0f7733b8f28fac Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 11 Jan 2013 21:31:02 +0800 Subject: [PATCH 100/178] Improvement: Use select() for main loop - Back to using select() for main loop. Thus we are not longer relying on libevent. - Add generic timeout system (untested) to prepare for D-Bus support. - Drop troff man pages. Revise Makefile to improve documentation building, fix double LDFLAGS inclusion, and re-add -lrt. This turns asciidoc into a build time dependency. - Change fading time calculation. - Add --logpath and ostream_reopen() for debugging with -b. - Drop unused lceil_ntimes() and other helper functions. - Only very limited tests are done. Bugs to be expected. --- compton.c | 361 +++++++++++++++++++++++++++++++++++++----------------- compton.h | 225 +++++++++++++++++++++++++++------- 2 files changed, 429 insertions(+), 157 deletions(-) diff --git a/compton.c b/compton.c index e1b8ade72..941134a10 100644 --- a/compton.c +++ b/compton.c @@ -57,8 +57,7 @@ static int fade_timeout(session_t *ps) { int diff = ps->o.fade_delta - get_time_ms() + ps->fade_time; - if (diff < 0) - diff = 0; + diff = normalize_i_range(diff, 0, ps->o.fade_delta * 2); return diff; } @@ -1196,11 +1195,14 @@ paint_preprocess(session_t *ps, win *list) { bool is_highest = true; // Fading step calculation - time_ms_t steps = ((get_time_ms() - ps->fade_time) + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta; - if (steps < 0L) { - // Time disorder + time_ms_t steps = 0L; + if (ps->fade_time) { + steps = ((get_time_ms() - ps->fade_time) + FADE_DELTA_TOLERANCE * ps->o.fade_delta) / ps->o.fade_delta; + } + // Reset fade_time if unset, or there appears to be a time disorder + if (!ps->fade_time || steps < 0L) { ps->fade_time = get_time_ms(); - steps = 0; + steps = 0L; } ps->fade_time += steps * ps->o.fade_delta; @@ -3920,11 +3922,29 @@ register_cm(session_t *ps, bool want_glxct) { XSetSelectionOwner(ps->dpy, a, ps->reg_win, 0); } +/** + * Reopen streams for logging. + */ +static bool +ostream_reopen(session_t *ps, const char *path) { + if (!path) + path = ps->o.logpath; + if (!path) + path = "/dev/null"; + + bool success = freopen(path, "a", stdout); + success = freopen(path, "a", stderr) && success; + if (!success) + printf_errfq(1, "(%s): freopen() failed.", path); + + return success; +} + /** * Fork program to background and disable all I/O streams. */ static bool -fork_after(void) { +fork_after(session_t *ps) { if (getppid() == 1) return true; @@ -3941,18 +3961,13 @@ fork_after(void) { // Mainly to suppress the _FORTIFY_SOURCE warning bool success = freopen("/dev/null", "r", stdin); - success = freopen("/dev/null", "w", stdout) && success; - if (!success) { - printf_errf("(): freopen() failed."); - return false; - } - success = freopen("/dev/null", "w", stderr); if (!success) { printf_errf("(): freopen() failed."); return false; } + success = ostream_reopen(ps, NULL); - return true; + return success; } #ifdef CONFIG_LIBCONFIG @@ -4299,6 +4314,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "blur-background-frame", no_argument, NULL, 284 }, { "blur-background-fixed", no_argument, NULL, 285 }, { "dbus", no_argument, NULL, 286 }, + { "logpath", required_argument, NULL, 287 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4405,8 +4421,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { case 'n': case 'a': case 's': - fprintf(stderr, "Warning: " - "-n, -a, and -s have been removed.\n"); + printf_errfq(1, "(): -n, -a, and -s have been removed."); break; case 'b': ps->o.fork_after_register = true; @@ -4535,8 +4550,13 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --dbus ps->o.dbus = true; break; + case 287: + // --logpath + ps->o.logpath = mstrcpy(optarg); + break; default: usage(); + break; } } @@ -4694,22 +4714,6 @@ swopti_init(session_t *ps) { return true; } -/** - * Get the smaller number that is bigger than dividend and is - * N times of divisor. - */ -static inline long -lceil_ntimes(long dividend, long divisor) { - // It's possible to use the more beautiful expression here: - // ret = ((dividend - 1) / divisor + 1) * divisor; - // But it does not work well for negative values. - long ret = dividend / divisor * divisor; - if (ret < dividend) - ret += divisor; - - return ret; -} - /** * Modify a struct timeval timeout value to render at a fixed pace. * @@ -4743,32 +4747,6 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv) { } } -/** - * Libevent callback function to handle X events. - */ -static void -evcallback_x(evutil_socket_t fd, short what, void *arg) { - session_t *ps = ps_g; - - // Sometimes poll() returns 1 but no events are actually read, - // causing XNextEvent() to block, I have no idea what's wrong, so we - // check for the number of events here - if (XEventsQueued(ps->dpy, QueuedAfterReading)) { - XEvent ev = { }; - - XNextEvent(ps->dpy, &ev); - ev_handle(ps, &ev); - ps->ev_received = true; - } -} - -/** - * NULL libevent callback function. - */ -static void -evcallback_null(evutil_socket_t fd, short what, void *arg) { -} - /** * Initialize DRM VSync. * @@ -5003,6 +4981,141 @@ redir_start(session_t *ps) { } } +/** + * Get the poll time. + */ +static time_ms_t +timeout_get_poll_time(session_t *ps) { + const time_ms_t now = get_time_ms(); + time_ms_t wait = TIME_MS_MAX; + + // Traverse throught the timeout linked list + for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) { + if (ptmout->enabled) { + // Truncate the last run time to the closest interval + time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval; + if (newrun <= now) { + wait = 0; + break; + } + else { + time_ms_t newwait = newrun - now; + if (newwait < wait) + wait = newwait; + } + } + } + + return wait; +} + +/** + * Insert a new timeout. + */ +static timeout_t * +timeout_insert(session_t *ps, time_ms_t interval, + bool (*callback)(session_t *ps, timeout_t *ptmout), void *data) { + const static timeout_t tmout_def = { + .enabled = true, + .data = NULL, + .callback = NULL, + .firstrun = 0L, + .lastrun = 0L, + .interval = 0L, + }; + + const time_ms_t now = get_time_ms(); + timeout_t *ptmout = malloc(sizeof(timeout_t)); + if (!ptmout) + printf_errfq(1, "(): Failed to allocate memory for timeout."); + memcpy(ptmout, &tmout_def, sizeof(timeout_t)); + + ptmout->interval = interval; + ptmout->firstrun = now; + ptmout->lastrun = now; + ptmout->data = data; + ptmout->callback = callback; + ptmout->next = ps->tmout_lst; + ps->tmout_lst = ptmout; + + return ptmout; +} + +/** + * Drop a timeout. + * + * @return true if we have found the timeout and removed it, false + * otherwise + */ +static bool +timeout_drop(session_t *ps, timeout_t *prm) { + timeout_t **pplast = &ps->tmout_lst; + + for (timeout_t *ptmout = ps->tmout_lst; ptmout; + pplast = &ptmout->next, ptmout = ptmout->next) { + if (prm == ptmout) { + *pplast = ptmout->next; + free(ptmout); + + return true; + } + } + + return false; +} + +/** + * Clear all timeouts. + */ +static void +timeout_clear(session_t *ps) { + timeout_t *ptmout = ps->tmout_lst; + timeout_t *next = NULL; + while (ptmout) { + next = ptmout->next; + free(ptmout); + ptmout = next; + } +} + +/** + * Run timeouts. + * + * @return true if we have ran a timeout, false otherwise + */ +static bool +timeout_run(session_t *ps) { + const time_ms_t now = get_time_ms(); + bool ret = false; + + for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) { + if (ptmout->enabled) { + const time_ms_t max = now + + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE); + time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval; + if (newrun <= max) { + ret = true; + timeout_invoke(ps, ptmout); + } + } + } + + return ret; +} + +/** + * Invoke a timeout. + */ +static void +timeout_invoke(session_t *ps, timeout_t *ptmout) { + const time_ms_t now = get_time_ms(); + ptmout->lastrun = now; + // Avoid modifying the timeout structure after running timeout, to + // make it possible to remove timeout in callback + if (ptmout->callback) + ptmout->callback(ps, ptmout); +} + /** * Unredirect all windows. */ @@ -5037,36 +5150,74 @@ redir_stop(session_t *ps) { */ static bool mainloop(session_t *ps) { - bool infinite_wait = false; - // Process existing events + // Sometimes poll() returns 1 but no events are actually read, + // causing XNextEvent() to block, I have no idea what's wrong, so we + // check for the number of events here. if (XEventsQueued(ps->dpy, QueuedAfterReading)) { - evcallback_x(ConnectionNumber(ps->dpy), 0, NULL); + XEvent ev = { }; + + XNextEvent(ps->dpy, &ev); + ev_handle(ps, &ev); + ps->ev_received = true; + return true; } - // Add timeout - if (ps->ev_received || !ps->idling) { - struct timeval tv = ms_to_tv(ps->ev_received ? 0: fade_timeout(ps)); - if (ps->o.sw_opti) - swopti_handle_timeout(ps, &tv); - assert(tv.tv_sec >= 0 && tv.tv_usec >= 0); - if (timeval_isempty(tv)) + if (ps->reset) + return false; + + // Calculate timeout + struct timeval *ptv = NULL; + { + // Consider ev_received firstly + if (ps->ev_received) { + ptv = malloc(sizeof(struct timeval)); + ptv->tv_sec = 0L; + ptv->tv_usec = 0L; + } + // Then consider fading timeout + else if (!ps->idling) { + ptv = malloc(sizeof(struct timeval)); + *ptv = ms_to_tv(fade_timeout(ps)); + } + + // Software optimization is to be applied on timeouts that require + // immediate painting only + if (ptv && ps->o.sw_opti) + swopti_handle_timeout(ps, ptv); + + // Don't continue looping for 0 timeout + if (ptv && timeval_isempty(ptv)) { + free(ptv); return false; - evtimer_add(ps->ev_tmout, &tv); - } - else { - infinite_wait = true; - } + } - // Run libevent main loop - if (event_base_loop(ps->ev_base, EVLOOP_ONCE)) - printf_errfq(1, "(): Unexpected error when running event loop."); + // Now consider the waiting time of other timeouts + time_ms_t tmout_ms = timeout_get_poll_time(ps); + if (tmout_ms < TIME_MS_MAX) { + if (!ptv) { + ptv = malloc(sizeof(struct timeval)); + *ptv = ms_to_tv(tmout_ms); + } + else if (timeval_ms_cmp(ptv, tmout_ms) > 0) { + *ptv = ms_to_tv(tmout_ms); + } + } + + // Don't continue looping for 0 timeout + if (ptv && timeval_isempty(ptv)) { + free(ptv); + return false; + } + } - evtimer_del(ps->ev_tmout); + // Polling + fds_poll(ps, ptv); + free(ptv); + ptv = NULL; - if (infinite_wait) - ps->fade_time = get_time_ms(); + timeout_run(ps); return true; } @@ -5106,6 +5257,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .detect_rounded_corners = false, .paint_on_overlay = false, .unredir_if_possible = false, + .dbus = false, + .logpath = NULL, .refresh_rate = 0, .sw_opti = false, @@ -5156,6 +5309,12 @@ session_init(session_t *ps_old, int argc, char **argv) { .track_leader = false, }, + .pfds_read = NULL, + .pfds_write = NULL, + .pfds_except = NULL, + .nfds_max = 0, + .tmout_lst = NULL, + .all_damage = None, .time_start = { 0, 0 }, .redirected = false, @@ -5163,7 +5322,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .alpha_picts = NULL, .reg_ignore_expire = false, .idling = false, - .fade_time = 0, + .fade_time = 0L, .ignore_head = NULL, .ignore_tail = NULL, .reset = false, @@ -5406,24 +5565,7 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue); } - ps->all_damage = None; - - // Build event base - if (!(ps->ev_base = -#ifndef CONFIG_LIBEVENT_LEGACY - event_base_new() -#else - event_init() -#endif - )) - printf_errfq(1, "(): Failed to build event base."); - if (!(ps->ev_x = EVENT_NEW(ps->ev_base, ConnectionNumber(ps->dpy), - EV_READ | EV_PERSIST, evcallback_x, NULL))) - printf_errfq(1, "(): Failed to build event."); - if (event_add(ps->ev_x, NULL)) - printf_errfq(1, "(): Failed to add event."); - if (!(ps->ev_tmout = evtimer_new(ps->ev_base, evcallback_null, NULL))) - printf_errfq(1, "(): Failed to build event."); + fds_insert(ps, ConnectionNumber(ps->dpy), POLLIN); XGrabServer(ps->dpy); @@ -5459,14 +5601,10 @@ session_init(session_t *ps_old, int argc, char **argv) { // Fork to background, if asked if (ps->o.fork_after_register) { - if (!fork_after()) { + if (!fork_after(ps)) { session_destroy(ps); return NULL; } - - // Reinitialize event base - if (event_reinit(ps->ev_base) < 0) - printf_errfq(1, "Failed to reinitialize event base."); } // Free the old session @@ -5565,6 +5703,10 @@ session_destroy(session_t *ps) { free(ps->shadow_top); free(ps->gaussian_map); free(ps->o.display); + free(ps->o.logpath); + free(ps->pfds_read); + free(ps->pfds_write); + free(ps->pfds_except); // Free reg_win and glx_context if (ps->reg_win) { @@ -5598,14 +5740,12 @@ session_destroy(session_t *ps) { ps->overlay = None; } - // Free libevent things - event_free(ps->ev_x); - event_free(ps->ev_tmout); - event_base_free(ps->ev_base); - // Flush all events XSync(ps->dpy, True); + // Free timeouts + timeout_clear(ps); + if (ps == ps_g) ps_g = NULL; } @@ -5624,8 +5764,6 @@ session_run(session_t *ps) { ps->reg_ignore_expire = true; - ps->fade_time = get_time_ms(); - t = paint_preprocess(ps, ps->list); if (ps->redirected) @@ -5658,6 +5796,9 @@ session_run(session_t *ps) { XSync(ps->dpy, False); ps->all_damage = None; } + + if (ps->idling) + ps->fade_time = 0L; } } diff --git a/compton.h b/compton.h index 9bf25e240..cb4f623f9 100644 --- a/compton.h +++ b/compton.h @@ -43,9 +43,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -54,20 +56,6 @@ #include #include -// libevent -#ifndef CONFIG_LIBEVENT_LEGACY -#include -#else -#include -typedef int evutil_socket_t; -typedef void(* event_callback_fn)(evutil_socket_t, short, void *); -#define event_free(ev) (event_del((ev)), free((ev))) -#endif - -#ifndef evtimer_new -#define evtimer_new(b, cb, arg) EVENT_NEW((b), -1, 0, (cb), (arg)) -#endif - // libpcre #ifdef CONFIG_REGEX_PCRE #include @@ -127,8 +115,10 @@ typedef void(* event_callback_fn)(evutil_socket_t, short, void *); #define OPAQUE 0xffffffff #define REGISTER_PROP "_NET_WM_CM_S" +#define TIME_MS_MAX LONG_MAX #define FADE_DELTA_TOLERANCE 0.2 #define SWOPTI_TOLERANCE 3000 +#define TIMEOUT_RUN_TOLERANCE 0.2 #define WIN_GET_LEADER_MAX_RECURSION 20 #define SEC_WRAP (15L * 24L * 60L * 60L) @@ -286,6 +276,8 @@ typedef struct { double *data; } conv; +struct _timeout_t; + struct _win; /// Structure representing all options. @@ -308,6 +300,8 @@ typedef struct { bool unredir_if_possible; /// Whether to enable D-Bus support. bool dbus; + /// Path to log file. + char *logpath; /// Whether to work under synchronized mode for debugging. bool synchronize; @@ -447,12 +441,16 @@ typedef struct { // === Operation related === /// Program options. options_t o; - /// Libevent event base. - struct event_base *ev_base; - /// Libevent event for X connection. - struct event *ev_x; - /// Libevent event for timeout. - struct event *ev_tmout; + /// File descriptors to check for reading. + fd_set *pfds_read; + /// File descriptors to check for writing. + fd_set *pfds_write; + /// File descriptors to check for exceptions. + fd_set *pfds_except; + /// Largest file descriptor in fd_set-s above. + int nfds_max; + /// Linked list of all timeouts. + struct _timeout_t *tmout_lst; /// Whether we have received an event in this cycle. bool ev_received; /// Whether the program is idling. I.e. no fading, no potential window @@ -770,6 +768,18 @@ struct options_tmp { double menu_opacity; }; +/// Structure for a recorded timeout. +typedef struct _timeout_t { + bool enabled; + void *data; + bool (*callback)(session_t *ps, struct _timeout_t *ptmout); + time_ms_t interval; + time_ms_t firstrun; + time_ms_t lastrun; + struct _timeout_t *next; +} timeout_t; + +/// Enumeration for window event hints. typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, @@ -840,22 +850,6 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, // == Functions == -/** - * Wrapper of libevent event_new(), for compatibility with libevent-1\.x. - */ -static inline struct event * -EVENT_NEW(struct event_base *base, evutil_socket_t fd, - short what, event_callback_fn cb, void *arg) { -#ifndef CONFIG_LIBEVENT_LEGACY - return event_new(base, fd, what, cb, arg); -#else - struct event *pev = malloc(sizeof(struct event)); - if (pev) - event_set(pev, fd, what, cb, arg); - return pev; -#endif -} - // inline functions must be made static to compile correctly under clang: // http://clang.llvm.org/compatibility.html#inline @@ -1007,6 +1001,14 @@ min_i(int a, int b) { return (a > b ? b : a); } +/** + * Select the smaller long integer of two. + */ +static inline long __attribute__((const)) +min_l(long a, long b) { + return (a > b ? b : a); +} + /** * Normalize a double value to a specific range. * @@ -1055,8 +1057,41 @@ array_wid_exists(const Window *arr, int count, Window wid) { * Return whether a struct timeval value is empty. */ static inline bool -timeval_isempty(struct timeval tv) { - return tv.tv_sec <= 0 && tv.tv_usec <= 0; +timeval_isempty(struct timeval *ptv) { + if (!ptv) + return false; + + return ptv->tv_sec <= 0 && ptv->tv_usec <= 0; +} + +/** + * Compare a struct timeval with a time in milliseconds. + * + * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms + */ +static inline int +timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) { + assert(ptv); + + // We use those if statement instead of a - expression because of possible + // truncation problem from long to int. + { + long sec = ms / MS_PER_SEC; + if (ptv->tv_sec > sec) + return 1; + if (ptv->tv_sec < sec) + return -1; + } + + { + long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC); + if (ptv->tv_usec > usec) + return 1; + if (ptv->tv_usec < usec) + return -1; + } + + return 0; } /* @@ -1290,8 +1325,94 @@ ms_to_tv(int timeout) { }; } -static int -fade_timeout(session_t *ps); +/** + * Add a file descriptor to a select() fd_set. + */ +static inline bool +fds_insert_select(fd_set **ppfds, int fd) { + assert(fd <= FD_SETSIZE); + + if (!*ppfds) { + if ((*ppfds = malloc(sizeof(fd_set)))) { + FD_ZERO(*ppfds); + } + else { + fprintf(stderr, "Failed to allocate memory for select() fdset.\n"); + exit(1); + } + } + + FD_SET(fd, *ppfds); + + return true; +} + +/** + * Add a new file descriptor to wait for. + */ +static inline bool +fds_insert(session_t *ps, int fd, short events) { + bool result = true; + + ps->nfds_max = max_i(fd + 1, ps->nfds_max); + + if (POLLIN & events) + result = fds_insert_select(&ps->pfds_read, fd) && result; + if (POLLOUT & events) + result = fds_insert_select(&ps->pfds_write, fd) && result; + if (POLLPRI & events) + result = fds_insert_select(&ps->pfds_except, fd) && result; + + return result; +} + +/** + * Delete a file descriptor to wait for. + */ +static inline void +fds_drop(session_t *ps, int fd, short events) { + // Drop fd from respective fd_set-s + if (POLLIN & events) + FD_CLR(fd, ps->pfds_read); + if (POLLOUT & events) + FD_CLR(fd, ps->pfds_write); + if (POLLPRI & events) + FD_CLR(fd, ps->pfds_except); +} + +#define CPY_FDS(key) \ + fd_set * key = NULL; \ + if (ps->key) { \ + key = malloc(sizeof(fd_set)); \ + memcpy(key, ps->key, sizeof(fd_set)); \ + if (!key) { \ + fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \ + exit(1); \ + } \ + } \ + +/** + * Poll for changes. + * + * poll() is much better than select(), but ppoll() does not exist on + * *BSD. + */ +static inline int +fds_poll(session_t *ps, struct timeval *ptv) { + // Copy fds + CPY_FDS(pfds_read); + CPY_FDS(pfds_write); + CPY_FDS(pfds_except); + + int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv); + + free(pfds_read); + free(pfds_write); + free(pfds_except); + + return ret; +} +#undef CPY_FDS static void run_fade(session_t *ps, win *w, unsigned steps); @@ -2030,7 +2151,7 @@ inline static void ev_handle(session_t *ps, XEvent *ev); static bool -fork_after(void); +fork_after(session_t *ps); #ifdef CONFIG_LIBCONFIG /** @@ -2095,12 +2216,6 @@ swopti_init(session_t *ps); static void swopti_handle_timeout(session_t *ps, struct timeval *ptv); -static void -evcallback_x(evutil_socket_t fd, short what, void *arg); - -static void -evcallback_null(evutil_socket_t fd, short what, void *arg); - static bool vsync_drm_init(session_t *ps); @@ -2135,6 +2250,22 @@ redir_start(session_t *ps); static void redir_stop(session_t *ps); +static time_ms_t +timeout_get_poll_time(session_t *ps); + +static timeout_t * +timeout_insert(session_t *ps, time_ms_t interval, + bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); + +static void +timeout_invoke(session_t *ps, timeout_t *ptmout); + +static bool +timeout_drop(session_t *ps, timeout_t *prm); + +static void +timeout_clear(session_t *ps); + static bool mainloop(session_t *ps); From f0521b2d41e3c3439e733e288c8d8e8a92b3884b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 12 Jan 2013 22:21:35 +0800 Subject: [PATCH 101/178] Feature #75: Invert window color - Add window color inversion (--invert-color-include). It means 2-3 times extra painting, so performance issues are likely to appear. I hope I could find a faster way to invert colors. - Rename some functions. - Fix update of some window properties after window type/data change. Hopefully this will be helpful for #73. - Split win_build_picture() from win_blur_background(). - Correct memory leak of ps->focus_blacklist during reinitialization. - Add win_upd_t and win_upd_run(), to reduce duplicate window property updates. But is unused right now. - Limited tests are done overall. Bugs to be expected. --- compton.c | 232 ++++++++++++++++++++++++++++++++++++++++++------------ compton.h | 47 ++++++++--- 2 files changed, 221 insertions(+), 58 deletions(-) diff --git a/compton.c b/compton.c index 941134a10..9155a84e0 100644 --- a/compton.c +++ b/compton.c @@ -1242,7 +1242,7 @@ paint_preprocess(session_t *ps, win *list) { if (to_paint) { // If opacity changes if (w->opacity != opacity_old) { - determine_mode(ps, w); + win_determine_mode(ps, w); add_damage_win(ps, w); } @@ -1430,6 +1430,38 @@ win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) { w->shadow_width, w->shadow_height); } +/** + * Create an alternative picture for a window. + */ +static inline Picture +win_build_picture(session_t *ps, win *w, Visual *visual) { + const int wid = w->widthb; + const int hei = w->heightb; + int depth = 0; + XRenderPictFormat *pictformat = NULL; + + if (visual && ps->vis != visual) { + pictformat = XRenderFindVisualFormat(ps->dpy, visual); + depth = pictformat->depth; + } + else { + pictformat = XRenderFindVisualFormat(ps->dpy, ps->vis); + } + + if (!depth) + depth = ps->depth; + + Pixmap tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wid, hei, depth); + if (!tmp_pixmap) + return None; + + Picture tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap, + pictformat, 0, 0); + free_pixmap(ps, &tmp_pixmap); + + return tmp_picture; +} + /** * Blur the background of a window. */ @@ -1451,24 +1483,17 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), }; - Pixmap tmp_pixmap = None; - Picture tmp_picture = None; - - int x = w->a.x; - int y = w->a.y; - int wid = w->widthb; - int hei = w->heightb; + const int x = w->a.x; + const int y = w->a.y; + const int wid = w->widthb; + const int hei = w->heightb; // Directly copying from tgt_buffer does not work, so we create a // Picture in the middle. - tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wid, hei, ps->depth); - if (!tmp_pixmap) - goto win_blur_background_err; + Picture tmp_picture = win_build_picture(ps, w, NULL); - tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap, - XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0); if (!tmp_picture) - goto win_blur_background_err; + return; // Adjust blur strength according to window opacity, to make it appear // better during fading @@ -1496,8 +1521,6 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, xrfilter_reset(ps, tgt_buffer); XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); -win_blur_background_err: - free_pixmap(ps, &tmp_pixmap); free_picture(ps, &tmp_picture); } @@ -1513,9 +1536,27 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); int op = (w->mode == WMODE_SOLID ? PictOpSrc: PictOpOver); + Picture pict = w->picture; + + // Invert window color, if required + if (w->invert_color) { + Picture newpict = win_build_picture(ps, w, w->a.visual); + if (newpict) { + XRenderComposite(ps->dpy, PictOpSrc, pict, None, + newpict, 0, 0, 0, 0, 0, 0, wid, hei); + XRenderComposite(ps->dpy, PictOpDifference, ps->white_picture, None, + newpict, 0, 0, 0, 0, 0, 0, wid, hei); + // We use an extra PictOpInReverse operation to get correct pixel + // alpha. There could be a better solution. + if (WMODE_ARGB == w->mode) + XRenderComposite(ps->dpy, PictOpInReverse, pict, None, + newpict, 0, 0, 0, 0, 0, 0, wid, hei); + pict = newpict; + } + } if (!w->frame_opacity) { - XRenderComposite(ps->dpy, op, w->picture, alpha_mask, + XRenderComposite(ps->dpy, op, pict, alpha_mask, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } else { @@ -1526,7 +1567,7 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { const int r = w->a.border_width + w->right_width; #define COMP_BDR(cx, cy, cwid, chei) \ - XRenderComposite(ps->dpy, PictOpOver, w->picture, w->frame_alpha_pict, \ + XRenderComposite(ps->dpy, PictOpOver, pict, w->frame_alpha_pict, \ tgt_buffer, (cx), (cy), 0, 0, x + (cx), y + (cy), (cwid), (chei)) // The following complicated logic is required because some broken @@ -1562,7 +1603,7 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { pwid = wid - l - pwid; if (pwid > 0) { // body - XRenderComposite(ps->dpy, op, w->picture, alpha_mask, + XRenderComposite(ps->dpy, op, pict, alpha_mask, tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei); } } @@ -1572,6 +1613,9 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { #undef COMP_BDR + if (pict != w->picture) + free_picture(ps, &pict); + // Dimming the window if needed if (w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) { XRenderComposite(ps->dpy, PictOpOver, ps->black_picture, @@ -1919,7 +1963,7 @@ map_win(session_t *ps, Window id) { win_update_prop_shadow_raw(ps, w); // Many things above could affect shadow - determine_shadow(ps, w); + win_determine_shadow(ps, w); // Set fading state if (ps->o.no_fading_openclose) { @@ -1930,7 +1974,7 @@ map_win(session_t *ps, Window id) { } else { set_fade_callback(ps, w, NULL, true); - determine_fade(ps, w); + win_determine_fade(ps, w); } w->damaged = true; @@ -1947,7 +1991,7 @@ map_win(session_t *ps, Window id) { static void finish_map_win(session_t *ps, win *w) { if (ps->o.no_fading_openclose) - determine_fade(ps, w); + win_determine_fade(ps, w); } static void @@ -2015,7 +2059,7 @@ get_opacity_percent(win *w) { } static void -determine_mode(session_t *ps, win *w) { +win_determine_mode(session_t *ps, win *w) { winmode_t mode = WMODE_SOLID; XRenderPictFormat *format; @@ -2109,7 +2153,7 @@ calc_dim(session_t *ps, win *w) { * Determine if a window should fade on opacity change. */ static void -determine_fade(session_t *ps, win *w) { +win_determine_fade(session_t *ps, win *w) { w->fade = ps->o.wintype_fade[w->window_type]; } @@ -2136,7 +2180,7 @@ win_update_shape(session_t *ps, win *w) { win_update_shape_raw(ps, w); // Shadow state could be changed - determine_shadow(ps, w); + win_determine_shadow(ps, w); /* // If clear_shadow state on the window possibly changed, destroy the old @@ -2178,7 +2222,7 @@ win_update_prop_shadow(session_t *ps, win *w) { win_update_prop_shadow_raw(ps, w); if (w->prop_shadow != attr_shadow_old) - determine_shadow(ps, w); + win_determine_shadow(ps, w); } /** @@ -2186,7 +2230,7 @@ win_update_prop_shadow(session_t *ps, win *w) { * on shadow state. */ static void -determine_shadow(session_t *ps, win *w) { +win_determine_shadow(session_t *ps, win *w) { bool shadow_old = w->shadow; w->shadow = (UNSET == w->shadow_force ? @@ -2216,9 +2260,72 @@ determine_shadow(session_t *ps, win *w) { } /** - * Update cache data in struct _win that depends on window size. + * Determine if a window should have color inverted. */ +static void +win_determine_invert_color(session_t *ps, win *w) { + bool invert_color_old = w->invert_color; + + if (UNSET != w->invert_color_force) + w->invert_color = w->invert_color_force; + else + w->invert_color = win_match(w, ps->o.invert_color_list, &w->cache_ivclst); + if (w->invert_color != invert_color_old) + add_damage_win(ps, w); +} + +/** + * Function to be called on window type changes. + */ +static void +win_on_wtype_change(session_t *ps, win *w) { + win_determine_shadow(ps, w); + win_determine_fade(ps, w); + win_update_focused(ps, w); +} + +/** + * Function to be called on window data changes. + */ +static void +win_on_wdata_change(session_t *ps, win *w) { + if (ps->o.shadow_blacklist) + win_determine_shadow(ps, w); + if (ps->o.fade_blacklist) + win_determine_fade(ps, w); + if (ps->o.invert_color_list) + win_determine_invert_color(ps, w); + if (ps->o.focus_blacklist) + win_update_focused(ps, w); +} + +/** + * Process needed window updates. + */ +static void +win_upd_run(session_t *ps, win *w, win_upd_t *pupd) { + if (pupd->shadow) { + win_determine_shadow(ps, w); + pupd->shadow = false; + } + if (pupd->fade) { + win_determine_fade(ps, w); + pupd->fade = false; + } + if (pupd->invert_color) { + win_determine_invert_color(ps, w); + pupd->invert_color = false; + } + if (pupd->focus) { + win_update_focused(ps, w); + pupd->focus = false; + } +} + +/** + * Update cache data in struct _win that depends on window size. + */ static void calc_win_size(session_t *ps, win *w) { w->widthb = w->a.width + w->a.border_width * 2; @@ -2254,7 +2361,8 @@ win_mark_client(session_t *ps, win *w, Window client) { if (IsViewable != w->a.map_state) return; - XSelectInput(ps->dpy, client, determine_evmask(ps, client, WIN_EVMODE_CLIENT)); + XSelectInput(ps->dpy, client, + determine_evmask(ps, client, WIN_EVMODE_CLIENT)); // Make sure the XSelectInput() requests are sent XSync(ps->dpy, False); @@ -2264,19 +2372,26 @@ win_mark_client(session_t *ps, win *w, Window client) { get_frame_extents(ps, w, client); } - // Detect window type here - if (WINTYPE_UNKNOWN == w->window_type) - w->window_type = wid_get_prop_wintype(ps, w->client_win); + { + wintype_t wtype_old = w->window_type; + + // Detect window type here + if (WINTYPE_UNKNOWN == w->window_type) + w->window_type = wid_get_prop_wintype(ps, w->client_win); + + // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take + // override-redirect windows or windows without WM_TRANSIENT_FOR as + // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. + if (WINTYPE_UNKNOWN == w->window_type) { + if (w->a.override_redirect + || !wid_has_prop(ps, client, ps->atom_transient)) + w->window_type = WINTYPE_NORMAL; + else + w->window_type = WINTYPE_DIALOG; + } - // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take - // override-redirect windows or windows without WM_TRANSIENT_FOR as - // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. - if (WINTYPE_UNKNOWN == w->window_type) { - if (w->a.override_redirect - || !wid_has_prop(ps, client, ps->atom_transient)) - w->window_type = WINTYPE_NORMAL; - else - w->window_type = WINTYPE_DIALOG; + if (w->window_type != wtype_old) + win_on_wtype_change(ps, w); } // Get window group @@ -2288,6 +2403,7 @@ win_mark_client(session_t *ps, win *w, Window client) { win_get_name(ps, w); win_get_class(ps, w); win_get_role(ps, w); + win_on_wdata_change(ps, w); } // Update window focus state @@ -2393,6 +2509,7 @@ add_win(session_t *ps, Window id, Window prev) { .cache_sblst = NULL, .cache_fblst = NULL, .cache_fcblst = NULL, + .cache_ivclst = NULL, .opacity = 0, .opacity_tgt = 0, @@ -2423,6 +2540,9 @@ add_win(session_t *ps, Window id, Window prev) { .dim = false, .dim_alpha_pict = None, + + .invert_color = false, + .invert_color_force = UNSET, }; // Reject overlay window and already added windows @@ -3480,8 +3600,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { && (ps->atom_name == ev->atom || ps->atom_name_ewmh == ev->atom)) { win *w = find_toplevel(ps, ev->window); if (w && 1 == win_get_name(ps, w)) { - determine_shadow(ps, w); - win_update_focused(ps, w); + win_on_wdata_change(ps, w); } } @@ -3490,8 +3609,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { win *w = find_toplevel(ps, ev->window); if (w) { win_get_class(ps, w); - determine_shadow(ps, w); - win_update_focused(ps, w); + win_on_wdata_change(ps, w); } } @@ -3499,8 +3617,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (ps->o.track_wdata && ps->atom_role == ev->atom) { win *w = find_toplevel(ps, ev->window); if (w && 1 == win_get_role(ps, w)) { - determine_shadow(ps, w); - win_update_focused(ps, w); + win_on_wdata_change(ps, w); } } @@ -3816,6 +3933,9 @@ usage(void) { "--blur-background-fixed\n" " Use fixed blur strength instead of adjusting according to window\n" " opacity.\n" + "--invert-color-include condition\n" + " Specify a list of conditions of windows that should be painted with\n" + " inverted color. Resource-hogging, and is not well tested.\n" "\n" "Format of a condition:\n" "\n" @@ -4242,6 +4362,8 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude"); // --focus-exclude parse_cfg_condlst(&cfg, &ps->o.focus_blacklist, "focus-exclude"); + // --invert-color-include + parse_cfg_condlst(&cfg, &ps->o.invert_color_list, "invert-color-include"); // --blur-background lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background); // --blur-background-frame @@ -4315,6 +4437,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { "blur-background-fixed", no_argument, NULL, 285 }, { "dbus", no_argument, NULL, 286 }, { "logpath", required_argument, NULL, 287 }, + { "invert-color-include", required_argument, NULL, 288 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4554,6 +4677,10 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // --logpath ps->o.logpath = mstrcpy(optarg); break; + case 288: + // --invert-color-include + condlst_add(&ps->o.invert_color_list, optarg); + break; default: usage(); break; @@ -5291,12 +5418,14 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_opacity_override = false, .frame_opacity = 0.0, .detect_client_opacity = false, - .inactive_dim = 0.0, - .inactive_dim_fixed = false, .alpha_step = 0.03, + .blur_background = false, .blur_background_frame = false, .blur_background_fixed = false, + .inactive_dim = 0.0, + .inactive_dim_fixed = false, + .invert_color_list = NULL, .wintype_focus = { false }, .use_ewmh_active_win = false, @@ -5337,6 +5466,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .black_picture = None, .cshadow_picture = None, + .white_picture = None, .gaussian_map = NULL, .cgsize = 0, .shadow_corner = NULL, @@ -5555,6 +5685,7 @@ session_init(session_t *ps_old, int argc, char **argv) { init_filters(ps); ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0); + ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1); // Generates another Picture for shadows if the color is modified by // user @@ -5658,6 +5789,8 @@ session_destroy(session_t *ps) { // Free blacklists free_wincondlst(&ps->o.shadow_blacklist); free_wincondlst(&ps->o.fade_blacklist); + free_wincondlst(&ps->o.focus_blacklist); + free_wincondlst(&ps->o.invert_color_list); // Free ignore linked list { @@ -5680,6 +5813,7 @@ session_destroy(session_t *ps) { free_picture(ps, &ps->cshadow_picture); free_picture(ps, &ps->black_picture); + free_picture(ps, &ps->white_picture); // Free tgt_{buffer,picture} and root_picture if (ps->tgt_buffer == ps->tgt_picture) diff --git a/compton.h b/compton.h index cb4f623f9..f58711a2a 100644 --- a/compton.h +++ b/compton.h @@ -211,6 +211,14 @@ typedef enum { WMODE_ARGB } winmode_t; +/// Structure representing needed window updates. +typedef struct { + bool shadow : 1; + bool fade : 1; + bool focus : 1; + bool invert_color : 1; +} win_upd_t; + /// Structure representing Window property value. typedef struct { // All pointers have the same length, right? @@ -363,13 +371,10 @@ typedef struct { /// Whether to detect _NET_WM_OPACITY on client windows. Used on window /// managers that don't pass _NET_WM_OPACITY to frame windows. bool detect_client_opacity; - /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. - double inactive_dim; - /// Whether to use fixed inactive dim opacity, instead of deciding - /// based on window opacity. - bool inactive_dim_fixed; /// Step for pregenerating alpha pictures. 0.01 - 1.0. double alpha_step; + + // === Other window processing === /// Whether to blur background of semi-transparent / ARGB windows. bool blur_background; /// Whether to blur background when the window frame is not opaque. @@ -378,6 +383,13 @@ typedef struct { /// Whether to use fixed blur strength instead of adjusting according /// to window opacity. bool blur_background_fixed; + /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. + double inactive_dim; + /// Whether to use fixed inactive dim opacity, instead of deciding + /// based on window opacity. + bool inactive_dim_fixed; + /// Conditions of windows to have inverted colors. + wincond_t *invert_color_list; // === Focus related === /// Consider windows of specific types to be always focused. @@ -398,7 +410,6 @@ typedef struct { bool track_wdata; /// Whether compton needs to track window leaders. bool track_leader; - } options_t; /// Structure containing all necessary data for a compton session. @@ -505,6 +516,8 @@ typedef struct { Picture black_picture; /// 1x1 Picture of the shadow color. Picture cshadow_picture; + /// 1x1 white Picture. + Picture white_picture; /// Gaussian map of shadow. conv *gaussian_map; // for shadow precomputation @@ -699,6 +712,7 @@ typedef struct _win { wincond_t *cache_sblst; wincond_t *cache_fblst; wincond_t *cache_fcblst; + wincond_t *cache_ivclst; // Opacity-related members /// Current window opacity. @@ -758,6 +772,12 @@ typedef struct _win { /// Picture for dimming. Affected by user-specified inactive dim /// opacity and window opacity. Picture dim_alpha_pict; + + /// Whether to invert window color. + bool invert_color; + /// Override value of window color inversion state. Set by D-Bus method + /// calls. + switch_t invert_color_force; } win; /// Temporary structure used for communication between @@ -1860,7 +1880,7 @@ static double get_opacity_percent(win *w); static void -determine_mode(session_t *ps, win *w); +win_determine_mode(session_t *ps, win *w); static void calc_opacity(session_t *ps, win *w); @@ -1902,7 +1922,7 @@ static inline void win_set_focused(session_t *ps, win *w, bool focused); static void -determine_fade(session_t *ps, win *w); +win_determine_fade(session_t *ps, win *w); static void win_update_shape_raw(session_t *ps, win *w); @@ -1917,7 +1937,16 @@ static void win_update_prop_shadow(session_t *ps, win *w); static void -determine_shadow(session_t *ps, win *w); +win_determine_shadow(session_t *ps, win *w); + +static void +win_on_wtype_change(session_t *ps, win *w); + +static void +win_on_wdata_change(session_t *ps, win *w); + +static void +win_upd_run(session_t *ps, win *w, win_upd_t *pupd); static void calc_win_size(session_t *ps, win *w); From a5dc829944175cab684e5b4488ddff409f308099 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 13 Jan 2013 13:44:05 +0800 Subject: [PATCH 102/178] Bug fix #75: --invert-color-include not working & others - Fix a small bug that breaks --invert-color-include if no other blacklists are present. Thanks to MaskRay and xiaq for reporting. - Disable --unredir-if-possible for multi-screen setups. - Fix a bug that causes --no-fading-openclose to have no effect in some cases. Add w->in_openclose to keep track of window open/close state. --- compton.c | 34 ++++++++++++++++++++++------------ compton.h | 6 ++++-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/compton.c b/compton.c index 9155a84e0..f933d437d 100644 --- a/compton.c +++ b/compton.c @@ -1358,9 +1358,11 @@ paint_preprocess(session_t *ps, win *list) { if (is_highest && to_paint) { is_highest = false; + // Disable unredirection for multi-screen setups if (WMODE_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) - && win_is_fullscreen(ps, w)) + && win_is_fullscreen(ps, w) + && ScreenCount(ps->dpy) <= 1) ps->unredir_possible = true; } @@ -1966,20 +1968,18 @@ map_win(session_t *ps, Window id) { win_determine_shadow(ps, w); // Set fading state + w->in_openclose = false; if (ps->o.no_fading_openclose) { set_fade_callback(ps, w, finish_map_win, true); - // Must be set after we execute the old fade callback, in case we - // receive two continuous MapNotify for the same window - w->fade = false; + w->in_openclose = true; } else { set_fade_callback(ps, w, NULL, true); - win_determine_fade(ps, w); } + win_determine_fade(ps, w); w->damaged = true; - /* if any configure events happened while the window was unmapped, then configure the window to its correct place */ @@ -1990,14 +1990,18 @@ map_win(session_t *ps, Window id) { static void finish_map_win(session_t *ps, win *w) { - if (ps->o.no_fading_openclose) + w->in_openclose = false; + if (ps->o.no_fading_openclose) { win_determine_fade(ps, w); + } } static void finish_unmap_win(session_t *ps, win *w) { w->damaged = false; + w->in_openclose = false; + update_reg_ignore_expire(ps, w); if (w->extents != None) { @@ -2031,8 +2035,10 @@ unmap_win(session_t *ps, Window id) { // Fading out w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); - if (ps->o.no_fading_openclose) - w->fade = false; + if (ps->o.no_fading_openclose) { + w->in_openclose = true; + win_determine_fade(ps, w); + } // don't care about properties anymore win_ev_stop(ps, w); @@ -2154,7 +2160,10 @@ calc_dim(session_t *ps, win *w) { */ static void win_determine_fade(session_t *ps, win *w) { - w->fade = ps->o.wintype_fade[w->window_type]; + if (ps->o.no_fading_openclose && w->in_openclose) + w->fade = false; + else + w->fade = ps->o.wintype_fade[w->window_type]; } /** @@ -2485,12 +2494,13 @@ add_win(session_t *ps, Window id, Window prev) { .need_configure = false, .queue_configure = { }, .reg_ignore = None, - .destroyed = false, .widthb = 0, .heightb = 0, + .destroyed = false, .bounding_shaped = false, .rounded_corners = false, .to_paint = false, + .in_openclose = false, .client_win = None, .window_type = WINTYPE_UNKNOWN, @@ -4733,7 +4743,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { // Determine whether we need to track window name and class if (ps->o.shadow_blacklist || ps->o.fade_blacklist - || ps->o.focus_blacklist) + || ps->o.focus_blacklist || ps->o.invert_color_list) ps->o.track_wdata = true; // Determine whether we track window grouping diff --git a/compton.h b/compton.h index f58711a2a..2154f865b 100644 --- a/compton.h +++ b/compton.h @@ -667,16 +667,18 @@ typedef struct _win { /// opacity state, window geometry, window mapped/unmapped state, /// window mode, of this and all higher windows. XserverRegion reg_ignore; - /// Whether the window has been destroyed. - bool destroyed; /// Cached width/height of the window including border. int widthb, heightb; + /// Whether the window has been destroyed. + bool destroyed; /// Whether the window is bounding-shaped. bool bounding_shaped; /// Whether the window just have rounded corners. bool rounded_corners; /// Whether this window is to be painted. bool to_paint; + /// Whether this window is in open/close state. + bool in_openclose; // Client window related members /// ID of the top-level client window of the window. From 32132312985e3a7f63444dcd4b54f821520a6042 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 19 Jan 2013 20:20:27 +0800 Subject: [PATCH 103/178] Feature #80: D-Bus support - Add D-Bus support. Currently 7 methods are available: "reset" (same as SIGUSR1), "list_win" (list the windows compton manages), "win_get" (get a property of the window), "win_set" (set a property of the window), "find_win" (find window based on client window / focus), "opts_get" (get the value of a compton option), and "opts_set" (set the value of a compton option), together with 4 signals: "win_added", "win_destroyed", "win_mapped", "win_unmapped". - D-Bus support depends on libdbus. - As there are many items and my time is tight, no much tests are done. Bugs to be expected. - Create a new header file `common.h` that contains shared content. - Fix some bugs in timeout handling. - Update file headers in all source files. - Re-enable --unredir-if-possible on multi-screen set-ups, as the user could turn if off manually anyway. - Check if the window is mapped in `repair_win()`. - Add ps->track_atom_lst and its handlers, to prepare for the new condition format. - Known issue 1: "win_get", "win_set", "opts_get", "opts_set" support a very limited number of targets only. New ones will be added gradually. - Known issue 2: Accidental drop of D-Bus connection is not handled. - Known issue 3: Introspection does not reveal all available methods, because some methods have unpredictable prototypes. Still hesitating about what to do... - Known issue 4: Error handling is not finished yet. Compton does not always reply with the correct error message (but it does print out the correct error message, usually). --- common.h | 1370 +++++++++++++++++++++++++++++++++++++++++++++++++++++ compton.c | 155 +++++- compton.h | 1256 +----------------------------------------------- dbus.c | 920 +++++++++++++++++++++++++++++++++++ dbus.h | 213 +++++++++ 5 files changed, 2650 insertions(+), 1264 deletions(-) create mode 100644 common.h create mode 100644 dbus.c create mode 100644 dbus.h diff --git a/common.h b/common.h new file mode 100644 index 000000000..31c9c2002 --- /dev/null +++ b/common.h @@ -0,0 +1,1370 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +// === Options === + +// Debug options, enable them using -D in CFLAGS +// #define DEBUG_REPAINT 1 +// #define DEBUG_EVENTS 1 +// #define DEBUG_RESTACK 1 +// #define DEBUG_WINTYPE 1 +// #define DEBUG_CLIENTWIN 1 +// #define DEBUG_WINDATA 1 +// #define DEBUG_WINMATCH 1 +// #define DEBUG_REDIR 1 +// #define DEBUG_ALLOC_REG 1 +// #define DEBUG_FRAME 1 +// #define DEBUG_LEADER 1 +// #define MONITOR_REPAINT 1 + +// Whether to enable PCRE regular expression support in blacklists, enabled +// by default +// #define CONFIG_REGEX_PCRE 1 +// Whether to enable JIT support of libpcre. This may cause problems on PaX +// kernels. +// #define CONFIG_REGEX_PCRE_JIT 1 +// Whether to enable parsing of configuration files using libconfig. +// #define CONFIG_LIBCONFIG 1 +// Whether we are using a legacy version of libconfig (1.3.x). +// #define CONFIG_LIBCONFIG_LEGACY 1 +// Whether to enable DRM VSync support +// #define CONFIG_VSYNC_DRM 1 +// Whether to enable OpenGL VSync support +// #define CONFIG_VSYNC_OPENGL 1 +// Whether to enable DBus support with libdbus. +// #define CONFIG_DBUS 1 + +// === Includes === + +// For some special functions +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// libpcre +#ifdef CONFIG_REGEX_PCRE +#include + +// For compatiblity with +#include +#endif + +// libdbus +#ifdef CONFIG_DBUS +#include +#endif + +// libGL +#ifdef CONFIG_VSYNC_OPENGL +#include +#endif + +// === Macros === + +#define MSTR_(s) #s +#define MSTR(s) MSTR_(s) + +/// Print out an error message. +#define printf_err(format, ...) \ + fprintf(stderr, format "\n", ## __VA_ARGS__) + +/// Print out an error message with function name. +#define printf_errf(format, ...) \ + printf_err("%s" format, __func__, ## __VA_ARGS__) + +/// Print out an error message with function name, and quit with a +/// specific exit code. +#define printf_errfq(code, format, ...) { \ + printf_err("%s" format, __func__, ## __VA_ARGS__); \ + exit(code); \ +} + +/// Print out a debug message. +#define printf_dbg(format, ...) \ + printf(format, ## __VA_ARGS__); \ + fflush(stdout) + +/// Print out a debug message with function name. +#define printf_dbgf(format, ...) \ + printf_dbg("%s" format, __func__, ## __VA_ARGS__) + +// Use #s here to prevent macro expansion +/// Macro used for shortening some debugging code. +#define CASESTRRET(s) case s: return #s + +// === Constants === +#if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) +#error libXcomposite version unsupported +#endif + +#define ROUNDED_PERCENT 0.05 +#define ROUNDED_PIXELS 10 + +#define OPAQUE 0xffffffff +#define REGISTER_PROP "_NET_WM_CM_S" + +#define TIME_MS_MAX LONG_MAX +#define FADE_DELTA_TOLERANCE 0.2 +#define SWOPTI_TOLERANCE 3000 +#define TIMEOUT_RUN_TOLERANCE 0.05 +#define WIN_GET_LEADER_MAX_RECURSION 20 + +#define SEC_WRAP (15L * 24L * 60L * 60L) + +#define NS_PER_SEC 1000000000L +#define US_PER_SEC 1000000L +#define MS_PER_SEC 1000 + +#define XRFILTER_CONVOLUTION "convolution" +#define XRFILTER_GUASSIAN "gaussian" +#define XRFILTER_BINOMIAL "binomial" + +// Window flags + +// Window size is changed +#define WFLAG_SIZE_CHANGE 0x0001 +// Window size/position is changed +#define WFLAG_POS_CHANGE 0x0002 +// Window opacity / dim state changed +#define WFLAG_OPCT_CHANGE 0x0004 + +// === Types === + +typedef uint32_t opacity_t; +typedef long time_ms_t; + +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_t; + +/// Enumeration type to represent switches. +typedef enum { + OFF, // false + ON, // true + UNSET +} switch_t; + +/// Enumeration type of window painting mode. +typedef enum { + WMODE_TRANS, + WMODE_SOLID, + WMODE_ARGB +} winmode_t; + +/// Structure representing needed window updates. +typedef struct { + bool shadow : 1; + bool fade : 1; + bool focus : 1; + bool invert_color : 1; +} win_upd_t; + +/// Structure representing Window property value. +typedef struct { + // All pointers have the same length, right? + // I wanted to use anonymous union but it's a GNU extension... + union { + unsigned char *p8; + short *p16; + long *p32; + } data; + unsigned long nitems; + Atom type; + int format; +} winprop_t; + +typedef struct _ignore { + struct _ignore *next; + unsigned long sequence; +} ignore_t; + +enum wincond_target { + CONDTGT_NAME, + CONDTGT_CLASSI, + CONDTGT_CLASSG, + CONDTGT_ROLE, +}; + +enum wincond_type { + CONDTP_EXACT, + CONDTP_ANYWHERE, + CONDTP_FROMSTART, + CONDTP_WILDCARD, + CONDTP_REGEX_PCRE, +}; + +#define CONDF_IGNORECASE 0x0001 + +typedef struct _wincond { + enum wincond_target target; + enum wincond_type type; + char *pattern; +#ifdef CONFIG_REGEX_PCRE + pcre *regex_pcre; + pcre_extra *regex_pcre_extra; +#endif + int16_t flags; + struct _wincond *next; +} wincond_t; + +/// VSync modes. +typedef enum { + VSYNC_NONE, + VSYNC_DRM, + VSYNC_OPENGL, +} vsync_t; + +#ifdef CONFIG_VSYNC_OPENGL +typedef int (*f_WaitVideoSync) (int, int, unsigned *); +typedef int (*f_GetVideoSync) (unsigned *); +#endif + +typedef struct { + int size; + double *data; +} conv; + +/// Linked list type of atoms. +typedef struct _latom { + Atom atom; + struct _latom *next; +} latom_t; + +struct _timeout_t; + +struct _win; + +/// Structure representing all options. +typedef struct { + // === General === + char *display; + /// Whether to try to detect WM windows and mark them as focused. + bool mark_wmwin_focused; + /// Whether to mark override-redirect windows as focused. + bool mark_ovredir_focused; + /// Whether to fork to background. + bool fork_after_register; + /// Whether to detect rounded corners. + bool detect_rounded_corners; + /// Whether to paint on X Composite overlay window instead of root + /// window. + bool paint_on_overlay; + /// Whether to unredirect all windows if a full-screen opaque window + /// is detected. + bool unredir_if_possible; + /// Whether to enable D-Bus support. + bool dbus; + /// Path to log file. + char *logpath; + /// Whether to work under synchronized mode for debugging. + bool synchronize; + + // === VSync & software optimization === + /// User-specified refresh rate. + int refresh_rate; + /// Whether to enable refresh-rate-based software optimization. + bool sw_opti; + /// VSync method to use; + vsync_t vsync; + /// Whether to enable double buffer. + bool dbe; + /// Whether to do VSync aggressively. + bool vsync_aggressive; + + // === Shadow === + /// Enable/disable shadow for specific window types. + bool wintype_shadow[NUM_WINTYPES]; + /// Red, green and blue tone of the shadow. + double shadow_red, shadow_green, shadow_blue; + int shadow_radius; + int shadow_offset_x, shadow_offset_y; + double shadow_opacity; + bool clear_shadow; + /// Shadow blacklist. A linked list of conditions. + wincond_t *shadow_blacklist; + /// Whether bounding-shaped window should be ignored. + bool shadow_ignore_shaped; + /// Whether to respect _COMPTON_SHADOW. + bool respect_prop_shadow; + + // === Fading === + /// Enable/disable fading for specific window types. + bool wintype_fade[NUM_WINTYPES]; + /// How much to fade in in a single fading step. + opacity_t fade_in_step; + /// How much to fade out in a single fading step. + opacity_t fade_out_step; + /// Fading time delta. In milliseconds. + time_ms_t fade_delta; + /// Whether to disable fading on window open/close. + bool no_fading_openclose; + /// Fading blacklist. A linked list of conditions. + wincond_t *fade_blacklist; + + // === Opacity === + /// Default opacity for specific window types + double wintype_opacity[NUM_WINTYPES]; + /// Default opacity for inactive windows. + /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for + /// not enabled, default. + opacity_t inactive_opacity; + /// Whether inactive_opacity overrides the opacity set by window + /// attributes. + bool inactive_opacity_override; + /// Frame opacity. Relative to window opacity, also affects shadow + /// opacity. + double frame_opacity; + /// Whether to detect _NET_WM_OPACITY on client windows. Used on window + /// managers that don't pass _NET_WM_OPACITY to frame windows. + bool detect_client_opacity; + /// Step for pregenerating alpha pictures. 0.01 - 1.0. + double alpha_step; + + // === Other window processing === + /// Whether to blur background of semi-transparent / ARGB windows. + bool blur_background; + /// Whether to blur background when the window frame is not opaque. + /// Implies blur_background. + bool blur_background_frame; + /// Whether to use fixed blur strength instead of adjusting according + /// to window opacity. + bool blur_background_fixed; + /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. + double inactive_dim; + /// Whether to use fixed inactive dim opacity, instead of deciding + /// based on window opacity. + bool inactive_dim_fixed; + /// Conditions of windows to have inverted colors. + wincond_t *invert_color_list; + + // === Focus related === + /// Consider windows of specific types to be always focused. + bool wintype_focus[NUM_WINTYPES]; + /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. + bool use_ewmh_active_win; + /// A list of windows always to be considered focused. + wincond_t *focus_blacklist; + /// Whether to do window grouping with WM_TRANSIENT_FOR. + bool detect_transient; + /// Whether to do window grouping with WM_CLIENT_LEADER. + bool detect_client_leader; + + // === Calculated === + /// Whether compton needs to track focus changes. + bool track_focus; + /// Whether compton needs to track window name and class. + bool track_wdata; + /// Whether compton needs to track window leaders. + bool track_leader; +} options_t; + +/// Structure containing all necessary data for a compton session. +typedef struct { + // === Display related === + /// Display in use. + Display *dpy; + /// Default screen. + int scr; + /// Default visual. + Visual *vis; + /// Default depth. + int depth; + /// Root window. + Window root; + /// Height of root window. + int root_height; + /// Width of root window. + int root_width; + // Damage of root window. + // Damage root_damage; + /// X Composite overlay window. Used if --paint-on-overlay. + Window overlay; + /// Picture of the root window background. + Picture root_tile; + /// A region of the size of the screen. + XserverRegion screen_reg; + /// Picture of root window. Destination of painting in no-DBE painting + /// mode. + Picture root_picture; + /// A Picture acting as the painting target. + Picture tgt_picture; + /// Temporary buffer to paint to before sending to display. + Picture tgt_buffer; + /// DBE back buffer for root window. Used in DBE painting mode. + XdbeBackBuffer root_dbe; + /// Window ID of the window we register as a symbol. + Window reg_win; + + // === Operation related === + /// Program options. + options_t o; + /// File descriptors to check for reading. + fd_set *pfds_read; + /// File descriptors to check for writing. + fd_set *pfds_write; + /// File descriptors to check for exceptions. + fd_set *pfds_except; + /// Largest file descriptor in fd_set-s above. + int nfds_max; + /// Linked list of all timeouts. + struct _timeout_t *tmout_lst; + /// Whether we have received an event in this cycle. + bool ev_received; + /// Whether the program is idling. I.e. no fading, no potential window + /// changes. + bool idling; + /// Program start time. + struct timeval time_start; + /// The region needs to painted on next paint. + XserverRegion all_damage; + /// Whether all windows are currently redirected. + bool redirected; + /// Whether there's a highest full-screen window, and all windows could + /// be unredirected. + bool unredir_possible; + /// Pre-generated alpha pictures. + Picture *alpha_picts; + /// Whether all reg_ignore of windows should expire in this paint. + bool reg_ignore_expire; + /// Time of last fading. In milliseconds. + time_ms_t fade_time; + /// Head pointer of the error ignore linked list. + ignore_t *ignore_head; + /// Pointer to the next member of tail element of the error + /// ignore linked list. + ignore_t **ignore_tail; + /// Reset program after next paint. + bool reset; + + // === Expose event related === + /// Pointer to an array of XRectangle-s of exposed region. + XRectangle *expose_rects; + /// Number of XRectangle-s in expose_rects. + int size_expose; + /// Index of the next free slot in expose_rects. + int n_expose; + + // === Window related === + /// Linked list of all windows. + struct _win *list; + /// Pointer to win of current active window. Used by + /// EWMH _NET_ACTIVE_WINDOW focus detection. In theory, + /// it's more reliable to store the window ID directly here, just in + /// case the WM does something extraordinary, but caching the pointer + /// means another layer of complexity. + struct _win *active_win; + /// Window ID of leader window of currently active window. Used for + /// subsidiary window detection. + Window active_leader; + + // === Shadow/dimming related === + /// 1x1 black Picture. + Picture black_picture; + /// 1x1 Picture of the shadow color. + Picture cshadow_picture; + /// 1x1 white Picture. + Picture white_picture; + /// Gaussian map of shadow. + conv *gaussian_map; + // for shadow precomputation + /// Shadow depth on one side. + int cgsize; + /// Pre-computed color table for corners of shadow. + unsigned char *shadow_corner; + /// Pre-computed color table for a side of shadow. + unsigned char *shadow_top; + + // === Software-optimization-related === + /// Currently used refresh rate. + short refresh_rate; + /// Interval between refresh in nanoseconds. + long refresh_intv; + /// Nanosecond offset of the first painting. + long paint_tm_offset; + + #ifdef CONFIG_VSYNC_DRM + // === DRM VSync related === + /// File descriptor of DRI device file. Used for DRM VSync. + int drm_fd; + #endif + + #ifdef CONFIG_VSYNC_OPENGL + // === OpenGL VSync related === + /// GLX context. + GLXContext glx_context; + /// Pointer to glXGetVideoSyncSGI function. + f_GetVideoSync glx_get_video_sync; + /// Pointer to glXWaitVideoSyncSGI function. + f_WaitVideoSync glx_wait_video_sync; + #endif + + // === X extension related === + /// Event base number for X Fixes extension. + int xfixes_event; + /// Error base number for X Fixes extension. + int xfixes_error; + /// Event base number for X Damage extension. + int damage_event; + /// Error base number for X Damage extension. + int damage_error; + /// Event base number for X Render extension. + int render_event; + /// Error base number for X Render extension. + int render_error; + /// Event base number for X Composite extension. + int composite_event; + /// Error base number for X Composite extension. + int composite_error; + /// Major opcode for X Composite extension. + int composite_opcode; + /// Whether X Composite NameWindowPixmap is available. Aka if X + /// Composite version >= 0.2. + bool has_name_pixmap; + /// Whether X Shape extension exists. + bool shape_exists; + /// Event base number for X Shape extension. + int shape_event; + /// Error base number for X Shape extension. + int shape_error; + /// Whether X RandR extension exists. + bool randr_exists; + /// Event base number for X RandR extension. + int randr_event; + /// Error base number for X RandR extension. + int randr_error; + #ifdef CONFIG_VSYNC_OPENGL + /// Whether X GLX extension exists. + bool glx_exists; + /// Event base number for X GLX extension. + int glx_event; + /// Error base number for X GLX extension. + int glx_error; + #endif + /// Whether X DBE extension exists. + bool dbe_exists; + /// Whether X Render convolution filter exists. + bool xrfilter_convolution_exists; + + // === Atoms === + /// Atom of property _NET_WM_OPACITY. + Atom atom_opacity; + /// Atom of _NET_FRAME_EXTENTS. + Atom atom_frame_extents; + /// Property atom to identify top-level frame window. Currently + /// WM_STATE. + Atom atom_client; + /// Atom of property WM_NAME. + Atom atom_name; + /// Atom of property _NET_WM_NAME. + Atom atom_name_ewmh; + /// Atom of property WM_CLASS. + Atom atom_class; + /// Atom of property WM_WINDOW_ROLE. + Atom atom_role; + /// Atom of property WM_TRANSIENT_FOR. + Atom atom_transient; + /// Atom of property WM_CLIENT_LEADER. + Atom atom_client_leader; + /// Atom of property _NET_ACTIVE_WINDOW. + Atom atom_ewmh_active_win; + /// Atom of property _COMPTON_SHADOW. + Atom atom_compton_shadow; + /// Atom of property _NET_WM_WINDOW_TYPE. + Atom atom_win_type; + /// Array of atoms of all possible window types. + Atom atoms_wintypes[NUM_WINTYPES]; + /// Linked list of additional atoms to track. + latom_t *track_atom_lst; + +#ifdef CONFIG_DBUS + // === DBus related === + // DBus connection. + DBusConnection *dbus_conn; + // DBus service name. + char *dbus_service; +#endif +} session_t; + +/// Structure representing a top-level window compton manages. +typedef struct _win { + /// Pointer to the next structure in the linked list. + struct _win *next; + /// Pointer to the next higher window to paint. + struct _win *prev_trans; + + // Core members + /// ID of the top-level frame window. + Window id; + /// Window attributes. + XWindowAttributes a; + /// Window painting mode. + winmode_t mode; + /// Whether the window has been damaged at least once. + bool damaged; + /// Damage of the window. + Damage damage; + /// NameWindowPixmap of the window. + Pixmap pixmap; + /// Picture of the window. + Picture picture; + /// Bounding shape of the window. + XserverRegion border_size; + /// Region of the whole window, shadow region included. + XserverRegion extents; + /// Window flags. Definitions above. + int_fast16_t flags; + /// Whether there's a pending ConfigureNotify happening + /// when the window is unmapped. + bool need_configure; + /// Queued ConfigureNotify when the window is unmapped. + XConfigureEvent queue_configure; + /// Region to be ignored when painting. Basically the region where + /// higher opaque windows will paint upon. Depends on window frame + /// opacity state, window geometry, window mapped/unmapped state, + /// window mode, of this and all higher windows. + XserverRegion reg_ignore; + /// Cached width/height of the window including border. + int widthb, heightb; + /// Whether the window has been destroyed. + bool destroyed; + /// Whether the window is bounding-shaped. + bool bounding_shaped; + /// Whether the window just have rounded corners. + bool rounded_corners; + /// Whether this window is to be painted. + bool to_paint; + /// Whether this window is in open/close state. + bool in_openclose; + + // Client window related members + /// ID of the top-level client window of the window. + Window client_win; + /// Type of the window. + wintype_t window_type; + /// Whether it looks like a WM window. We consider a window WM window if + /// it does not have a decedent with WM_STATE and it is not override- + /// redirected itself. + bool wmwin; + /// Leader window ID of the window. + Window leader; + /// Cached topmost window ID of the window. + Window cache_leader; + + // Focus-related members + /// Whether the window is to be considered focused. + bool focused; + /// Override value of window focus state. Set by D-Bus method calls. + switch_t focused_force; + /// Whether the window is actually focused. + bool focused_real; + + // Blacklist related members + /// Name of the window. + char *name; + /// Window instance class of the window. + char *class_instance; + /// Window general class of the window. + char *class_general; + /// WM_WINDOW_ROLE value of the window. + char *role; + wincond_t *cache_sblst; + wincond_t *cache_fblst; + wincond_t *cache_fcblst; + wincond_t *cache_ivclst; + + // Opacity-related members + /// Current window opacity. + opacity_t opacity; + /// Target window opacity. + opacity_t opacity_tgt; + /// Cached value of opacity window attribute. + opacity_t opacity_prop; + /// Cached value of opacity window attribute on client window. For + /// broken window managers not transferring client window's + /// _NET_WM_OPACITY value + opacity_t opacity_prop_client; + /// Alpha mask Picture to render window with opacity. + Picture alpha_pict; + + // Fading-related members + /// Do not fade if it's false. Change on window type change. + /// Used by fading blacklist in the future. + bool fade; + /// Callback to be called after fading completed. + void (*fade_callback) (session_t *ps, struct _win *w); + + // Frame-opacity-related members + /// Current window frame opacity. Affected by window opacity. + double frame_opacity; + /// Alpha mask Picture to render window frame with opacity. + Picture frame_alpha_pict; + /// Frame widths. Determined by client window attributes. + unsigned int left_width, right_width, top_width, bottom_width; + + // Shadow-related members + /// Whether a window has shadow. Calculated. + bool shadow; + /// Override value of window shadow state. Set by D-Bus method calls. + switch_t shadow_force; + /// Opacity of the shadow. Affected by window opacity and frame opacity. + double shadow_opacity; + /// X offset of shadow. Affected by commandline argument. + int shadow_dx; + /// Y offset of shadow. Affected by commandline argument. + int shadow_dy; + /// Width of shadow. Affected by window size and commandline argument. + int shadow_width; + /// Height of shadow. Affected by window size and commandline argument. + int shadow_height; + /// Picture to render shadow. Affected by window size. + Picture shadow_pict; + /// Alpha mask Picture to render shadow. Affected by shadow opacity. + Picture shadow_alpha_pict; + /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for + /// none. + long prop_shadow; + + // Dim-related members + /// Whether the window is to be dimmed. + bool dim; + /// Picture for dimming. Affected by user-specified inactive dim + /// opacity and window opacity. + Picture dim_alpha_pict; + + /// Whether to invert window color. + bool invert_color; + /// Override value of window color inversion state. Set by D-Bus method + /// calls. + switch_t invert_color_force; +} win; + +/// Temporary structure used for communication between +/// get_cfg() and parse_config(). +struct options_tmp { + bool no_dock_shadow; + bool no_dnd_shadow; + double menu_opacity; +}; + +/// Structure for a recorded timeout. +typedef struct _timeout_t { + bool enabled; + void *data; + bool (*callback)(session_t *ps, struct _timeout_t *ptmout); + time_ms_t interval; + time_ms_t firstrun; + time_ms_t lastrun; + struct _timeout_t *next; +} timeout_t; + +/// Enumeration for window event hints. +typedef enum { + WIN_EVMODE_UNKNOWN, + WIN_EVMODE_FRAME, + WIN_EVMODE_CLIENT +} win_evmode_t; + +extern const char *WINTYPES[NUM_WINTYPES]; +extern session_t *ps_g; + +// == Debugging code == +static inline void +print_timestamp(session_t *ps); + +#ifdef DEBUG_ALLOC_REG + +#include +#define BACKTRACE_SIZE 5 + +/** + * Print current backtrace, excluding the first two items. + * + * Stolen from glibc manual. + */ +static inline void +print_backtrace(void) { + void *array[BACKTRACE_SIZE]; + size_t size; + char **strings; + + size = backtrace(array, BACKTRACE_SIZE); + strings = backtrace_symbols(array, size); + + for (size_t i = 2; i < size; i++) + printf ("%s\n", strings[i]); + + free(strings); +} + +/** + * Wrapper of XFixesCreateRegion, for debugging. + */ +static inline XserverRegion +XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, + const char *func, int line) { + XserverRegion reg = XFixesCreateRegion(dpy, p, n); + print_timestamp(ps_g); + printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); + print_backtrace(); + fflush(stdout); + return reg; +} + +/** + * Wrapper of XFixesDestroyRegion, for debugging. + */ +static inline void +XFixesDestroyRegion_(Display *dpy, XserverRegion reg, + const char *func, int line) { + XFixesDestroyRegion(dpy, reg); + print_timestamp(ps_g); + printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); + fflush(stdout); +} + +#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) +#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) +#endif + +// === Functions === + +/** + * Return whether a struct timeval value is empty. + */ +static inline bool +timeval_isempty(struct timeval *ptv) { + if (!ptv) + return false; + + return ptv->tv_sec <= 0 && ptv->tv_usec <= 0; +} + +/** + * Compare a struct timeval with a time in milliseconds. + * + * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms + */ +static inline int +timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) { + assert(ptv); + + // We use those if statement instead of a - expression because of possible + // truncation problem from long to int. + { + long sec = ms / MS_PER_SEC; + if (ptv->tv_sec > sec) + return 1; + if (ptv->tv_sec < sec) + return -1; + } + + { + long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC); + if (ptv->tv_usec > usec) + return 1; + if (ptv->tv_usec < usec) + return -1; + } + + return 0; +} + +/** + * Subtracting two struct timeval values. + * + * Taken from glibc manual. + * + * Subtract the `struct timeval' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. + */ +static inline int +timeval_subtract(struct timeval *result, + struct timeval *x, + struct timeval *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_usec < y->tv_usec) { + long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; + y->tv_usec -= 1000000 * nsec; + y->tv_sec += nsec; + } + + if (x->tv_usec - y->tv_usec > 1000000) { + long nsec = (x->tv_usec - y->tv_usec) / 1000000; + y->tv_usec += 1000000 * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_usec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * Subtracting two struct timespec values. + * + * Taken from glibc manual. + * + * Subtract the `struct timespec' values X and Y, + * storing the result in RESULT. + * Return 1 if the difference is negative, otherwise 0. + */ +static inline int +timespec_subtract(struct timespec *result, + struct timespec *x, + struct timespec *y) { + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; + y->tv_nsec -= NS_PER_SEC * nsec; + y->tv_sec += nsec; + } + + if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { + long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; + y->tv_nsec += NS_PER_SEC * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * Get current time in struct timeval. + */ +static inline struct timeval __attribute__((const)) +get_time_timeval(void) { + struct timeval tv = { 0, 0 }; + + gettimeofday(&tv, NULL); + + // Return a time of all 0 if the call fails + return tv; +} + +/** + * Get current time in struct timespec. + * + * Note its starting time is unspecified. + */ +static inline struct timespec __attribute__((const)) +get_time_timespec(void) { + struct timespec tm = { 0, 0 }; + + clock_gettime(CLOCK_MONOTONIC, &tm); + + // Return a time of all 0 if the call fails + return tm; +} + + +/** + * Print time passed since program starts execution. + * + * Used for debugging. + */ +static inline void +print_timestamp(session_t *ps) { + struct timeval tm, diff; + + if (gettimeofday(&tm, NULL)) return; + + timeval_subtract(&diff, &tm, &ps->time_start); + printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); +} + +/** + * Allocate the space and copy a string. + */ +static inline char * __attribute__((const)) +mstrcpy(const char *src) { + char *str = malloc(sizeof(char) * (strlen(src) + 1)); + + strcpy(str, src); + + return str; +} + +/** + * Allocate the space and copy a string. + */ +static inline char * __attribute__((const)) +mstrncpy(const char *src, unsigned len) { + char *str = malloc(sizeof(char) * (len + 1)); + + strncpy(str, src, len); + + return str; +} + +/** + * Allocate the space and join two strings. + */ +static inline char * __attribute__((const)) +mstrjoin(const char *src1, const char *src2) { + char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); + + strcpy(str, src1); + strcat(str, src2); + + return str; +} + +/** + * Allocate the space and join two strings; + */ +static inline char * __attribute__((const)) +mstrjoin3(const char *src1, const char *src2, const char *src3) { + char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + + strlen(src3) + 1)); + + strcpy(str, src1); + strcat(str, src2); + strcat(str, src3); + + return str; +} + +/** + * Normalize an int value to a specific range. + * + * @param i int value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline int __attribute__((const)) +normalize_i_range(int i, int min, int max) { + if (i > max) return max; + if (i < min) return min; + return i; +} + +/** + * Select the larger integer of two. + */ +static inline int __attribute__((const)) +max_i(int a, int b) { + return (a > b ? a : b); +} + +/** + * Select the smaller integer of two. + */ +static inline int __attribute__((const)) +min_i(int a, int b) { + return (a > b ? b : a); +} + +/** + * Select the larger long integer of two. + */ +static inline long __attribute__((const)) +max_l(long a, long b) { + return (a > b ? a : b); +} + +/** + * Select the smaller long integer of two. + */ +static inline long __attribute__((const)) +min_l(long a, long b) { + return (a > b ? b : a); +} + +/** + * Normalize a double value to a specific range. + * + * @param d double value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline double __attribute__((const)) +normalize_d_range(double d, double min, double max) { + if (d > max) return max; + if (d < min) return min; + return d; +} + +/** + * Normalize a double value to 0.\ 0 - 1.\ 0. + * + * @param d double value to normalize + * @return normalized value + */ +static inline double __attribute__((const)) +normalize_d(double d) { + return normalize_d_range(d, 0.0, 1.0); +} + +timeout_t * +timeout_insert(session_t *ps, time_ms_t interval, + bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); + +void +timeout_invoke(session_t *ps, timeout_t *ptmout); + +bool +timeout_drop(session_t *ps, timeout_t *prm); + +/** + * Add a file descriptor to a select() fd_set. + */ +static inline bool +fds_insert_select(fd_set **ppfds, int fd) { + assert(fd <= FD_SETSIZE); + + if (!*ppfds) { + if ((*ppfds = malloc(sizeof(fd_set)))) { + FD_ZERO(*ppfds); + } + else { + fprintf(stderr, "Failed to allocate memory for select() fdset.\n"); + exit(1); + } + } + + FD_SET(fd, *ppfds); + + return true; +} + +/** + * Add a new file descriptor to wait for. + */ +static inline bool +fds_insert(session_t *ps, int fd, short events) { + bool result = true; + + ps->nfds_max = max_i(fd + 1, ps->nfds_max); + + if (POLLIN & events) + result = fds_insert_select(&ps->pfds_read, fd) && result; + if (POLLOUT & events) + result = fds_insert_select(&ps->pfds_write, fd) && result; + if (POLLPRI & events) + result = fds_insert_select(&ps->pfds_except, fd) && result; + + return result; +} + +/** + * Delete a file descriptor to wait for. + */ +static inline void +fds_drop(session_t *ps, int fd, short events) { + // Drop fd from respective fd_set-s + if (POLLIN & events && ps->pfds_read) + FD_CLR(fd, ps->pfds_read); + if (POLLOUT & events && ps->pfds_write) + FD_CLR(fd, ps->pfds_write); + if (POLLPRI & events && ps->pfds_except) + FD_CLR(fd, ps->pfds_except); +} + +#define CPY_FDS(key) \ + fd_set * key = NULL; \ + if (ps->key) { \ + key = malloc(sizeof(fd_set)); \ + memcpy(key, ps->key, sizeof(fd_set)); \ + if (!key) { \ + fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \ + exit(1); \ + } \ + } \ + +/** + * Poll for changes. + * + * poll() is much better than select(), but ppoll() does not exist on + * *BSD. + */ +static inline int +fds_poll(session_t *ps, struct timeval *ptv) { + // Copy fds + CPY_FDS(pfds_read); + CPY_FDS(pfds_write); + CPY_FDS(pfds_except); + + int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv); + + free(pfds_read); + free(pfds_write); + free(pfds_except); + + return ret; +} +#undef CPY_FDS + +/** + * Find a window from window id in window linked list of the session. + */ +static inline win * +find_win(session_t *ps, Window id) { + if (!id) + return NULL; + + win *w; + + for (w = ps->list; w; w = w->next) { + if (w->id == id && !w->destroyed) + return w; + } + + return 0; +} + +/** + * Find out the WM frame of a client window using existing data. + * + * @param id window ID + * @return struct _win object of the found window, NULL if not found + */ +static inline win * +find_toplevel(session_t *ps, Window id) { + if (!id) + return NULL; + + for (win *w = ps->list; w; w = w->next) { + if (w->client_win == id && !w->destroyed) + return w; + } + + return NULL; +} + +/** + * Find out the currently focused window. + * + * @return struct _win object of the found window, NULL if not found + */ +static inline win * +find_focused(session_t *ps) { + if (!ps->o.track_focus) + return NULL; + + for (win *w = ps->list; w; w = w->next) { + if (w->focused_real && !w->destroyed) + return w; + } + + return NULL; +} + +/** + * Copies a region + */ +static inline XserverRegion +copy_region(const session_t *ps, XserverRegion oldregion) { + XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); + + XFixesCopyRegion(ps->dpy, region, oldregion); + + return region; +} + +#ifdef CONFIG_DBUS +/** @name DBus handling + */ +///@{ +bool +cdbus_init(session_t *ps); + +void +cdbus_destroy(session_t *ps); + +void +cdbus_loop(session_t *ps); + +void +cdbus_ev_win_added(session_t *ps, win *w); + +void +cdbus_ev_win_destroyed(session_t *ps, win *w); + +void +cdbus_ev_win_mapped(session_t *ps, win *w); + +void +cdbus_ev_win_unmapped(session_t *ps, win *w); +//!@} + +/** @name DBus hooks + */ +///@{ +void +win_set_shadow_force(session_t *ps, win *w, switch_t val); + +void +win_set_focused_force(session_t *ps, win *w, switch_t val); + +void +win_set_invert_color_force(session_t *ps, win *w, switch_t val); + +void +force_repaint(session_t *ps); +//!@} +#endif diff --git a/compton.c b/compton.c index f933d437d..90133d11c 100644 --- a/compton.c +++ b/compton.c @@ -3,7 +3,7 @@ * * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * - * Copyright (c) 2011, Christopher Jeffrey + * Copyright (c) 2011-2013, Christopher Jeffrey * See LICENSE for more information. * */ @@ -837,7 +837,7 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { // Check if it's a mapped client window if (WIN_EVMODE_CLIENT == mode || ((w = find_toplevel(ps, wid)) && IsViewable == w->a.map_state)) { - if (ps->o.frame_opacity || ps->o.track_wdata + if (ps->o.frame_opacity || ps->o.track_wdata || ps->track_atom_lst || ps->o.detect_client_opacity) evmask |= PropertyChangeMask; } @@ -1361,8 +1361,7 @@ paint_preprocess(session_t *ps, win *list) { // Disable unredirection for multi-screen setups if (WMODE_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) - && win_is_fullscreen(ps, w) - && ScreenCount(ps->dpy) <= 1) + && win_is_fullscreen(ps, w)) ps->unredir_possible = true; } @@ -1848,6 +1847,9 @@ add_damage(session_t *ps, XserverRegion damage) { static void repair_win(session_t *ps, win *w) { + if (IsViewable != w->a.map_state) + return; + XserverRegion parts; if (!w->damaged) { @@ -1986,6 +1988,13 @@ map_win(session_t *ps, Window id) { if (w->need_configure) { configure_win(ps, &w->queue_configure); } + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_mapped(ps, w); + } +#endif } static void @@ -2042,6 +2051,13 @@ unmap_win(session_t *ps, Window id) { // don't care about properties anymore win_ev_stop(ps, w); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_unmapped(ps, w); + } +#endif } static opacity_t @@ -2583,8 +2599,8 @@ add_win(session_t *ps, Window id, Window prev) { // Fill structure new->id = id; - set_ignore_next(ps); + set_ignore_next(ps); if (!XGetWindowAttributes(ps->dpy, id, &new->a)) { // Failed to get window attributes. Which probably means, the window // is gone already. @@ -2608,7 +2624,14 @@ add_win(session_t *ps, Window id, Window prev) { new->next = *p; *p = new; - if (map_state == IsViewable) { +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_added(ps, new); + } +#endif + + if (IsViewable == map_state) { map_win(ps, id); } @@ -2811,6 +2834,13 @@ destroy_win(session_t *ps, Window id) { // Fading out the window w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, destroy_callback, false); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + cdbus_ev_win_destroyed(ps, w); + } +#endif } } @@ -3252,6 +3282,58 @@ win_get_class(session_t *ps, win *w) { return true; } +#ifdef CONFIG_DBUS +/** @name DBus hooks + */ +///@{ + +/** + * Set w->shadow_force of a window. + */ +void +win_set_shadow_force(session_t *ps, win *w, switch_t val) { + if (val != w->shadow_force) { + w->shadow_force = val; + win_determine_shadow(ps, w); + } +} + +/** + * Set w->focused_force of a window. + */ +void +win_set_focused_force(session_t *ps, win *w, switch_t val) { + if (val != w->focused_force) { + w->focused_force = val; + win_update_focused(ps, w); + } +} + +/** + * Set w->invert_color_force of a window. + */ +void +win_set_invert_color_force(session_t *ps, win *w, switch_t val) { + if (val != w->invert_color_force) { + w->invert_color_force = val; + win_determine_invert_color(ps, w); + } +} + +/** + * Force a full-screen repaint. + */ +void +force_repaint(session_t *ps) { + XserverRegion reg = None; + if (ps->screen_reg && (reg = copy_region(ps, ps->screen_reg))) { + ps->ev_received = true; + add_damage(ps, reg); + } +} +//!@} +#endif + #ifdef DEBUG_EVENTS static int ev_serial(XEvent *ev) { @@ -3647,6 +3729,17 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { } } + // Check for other atoms we are tracking + for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) { + if (platom->atom == ev->atom) { + win *w = find_win(ps, ev->window); + if (!w) + w = find_toplevel(ps, ev->window); + if (w) + win_on_wdata_change(ps, w); + break; + } + } } inline static void @@ -5129,8 +5222,7 @@ timeout_get_poll_time(session_t *ps) { // Traverse throught the timeout linked list for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) { if (ptmout->enabled) { - // Truncate the last run time to the closest interval - time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval; + time_ms_t newrun = timeout_get_newrun(ptmout); if (newrun <= now) { wait = 0; break; @@ -5149,7 +5241,7 @@ timeout_get_poll_time(session_t *ps) { /** * Insert a new timeout. */ -static timeout_t * +timeout_t * timeout_insert(session_t *ps, time_ms_t interval, bool (*callback)(session_t *ps, timeout_t *ptmout), void *data) { const static timeout_t tmout_def = { @@ -5184,7 +5276,7 @@ timeout_insert(session_t *ps, time_ms_t interval, * @return true if we have found the timeout and removed it, false * otherwise */ -static bool +bool timeout_drop(session_t *ps, timeout_t *prm) { timeout_t **pplast = &ps->tmout_lst; @@ -5224,12 +5316,14 @@ static bool timeout_run(session_t *ps) { const time_ms_t now = get_time_ms(); bool ret = false; + timeout_t *pnext = NULL; - for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = ptmout->next) { + for (timeout_t *ptmout = ps->tmout_lst; ptmout; ptmout = pnext) { + pnext = ptmout->next; if (ptmout->enabled) { const time_ms_t max = now + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE); - time_ms_t newrun = ptmout->firstrun + ((ptmout->lastrun - ptmout->firstrun) / ptmout->interval + 1) * ptmout->interval; + time_ms_t newrun = timeout_get_newrun(ptmout); if (newrun <= max) { ret = true; timeout_invoke(ps, ptmout); @@ -5243,7 +5337,7 @@ timeout_run(session_t *ps) { /** * Invoke a timeout. */ -static void +void timeout_invoke(session_t *ps, timeout_t *ptmout) { const time_ms_t now = get_time_ms(); ptmout->lastrun = now; @@ -5301,6 +5395,12 @@ mainloop(session_t *ps) { return true; } +#ifdef CONFIG_DBUS + if (ps->o.dbus) { + cdbus_loop(ps); + } +#endif + if (ps->reset) return false; @@ -5531,7 +5631,13 @@ session_init(session_t *ps_old, int argc, char **argv) { .atom_ewmh_active_win = None, .atom_compton_shadow = None, .atom_win_type = None, - .atoms_wintypes = { 0 } + .atoms_wintypes = { 0 }, + .track_atom_lst = NULL, + +#ifdef CONFIG_DBUS + .dbus_conn = NULL, + .dbus_service = NULL, +#endif }; // Allocate a session and copy default values into it @@ -5740,6 +5846,19 @@ session_init(session_t *ps_old, int argc, char **argv) { XUngrabServer(ps->dpy); + // Initialize DBus + if (ps->o.dbus) { +#ifdef CONFIG_DBUS + cdbus_init(ps); + if (!ps->dbus_conn) { + cdbus_destroy(ps); + ps->o.dbus = false; + } +#else + printf_errfq(1, "(): DBus support not compiled in!"); +#endif + } + // Fork to background, if asked if (ps->o.fork_after_register) { if (!fork_after(ps)) { @@ -5770,6 +5889,14 @@ session_destroy(session_t *ps) { // Stop listening to events on root window XSelectInput(ps->dpy, ps->root, 0); +#ifdef CONFIG_DBUS + // Kill DBus connection + if (ps->o.dbus) + cdbus_destroy(ps); + + free(ps->dbus_service); +#endif + // Free window linked list { win *next = NULL; diff --git a/compton.h b/compton.h index 2154f865b..c30991076 100644 --- a/compton.h +++ b/compton.h @@ -4,92 +4,20 @@ // Throw everything in here. -// === Options === - -// Debug options, enable them using -D in CFLAGS -// #define DEBUG_REPAINT 1 -// #define DEBUG_EVENTS 1 -// #define DEBUG_RESTACK 1 -// #define DEBUG_WINTYPE 1 -// #define DEBUG_CLIENTWIN 1 -// #define DEBUG_WINDATA 1 -// #define DEBUG_WINMATCH 1 -// #define DEBUG_REDIR 1 -// #define DEBUG_ALLOC_REG 1 -// #define DEBUG_FRAME 1 -// #define DEBUG_LEADER 1 -// #define MONITOR_REPAINT 1 - -// Whether to enable PCRE regular expression support in blacklists, enabled -// by default -// #define CONFIG_REGEX_PCRE 1 -// Whether to enable JIT support of libpcre. This may cause problems on PaX -// kernels. -// #define CONFIG_REGEX_PCRE_JIT 1 -// Whether to enable parsing of configuration files using libconfig -// #define CONFIG_LIBCONFIG 1 -// Whether to enable DRM VSync support -// #define CONFIG_VSYNC_DRM 1 -// Whether to enable OpenGL VSync support -// #define CONFIG_VSYNC_OPENGL 1 // === Includes === -// For some special functions -#define _GNU_SOURCE +#include "common.h" -#include -#include -#include -#include #include #include -#include -#include -#include #include #include #include -#include #include -#include #include #include -// libpcre -#ifdef CONFIG_REGEX_PCRE -#include - -// For compatiblity with -#include -#endif - -#ifdef CONFIG_DBUS -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #ifdef CONFIG_VSYNC_DRM #include // We references some definitions in drm.h, which could also be found in @@ -100,776 +28,6 @@ #include #endif -#ifdef CONFIG_VSYNC_OPENGL -#include -#endif - -// === Constants === -#if !(COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2) -#error libXcomposite version unsupported -#endif - -#define ROUNDED_PERCENT 0.05 -#define ROUNDED_PIXELS 10 - -#define OPAQUE 0xffffffff -#define REGISTER_PROP "_NET_WM_CM_S" - -#define TIME_MS_MAX LONG_MAX -#define FADE_DELTA_TOLERANCE 0.2 -#define SWOPTI_TOLERANCE 3000 -#define TIMEOUT_RUN_TOLERANCE 0.2 -#define WIN_GET_LEADER_MAX_RECURSION 20 - -#define SEC_WRAP (15L * 24L * 60L * 60L) - -#define NS_PER_SEC 1000000000L -#define US_PER_SEC 1000000L -#define MS_PER_SEC 1000 - -#define XRFILTER_CONVOLUTION "convolution" -#define XRFILTER_GUASSIAN "gaussian" -#define XRFILTER_BINOMIAL "binomial" - -// Window flags - -// Window size is changed -#define WFLAG_SIZE_CHANGE 0x0001 -// Window size/position is changed -#define WFLAG_POS_CHANGE 0x0002 -// Window opacity / dim state changed -#define WFLAG_OPCT_CHANGE 0x0004 - -// === Macros === - -// #define MSTR_(s) #s -// #define MSTR(s) MSTR_(s) - -/// Print out an error message. -#define printf_err(format, ...) \ - fprintf(stderr, format "\n", ## __VA_ARGS__) - -/// Print out an error message with function name. -#define printf_errf(format, ...) \ - printf_err("%s" format, __func__, ## __VA_ARGS__) - -/// Print out an error message with function name, and quit with a -/// specific exit code. -#define printf_errfq(code, format, ...) { \ - printf_err("%s" format, __func__, ## __VA_ARGS__); \ - exit(code); \ -} - -/// Print out a debug message. -#define printf_dbg(format, ...) \ - printf(format, ## __VA_ARGS__); \ - fflush(stdout) - -/// Print out a debug message with function name. -#define printf_dbgf(format, ...) \ - printf_dbg("%s" format, __func__, ## __VA_ARGS__) - -// Use #s here to prevent macro expansion -/// Macro used for shortening some debugging code. -#define CASESTRRET(s) case s: return #s - -// === Types === - -typedef uint32_t opacity_t; -typedef long time_ms_t; - -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_t; - -/// Enumeration type to represent switches. -typedef enum { - OFF, // false - ON, // true - UNSET -} switch_t; - -/// Enumeration type of window painting mode. -typedef enum { - WMODE_TRANS, - WMODE_SOLID, - WMODE_ARGB -} winmode_t; - -/// Structure representing needed window updates. -typedef struct { - bool shadow : 1; - bool fade : 1; - bool focus : 1; - bool invert_color : 1; -} win_upd_t; - -/// Structure representing Window property value. -typedef struct { - // All pointers have the same length, right? - // I wanted to use anonymous union but it's a GNU extension... - union { - unsigned char *p8; - short *p16; - long *p32; - } data; - unsigned long nitems; - Atom type; - int format; -} winprop_t; - -typedef struct _ignore { - struct _ignore *next; - unsigned long sequence; -} ignore_t; - -enum wincond_target { - CONDTGT_NAME, - CONDTGT_CLASSI, - CONDTGT_CLASSG, - CONDTGT_ROLE, -}; - -enum wincond_type { - CONDTP_EXACT, - CONDTP_ANYWHERE, - CONDTP_FROMSTART, - CONDTP_WILDCARD, - CONDTP_REGEX_PCRE, -}; - -#define CONDF_IGNORECASE 0x0001 - -typedef struct _wincond { - enum wincond_target target; - enum wincond_type type; - char *pattern; -#ifdef CONFIG_REGEX_PCRE - pcre *regex_pcre; - pcre_extra *regex_pcre_extra; -#endif - int16_t flags; - struct _wincond *next; -} wincond_t; - -/// VSync modes. -typedef enum { - VSYNC_NONE, - VSYNC_DRM, - VSYNC_OPENGL, -} vsync_t; - -#ifdef CONFIG_VSYNC_OPENGL -typedef int (*f_WaitVideoSync) (int, int, unsigned *); -typedef int (*f_GetVideoSync) (unsigned *); -#endif - -typedef struct { - int size; - double *data; -} conv; - -struct _timeout_t; - -struct _win; - -/// Structure representing all options. -typedef struct { - // === General === - char *display; - /// Whether to try to detect WM windows and mark them as focused. - bool mark_wmwin_focused; - /// Whether to mark override-redirect windows as focused. - bool mark_ovredir_focused; - /// Whether to fork to background. - bool fork_after_register; - /// Whether to detect rounded corners. - bool detect_rounded_corners; - /// Whether to paint on X Composite overlay window instead of root - /// window. - bool paint_on_overlay; - /// Whether to unredirect all windows if a full-screen opaque window - /// is detected. - bool unredir_if_possible; - /// Whether to enable D-Bus support. - bool dbus; - /// Path to log file. - char *logpath; - /// Whether to work under synchronized mode for debugging. - bool synchronize; - - // === VSync & software optimization === - /// User-specified refresh rate. - int refresh_rate; - /// Whether to enable refresh-rate-based software optimization. - bool sw_opti; - /// VSync method to use; - vsync_t vsync; - /// Whether to enable double buffer. - bool dbe; - /// Whether to do VSync aggressively. - bool vsync_aggressive; - - // === Shadow === - /// Enable/disable shadow for specific window types. - bool wintype_shadow[NUM_WINTYPES]; - /// Red, green and blue tone of the shadow. - double shadow_red, shadow_green, shadow_blue; - int shadow_radius; - int shadow_offset_x, shadow_offset_y; - double shadow_opacity; - bool clear_shadow; - /// Shadow blacklist. A linked list of conditions. - wincond_t *shadow_blacklist; - /// Whether bounding-shaped window should be ignored. - bool shadow_ignore_shaped; - /// Whether to respect _COMPTON_SHADOW. - bool respect_prop_shadow; - - // === Fading === - /// Enable/disable fading for specific window types. - bool wintype_fade[NUM_WINTYPES]; - /// How much to fade in in a single fading step. - opacity_t fade_in_step; - /// How much to fade out in a single fading step. - opacity_t fade_out_step; - /// Fading time delta. In milliseconds. - time_ms_t fade_delta; - /// Whether to disable fading on window open/close. - bool no_fading_openclose; - /// Fading blacklist. A linked list of conditions. - wincond_t *fade_blacklist; - - // === Opacity === - /// Default opacity for specific window types - double wintype_opacity[NUM_WINTYPES]; - /// Default opacity for inactive windows. - /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for - /// not enabled, default. - opacity_t inactive_opacity; - /// Whether inactive_opacity overrides the opacity set by window - /// attributes. - bool inactive_opacity_override; - /// Frame opacity. Relative to window opacity, also affects shadow - /// opacity. - double frame_opacity; - /// Whether to detect _NET_WM_OPACITY on client windows. Used on window - /// managers that don't pass _NET_WM_OPACITY to frame windows. - bool detect_client_opacity; - /// Step for pregenerating alpha pictures. 0.01 - 1.0. - double alpha_step; - - // === Other window processing === - /// Whether to blur background of semi-transparent / ARGB windows. - bool blur_background; - /// Whether to blur background when the window frame is not opaque. - /// Implies blur_background. - bool blur_background_frame; - /// Whether to use fixed blur strength instead of adjusting according - /// to window opacity. - bool blur_background_fixed; - /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. - double inactive_dim; - /// Whether to use fixed inactive dim opacity, instead of deciding - /// based on window opacity. - bool inactive_dim_fixed; - /// Conditions of windows to have inverted colors. - wincond_t *invert_color_list; - - // === Focus related === - /// Consider windows of specific types to be always focused. - bool wintype_focus[NUM_WINTYPES]; - /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. - bool use_ewmh_active_win; - /// A list of windows always to be considered focused. - wincond_t *focus_blacklist; - /// Whether to do window grouping with WM_TRANSIENT_FOR. - bool detect_transient; - /// Whether to do window grouping with WM_CLIENT_LEADER. - bool detect_client_leader; - - // === Calculated === - /// Whether compton needs to track focus changes. - bool track_focus; - /// Whether compton needs to track window name and class. - bool track_wdata; - /// Whether compton needs to track window leaders. - bool track_leader; -} options_t; - -/// Structure containing all necessary data for a compton session. -typedef struct { - // === Display related === - /// Display in use. - Display *dpy; - /// Default screen. - int scr; - /// Default visual. - Visual *vis; - /// Default depth. - int depth; - /// Root window. - Window root; - /// Height of root window. - int root_height; - /// Width of root window. - int root_width; - // Damage of root window. - // Damage root_damage; - /// X Composite overlay window. Used if --paint-on-overlay. - Window overlay; - /// Picture of the root window background. - Picture root_tile; - /// A region of the size of the screen. - XserverRegion screen_reg; - /// Picture of root window. Destination of painting in no-DBE painting - /// mode. - Picture root_picture; - /// A Picture acting as the painting target. - Picture tgt_picture; - /// Temporary buffer to paint to before sending to display. - Picture tgt_buffer; - /// DBE back buffer for root window. Used in DBE painting mode. - XdbeBackBuffer root_dbe; - /// Window ID of the window we register as a symbol. - Window reg_win; - - // === Operation related === - /// Program options. - options_t o; - /// File descriptors to check for reading. - fd_set *pfds_read; - /// File descriptors to check for writing. - fd_set *pfds_write; - /// File descriptors to check for exceptions. - fd_set *pfds_except; - /// Largest file descriptor in fd_set-s above. - int nfds_max; - /// Linked list of all timeouts. - struct _timeout_t *tmout_lst; - /// Whether we have received an event in this cycle. - bool ev_received; - /// Whether the program is idling. I.e. no fading, no potential window - /// changes. - bool idling; - /// Program start time. - struct timeval time_start; - /// The region needs to painted on next paint. - XserverRegion all_damage; - /// Whether all windows are currently redirected. - bool redirected; - /// Whether there's a highest full-screen window, and all windows could - /// be unredirected. - bool unredir_possible; - /// Pre-generated alpha pictures. - Picture *alpha_picts; - /// Whether all reg_ignore of windows should expire in this paint. - bool reg_ignore_expire; - /// Time of last fading. In milliseconds. - time_ms_t fade_time; - /// Head pointer of the error ignore linked list. - ignore_t *ignore_head; - /// Pointer to the next member of tail element of the error - /// ignore linked list. - ignore_t **ignore_tail; - /// Reset program after next paint. - bool reset; - - // === Expose event related === - /// Pointer to an array of XRectangle-s of exposed region. - XRectangle *expose_rects; - /// Number of XRectangle-s in expose_rects. - int size_expose; - /// Index of the next free slot in expose_rects. - int n_expose; - - // === Window related === - /// Linked list of all windows. - struct _win *list; - /// Pointer to win of current active window. Used by - /// EWMH _NET_ACTIVE_WINDOW focus detection. In theory, - /// it's more reliable to store the window ID directly here, just in - /// case the WM does something extraordinary, but caching the pointer - /// means another layer of complexity. - struct _win *active_win; - /// Window ID of leader window of currently active window. Used for - /// subsidiary window detection. - Window active_leader; - - // === Shadow/dimming related === - /// 1x1 black Picture. - Picture black_picture; - /// 1x1 Picture of the shadow color. - Picture cshadow_picture; - /// 1x1 white Picture. - Picture white_picture; - /// Gaussian map of shadow. - conv *gaussian_map; - // for shadow precomputation - /// Shadow depth on one side. - int cgsize; - /// Pre-computed color table for corners of shadow. - unsigned char *shadow_corner; - /// Pre-computed color table for a side of shadow. - unsigned char *shadow_top; - - // === Software-optimization-related === - /// Currently used refresh rate. - short refresh_rate; - /// Interval between refresh in nanoseconds. - long refresh_intv; - /// Nanosecond offset of the first painting. - long paint_tm_offset; - - #ifdef CONFIG_VSYNC_DRM - // === DRM VSync related === - /// File descriptor of DRI device file. Used for DRM VSync. - int drm_fd; - #endif - - #ifdef CONFIG_VSYNC_OPENGL - // === OpenGL VSync related === - /// GLX context. - GLXContext glx_context; - /// Pointer to glXGetVideoSyncSGI function. - f_GetVideoSync glx_get_video_sync; - /// Pointer to glXWaitVideoSyncSGI function. - f_WaitVideoSync glx_wait_video_sync; - #endif - - // === X extension related === - /// Event base number for X Fixes extension. - int xfixes_event; - /// Error base number for X Fixes extension. - int xfixes_error; - /// Event base number for X Damage extension. - int damage_event; - /// Error base number for X Damage extension. - int damage_error; - /// Event base number for X Render extension. - int render_event; - /// Error base number for X Render extension. - int render_error; - /// Event base number for X Composite extension. - int composite_event; - /// Error base number for X Composite extension. - int composite_error; - /// Major opcode for X Composite extension. - int composite_opcode; - /// Whether X Composite NameWindowPixmap is available. Aka if X - /// Composite version >= 0.2. - bool has_name_pixmap; - /// Whether X Shape extension exists. - bool shape_exists; - /// Event base number for X Shape extension. - int shape_event; - /// Error base number for X Shape extension. - int shape_error; - /// Whether X RandR extension exists. - bool randr_exists; - /// Event base number for X RandR extension. - int randr_event; - /// Error base number for X RandR extension. - int randr_error; - #ifdef CONFIG_VSYNC_OPENGL - /// Whether X GLX extension exists. - bool glx_exists; - /// Event base number for X GLX extension. - int glx_event; - /// Error base number for X GLX extension. - int glx_error; - #endif - /// Whether X DBE extension exists. - bool dbe_exists; - /// Whether X Render convolution filter exists. - bool xrfilter_convolution_exists; - - // === Atoms === - /// Atom of property _NET_WM_OPACITY. - Atom atom_opacity; - /// Atom of _NET_FRAME_EXTENTS. - Atom atom_frame_extents; - /// Property atom to identify top-level frame window. Currently - /// WM_STATE. - Atom atom_client; - /// Atom of property WM_NAME. - Atom atom_name; - /// Atom of property _NET_WM_NAME. - Atom atom_name_ewmh; - /// Atom of property WM_CLASS. - Atom atom_class; - /// Atom of property WM_WINDOW_ROLE. - Atom atom_role; - /// Atom of property WM_TRANSIENT_FOR. - Atom atom_transient; - /// Atom of property WM_CLIENT_LEADER. - Atom atom_client_leader; - /// Atom of property _NET_ACTIVE_WINDOW. - Atom atom_ewmh_active_win; - /// Atom of property _COMPTON_SHADOW. - Atom atom_compton_shadow; - /// Atom of property _NET_WM_WINDOW_TYPE. - Atom atom_win_type; - /// Array of atoms of all possible window types. - Atom atoms_wintypes[NUM_WINTYPES]; -} session_t; - -/// Structure representing a top-level window compton manages. -typedef struct _win { - /// Pointer to the next structure in the linked list. - struct _win *next; - /// Pointer to the next higher window to paint. - struct _win *prev_trans; - - // Core members - /// ID of the top-level frame window. - Window id; - /// Window attributes. - XWindowAttributes a; - /// Window painting mode. - winmode_t mode; - /// Whether the window has been damaged at least once. - bool damaged; - /// Damage of the window. - Damage damage; - /// NameWindowPixmap of the window. - Pixmap pixmap; - /// Picture of the window. - Picture picture; - /// Bounding shape of the window. - XserverRegion border_size; - /// Region of the whole window, shadow region included. - XserverRegion extents; - /// Window flags. Definitions above. - int_fast16_t flags; - /// Whether there's a pending ConfigureNotify happening - /// when the window is unmapped. - bool need_configure; - /// Queued ConfigureNotify when the window is unmapped. - XConfigureEvent queue_configure; - /// Region to be ignored when painting. Basically the region where - /// higher opaque windows will paint upon. Depends on window frame - /// opacity state, window geometry, window mapped/unmapped state, - /// window mode, of this and all higher windows. - XserverRegion reg_ignore; - /// Cached width/height of the window including border. - int widthb, heightb; - /// Whether the window has been destroyed. - bool destroyed; - /// Whether the window is bounding-shaped. - bool bounding_shaped; - /// Whether the window just have rounded corners. - bool rounded_corners; - /// Whether this window is to be painted. - bool to_paint; - /// Whether this window is in open/close state. - bool in_openclose; - - // Client window related members - /// ID of the top-level client window of the window. - Window client_win; - /// Type of the window. - wintype_t window_type; - /// Whether it looks like a WM window. We consider a window WM window if - /// it does not have a decedent with WM_STATE and it is not override- - /// redirected itself. - bool wmwin; - /// Leader window ID of the window. - Window leader; - /// Cached topmost window ID of the window. - Window cache_leader; - - // Focus-related members - /// Whether the window is to be considered focused. - bool focused; - /// Override value of window focus state. Set by D-Bus method calls. - switch_t focused_force; - /// Whether the window is actually focused. - bool focused_real; - - // Blacklist related members - /// Name of the window. - char *name; - /// Window instance class of the window. - char *class_instance; - /// Window general class of the window. - char *class_general; - /// WM_WINDOW_ROLE value of the window. - char *role; - wincond_t *cache_sblst; - wincond_t *cache_fblst; - wincond_t *cache_fcblst; - wincond_t *cache_ivclst; - - // Opacity-related members - /// Current window opacity. - opacity_t opacity; - /// Target window opacity. - opacity_t opacity_tgt; - /// Cached value of opacity window attribute. - opacity_t opacity_prop; - /// Cached value of opacity window attribute on client window. For - /// broken window managers not transferring client window's - /// _NET_WM_OPACITY value - opacity_t opacity_prop_client; - /// Alpha mask Picture to render window with opacity. - Picture alpha_pict; - - // Fading-related members - /// Do not fade if it's false. Change on window type change. - /// Used by fading blacklist in the future. - bool fade; - /// Callback to be called after fading completed. - void (*fade_callback) (session_t *ps, struct _win *w); - - // Frame-opacity-related members - /// Current window frame opacity. Affected by window opacity. - double frame_opacity; - /// Alpha mask Picture to render window frame with opacity. - Picture frame_alpha_pict; - /// Frame widths. Determined by client window attributes. - unsigned int left_width, right_width, top_width, bottom_width; - - // Shadow-related members - /// Whether a window has shadow. Calculated. - bool shadow; - /// Override value of window shadow state. Set by D-Bus method calls. - switch_t shadow_force; - /// Opacity of the shadow. Affected by window opacity and frame opacity. - double shadow_opacity; - /// X offset of shadow. Affected by commandline argument. - int shadow_dx; - /// Y offset of shadow. Affected by commandline argument. - int shadow_dy; - /// Width of shadow. Affected by window size and commandline argument. - int shadow_width; - /// Height of shadow. Affected by window size and commandline argument. - int shadow_height; - /// Picture to render shadow. Affected by window size. - Picture shadow_pict; - /// Alpha mask Picture to render shadow. Affected by shadow opacity. - Picture shadow_alpha_pict; - /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for - /// none. - long prop_shadow; - - // Dim-related members - /// Whether the window is to be dimmed. - bool dim; - /// Picture for dimming. Affected by user-specified inactive dim - /// opacity and window opacity. - Picture dim_alpha_pict; - - /// Whether to invert window color. - bool invert_color; - /// Override value of window color inversion state. Set by D-Bus method - /// calls. - switch_t invert_color_force; -} win; - -/// Temporary structure used for communication between -/// get_cfg() and parse_config(). -struct options_tmp { - bool no_dock_shadow; - bool no_dnd_shadow; - double menu_opacity; -}; - -/// Structure for a recorded timeout. -typedef struct _timeout_t { - bool enabled; - void *data; - bool (*callback)(session_t *ps, struct _timeout_t *ptmout); - time_ms_t interval; - time_ms_t firstrun; - time_ms_t lastrun; - struct _timeout_t *next; -} timeout_t; - -/// Enumeration for window event hints. -typedef enum { - WIN_EVMODE_UNKNOWN, - WIN_EVMODE_FRAME, - WIN_EVMODE_CLIENT -} win_evmode_t; - -extern const char *WINTYPES[NUM_WINTYPES]; -extern session_t *ps_g; - -// == Debugging code == -static void -print_timestamp(session_t *ps); - -#ifdef DEBUG_ALLOC_REG - -#include -#define BACKTRACE_SIZE 5 - -/** - * Print current backtrace, excluding the first two items. - * - * Stolen from glibc manual. - */ -static inline void -print_backtrace(void) { - void *array[BACKTRACE_SIZE]; - size_t size; - char **strings; - - size = backtrace(array, BACKTRACE_SIZE); - strings = backtrace_symbols(array, size); - - for (size_t i = 2; i < size; i++) - printf ("%s\n", strings[i]); - - free(strings); -} - -/** - * Wrapper of XFixesCreateRegion, for debugging. - */ -static inline XserverRegion -XFixesCreateRegion_(Display *dpy, XRectangle *p, int n, - const char *func, int line) { - XserverRegion reg = XFixesCreateRegion(dpy, p, n); - print_timestamp(ps_g); - printf("%#010lx: XFixesCreateRegion() in %s():%d\n", reg, func, line); - print_backtrace(); - fflush(stdout); - return reg; -} - -/** - * Wrapper of XFixesDestroyRegion, for debugging. - */ -static inline void -XFixesDestroyRegion_(Display *dpy, XserverRegion reg, - const char *func, int line) { - XFixesDestroyRegion(dpy, reg); - print_timestamp(ps_g); - printf("%#010lx: XFixesDestroyRegion() in %s():%d\n", reg, func, line); - fflush(stdout); -} - -#define XFixesCreateRegion(dpy, p, n) XFixesCreateRegion_(dpy, p, n, __func__, __LINE__) -#define XFixesDestroyRegion(dpy, reg) XFixesDestroyRegion_(dpy, reg, __func__, __LINE__) -#endif - // == Functions == // inline functions must be made static to compile correctly under clang: @@ -952,111 +110,6 @@ wintype_arr_enable_unset(switch_t arr[]) { arr[i] = ON; } -/** - * Allocate the space and copy a string. - */ -static inline char * __attribute__((const)) -mstrcpy(const char *src) { - char *str = malloc(sizeof(char) * (strlen(src) + 1)); - - strcpy(str, src); - - return str; -} - -/** - * Allocate the space and join two strings. - */ -static inline char * __attribute__((const)) -mstrjoin(const char *src1, const char *src2) { - char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); - - strcpy(str, src1); - strcat(str, src2); - - return str; -} - -/** - * Allocate the space and join two strings; - */ -static inline char * __attribute__((const)) -mstrjoin3(const char *src1, const char *src2, const char *src3) { - char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) - + strlen(src3) + 1)); - - strcpy(str, src1); - strcat(str, src2); - strcat(str, src3); - - return str; -} - -/** - * Normalize an int value to a specific range. - * - * @param i int value to normalize - * @param min minimal value - * @param max maximum value - * @return normalized value - */ -static inline int __attribute__((const)) -normalize_i_range(int i, int min, int max) { - if (i > max) return max; - if (i < min) return min; - return i; -} - -/** - * Select the larger integer of two. - */ -static inline int __attribute__((const)) -max_i(int a, int b) { - return (a > b ? a : b); -} - -/** - * Select the smaller integer of two. - */ -static inline int __attribute__((const)) -min_i(int a, int b) { - return (a > b ? b : a); -} - -/** - * Select the smaller long integer of two. - */ -static inline long __attribute__((const)) -min_l(long a, long b) { - return (a > b ? b : a); -} - -/** - * Normalize a double value to a specific range. - * - * @param d double value to normalize - * @param min minimal value - * @param max maximum value - * @return normalized value - */ -static inline double __attribute__((const)) -normalize_d_range(double d, double min, double max) { - if (d > max) return max; - if (d < min) return min; - return d; -} - -/** - * Normalize a double value to 0.\ 0 - 1.\ 0. - * - * @param d double value to normalize - * @return normalized value - */ -static inline double __attribute__((const)) -normalize_d(double d) { - return normalize_d_range(d, 0.0, 1.0); -} - /** * Check if a window ID exists in an array of window IDs. * @@ -1074,160 +127,6 @@ array_wid_exists(const Window *arr, int count, Window wid) { return false; } - -/** - * Return whether a struct timeval value is empty. - */ -static inline bool -timeval_isempty(struct timeval *ptv) { - if (!ptv) - return false; - - return ptv->tv_sec <= 0 && ptv->tv_usec <= 0; -} - -/** - * Compare a struct timeval with a time in milliseconds. - * - * @return > 0 if ptv > ms, 0 if ptv == 0, -1 if ptv < ms - */ -static inline int -timeval_ms_cmp(struct timeval *ptv, time_ms_t ms) { - assert(ptv); - - // We use those if statement instead of a - expression because of possible - // truncation problem from long to int. - { - long sec = ms / MS_PER_SEC; - if (ptv->tv_sec > sec) - return 1; - if (ptv->tv_sec < sec) - return -1; - } - - { - long usec = ms % MS_PER_SEC * (US_PER_SEC / MS_PER_SEC); - if (ptv->tv_usec > usec) - return 1; - if (ptv->tv_usec < usec) - return -1; - } - - return 0; -} - -/* - * Subtracting two struct timeval values. - * - * Taken from glibc manual. - * - * Subtract the `struct timeval' values X and Y, - * storing the result in RESULT. - * Return 1 if the difference is negative, otherwise 0. */ -static int -timeval_subtract(struct timeval *result, - struct timeval *x, - struct timeval *y) { - /* Perform the carry for the later subtraction by updating y. */ - if (x->tv_usec < y->tv_usec) { - long nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; - y->tv_usec -= 1000000 * nsec; - y->tv_sec += nsec; - } - - if (x->tv_usec - y->tv_usec > 1000000) { - long nsec = (x->tv_usec - y->tv_usec) / 1000000; - y->tv_usec += 1000000 * nsec; - y->tv_sec -= nsec; - } - - /* Compute the time remaining to wait. - tv_usec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_usec = x->tv_usec - y->tv_usec; - - /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; -} - -/* - * Subtracting two struct timespec values. - * - * Taken from glibc manual. - * - * Subtract the `struct timespec' values X and Y, - * storing the result in RESULT. - * Return 1 if the difference is negative, otherwise 0. - */ -static inline int -timespec_subtract(struct timespec *result, - struct timespec *x, - struct timespec *y) { - /* Perform the carry for the later subtraction by updating y. */ - if (x->tv_nsec < y->tv_nsec) { - long nsec = (y->tv_nsec - x->tv_nsec) / NS_PER_SEC + 1; - y->tv_nsec -= NS_PER_SEC * nsec; - y->tv_sec += nsec; - } - - if (x->tv_nsec - y->tv_nsec > NS_PER_SEC) { - long nsec = (x->tv_nsec - y->tv_nsec) / NS_PER_SEC; - y->tv_nsec += NS_PER_SEC * nsec; - y->tv_sec -= nsec; - } - - /* Compute the time remaining to wait. - tv_nsec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_nsec = x->tv_nsec - y->tv_nsec; - - /* Return 1 if result is negative. */ - return x->tv_sec < y->tv_sec; -} - -/** - * Get current time in struct timeval. - */ -static inline struct timeval __attribute__((const)) -get_time_timeval(void) { - struct timeval tv = { 0, 0 }; - - gettimeofday(&tv, NULL); - - // Return a time of all 0 if the call fails - return tv; -} - -/** - * Get current time in struct timespec. - * - * Note its starting time is unspecified. - */ -static inline struct timespec __attribute__((const)) -get_time_timespec(void) { - struct timespec tm = { 0, 0 }; - - clock_gettime(CLOCK_MONOTONIC, &tm); - - // Return a time of all 0 if the call fails - return tm; -} - -/** - * Print time passed since program starts execution. - * - * Used for debugging. - */ -static void -print_timestamp(session_t *ps) { - struct timeval tm, diff; - - if (gettimeofday(&tm, NULL)) return; - - timeval_subtract(&diff, &tm, &ps->time_start); - printf("[ %5ld.%02ld ] ", diff.tv_sec, diff.tv_usec / 10000); -} - /** * Destroy a XserverRegion. */ @@ -1347,95 +246,6 @@ ms_to_tv(int timeout) { }; } -/** - * Add a file descriptor to a select() fd_set. - */ -static inline bool -fds_insert_select(fd_set **ppfds, int fd) { - assert(fd <= FD_SETSIZE); - - if (!*ppfds) { - if ((*ppfds = malloc(sizeof(fd_set)))) { - FD_ZERO(*ppfds); - } - else { - fprintf(stderr, "Failed to allocate memory for select() fdset.\n"); - exit(1); - } - } - - FD_SET(fd, *ppfds); - - return true; -} - -/** - * Add a new file descriptor to wait for. - */ -static inline bool -fds_insert(session_t *ps, int fd, short events) { - bool result = true; - - ps->nfds_max = max_i(fd + 1, ps->nfds_max); - - if (POLLIN & events) - result = fds_insert_select(&ps->pfds_read, fd) && result; - if (POLLOUT & events) - result = fds_insert_select(&ps->pfds_write, fd) && result; - if (POLLPRI & events) - result = fds_insert_select(&ps->pfds_except, fd) && result; - - return result; -} - -/** - * Delete a file descriptor to wait for. - */ -static inline void -fds_drop(session_t *ps, int fd, short events) { - // Drop fd from respective fd_set-s - if (POLLIN & events) - FD_CLR(fd, ps->pfds_read); - if (POLLOUT & events) - FD_CLR(fd, ps->pfds_write); - if (POLLPRI & events) - FD_CLR(fd, ps->pfds_except); -} - -#define CPY_FDS(key) \ - fd_set * key = NULL; \ - if (ps->key) { \ - key = malloc(sizeof(fd_set)); \ - memcpy(key, ps->key, sizeof(fd_set)); \ - if (!key) { \ - fprintf(stderr, "Failed to allocate memory for copying select() fdset.\n"); \ - exit(1); \ - } \ - } \ - -/** - * Poll for changes. - * - * poll() is much better than select(), but ppoll() does not exist on - * *BSD. - */ -static inline int -fds_poll(session_t *ps, struct timeval *ptv) { - // Copy fds - CPY_FDS(pfds_read); - CPY_FDS(pfds_write); - CPY_FDS(pfds_except); - - int ret = select(ps->nfds_max, pfds_read, pfds_write, pfds_except, ptv); - - free(pfds_read); - free(pfds_write); - free(pfds_except); - - return ret; -} -#undef CPY_FDS - static void run_fade(session_t *ps, win *w, unsigned steps); @@ -1722,43 +532,6 @@ condlst_add(wincond_t **pcondlst, const char *pattern); static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); -/** - * Find a window from window id in window linked list of the session. - */ -static inline win * -find_win(session_t *ps, Window id) { - if (!id) - return NULL; - - win *w; - - for (w = ps->list; w; w = w->next) { - if (w->id == id && !w->destroyed) - return w; - } - - return 0; -} - -/** - * Find out the WM frame of a client window using existing data. - * - * @param w window ID - * @return struct _win object of the found window, NULL if not found - */ -static inline win * -find_toplevel(session_t *ps, Window id) { - if (!id) - return NULL; - - for (win *w = ps->list; w; w = w->next) { - if (w->client_win == id && !w->destroyed) - return w; - } - - return NULL; -} - /** * Clear leader cache of all windows. */ @@ -2112,18 +885,6 @@ get_screen_region(session_t *ps) { return XFixesCreateRegion(ps->dpy, &r, 1); } -/** - * Copies a region - */ -inline static XserverRegion -copy_region(const session_t *ps, XserverRegion oldregion) { - XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); - - XFixesCopyRegion(ps->dpy, region, oldregion); - - return region; -} - /** * Dump a region. */ @@ -2281,19 +1042,14 @@ redir_start(session_t *ps); static void redir_stop(session_t *ps); +static inline time_ms_t +timeout_get_newrun(const timeout_t *ptmout) { + return ptmout->firstrun + (max_l((ptmout->lastrun + (long) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + ptmout->interval * (1 - TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval) + 1) * ptmout->interval; +} + static time_ms_t timeout_get_poll_time(session_t *ps); -static timeout_t * -timeout_insert(session_t *ps, time_ms_t interval, - bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); - -static void -timeout_invoke(session_t *ps, timeout_t *ptmout); - -static bool -timeout_drop(session_t *ps, timeout_t *prm); - static void timeout_clear(session_t *ps); diff --git a/dbus.c b/dbus.c new file mode 100644 index 000000000..16be7cc8f --- /dev/null +++ b/dbus.c @@ -0,0 +1,920 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "dbus.h" + +/** + * Initialize D-Bus connection. + */ +bool +cdbus_init(session_t *ps) { + DBusError err = { }; + + // Initialize + dbus_error_init(&err); + + // Connect to D-Bus + // Use dbus_bus_get_private() so we can fully recycle it ourselves + ps->dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &err); + if (dbus_error_is_set(&err)) { + printf_errf("(): D-Bus connection failed (%s).", err.message); + dbus_error_free(&err); + return false; + } + + if (!ps->dbus_conn) { + printf_errf("(): D-Bus connection failed for unknown reason."); + return false; + } + + // Avoid exiting on disconnect + dbus_connection_set_exit_on_disconnect(ps->dbus_conn, false); + + // Request service name + { + // Get display name + char *display = DisplayString(ps->dpy); + if (!display) + display = "unknown"; + display = mstrcpy(display); + + // Convert all special characters in display name to underscore + { + char *pdisp = display; + + while (*pdisp) { + if (!isalnum(*pdisp)) + *pdisp = '_'; + ++pdisp; + } + } + + // Build service name + char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", display); + ps->dbus_service = service; + + free(display); + display = NULL; + + // Request for the name + int ret = dbus_bus_request_name(ps->dbus_conn, service, + DBUS_NAME_FLAG_DO_NOT_QUEUE, &err); + + if (dbus_error_is_set(&err)) { + printf_errf("(): Failed to obtain D-Bus name (%s).", err.message); + dbus_error_free(&err); + } + + if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret + && DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER != ret) { + printf_errf("(): Failed to become the primary owner of requested " + "D-Bus name (%d).", ret); + } + } + + + // Add watch handlers + if (!dbus_connection_set_watch_functions(ps->dbus_conn, + cdbus_callback_add_watch, cdbus_callback_remove_watch, + cdbus_callback_watch_toggled, ps, NULL)) { + printf_errf("(): Failed to add D-Bus watch functions."); + return false; + } + + // Add timeout handlers + if (!dbus_connection_set_timeout_functions(ps->dbus_conn, + cdbus_callback_add_timeout, cdbus_callback_remove_timeout, + cdbus_callback_timeout_toggled, ps, NULL)) { + printf_errf("(): Failed to add D-Bus timeout functions."); + return false; + } + + // Add match + dbus_bus_add_match(ps->dbus_conn, + "type='method_call',interface='" CDBUS_INTERFACE_NAME "'", &err); + if (dbus_error_is_set(&err)) { + printf_errf("(): Failed to add D-Bus match."); + dbus_error_free(&err); + return false; + } + + return true; +} + +/** + * Destroy D-Bus connection. + */ +void +cdbus_destroy(session_t *ps) { + if (ps->dbus_conn) { + // Release DBus name firstly + if (ps->dbus_service) { + DBusError err = { }; + dbus_error_init(&err); + + dbus_bus_release_name(ps->dbus_conn, ps->dbus_service, &err); + if (dbus_error_is_set(&err)) { + printf_errf("(): Failed to release DBus name (%s).", + err.message); + dbus_error_free(&err); + } + } + + // Close and unref the connection + dbus_connection_close(ps->dbus_conn); + dbus_connection_unref(ps->dbus_conn); + } +} + +/** @name DBusTimeout handling + */ +///@{ + +/** + * Callback for adding D-Bus timeout. + */ +static dbus_bool_t +cdbus_callback_add_timeout(DBusTimeout *timeout, void *data) { + session_t *ps = data; + + timeout_t *ptmout = timeout_insert(ps, dbus_timeout_get_interval(timeout), + cdbus_callback_handle_timeout, timeout); + if (ptmout) + dbus_timeout_set_data(timeout, ptmout, NULL); + + return (bool) ptmout; +} + +/** + * Callback for removing D-Bus timeout. + */ +static void +cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data) { + session_t *ps = data; + + timeout_t *ptmout = dbus_timeout_get_data(timeout); + assert(ptmout); + if (ptmout) + timeout_drop(ps, ptmout); +} + +/** + * Callback for toggling a D-Bus timeout. + */ +static void +cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data) { + timeout_t *ptmout = dbus_timeout_get_data(timeout); + + assert(ptmout); + if (ptmout) { + ptmout->enabled = dbus_timeout_get_enabled(timeout); + // Refresh interval as libdbus doc says: "Whenever a timeout is toggled, + // its interval may change." + ptmout->interval = dbus_timeout_get_interval(timeout); + } +} + +/** + * Callback for handling a D-Bus timeout. + */ +static bool +cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout) { + assert(ptmout && ptmout->data); + if (ptmout && ptmout->data) + return dbus_timeout_handle(ptmout->data); + + return false; +} + +///@} + +/** @name DBusWatch handling + */ +///@{ + +/** + * Callback for adding D-Bus watch. + */ +static dbus_bool_t +cdbus_callback_add_watch(DBusWatch *watch, void *data) { + // Leave disabled watches alone + if (!dbus_watch_get_enabled(watch)) + return TRUE; + + session_t *ps = data; + + // Insert the file descriptor + fds_insert(ps, dbus_watch_get_unix_fd(watch), + cdbus_get_watch_cond(watch)); + + // Always return true + return TRUE; +} + +/** + * Callback for removing D-Bus watch. + */ +static void +cdbus_callback_remove_watch(DBusWatch *watch, void *data) { + session_t *ps = data; + + fds_drop(ps, dbus_watch_get_unix_fd(watch), + cdbus_get_watch_cond(watch)); +} + +/** + * Callback for toggling D-Bus watch status. + */ +static void +cdbus_callback_watch_toggled(DBusWatch *watch, void *data) { + if (dbus_watch_get_enabled(watch)) { + cdbus_callback_add_watch(watch, data); + } + else { + cdbus_callback_remove_watch(watch, data); + } +} + +///@} + +/** @name Message argument appending callbacks + */ +///@{ + +/** + * Callback to append a bool argument to a message. + */ +static bool +cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data) { + assert(data); + + dbus_bool_t val = *(const bool *) data; + + if (!dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &val, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append a Window argument to a message. + */ +static bool +cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data) { + assert(data); + cdbus_window_t val = *(const Window *)data; + + if (!dbus_message_append_args(msg, CDBUS_TYPE_WINDOW, &val, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append an cdbus_enum_t argument to a message. + */ +static bool +cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data) { + assert(data); + if (!dbus_message_append_args(msg, CDBUS_TYPE_ENUM, data, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append a string argument to a message. + */ +static bool +cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data) { + const char *str = data; + if (!str) + str = ""; + + if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append all window IDs to a message. + */ +static bool +cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data) { + // Get the number of wids we are to include + unsigned count = 0; + for (win *w = ps->list; w; w = w->next) { + if (!w->destroyed) + ++count; + } + + // Allocate memory for an array of window IDs + cdbus_window_t *arr = malloc(sizeof(cdbus_window_t) * count); + if (!arr) { + printf_errf("(): Failed to allocate memory for window ID array."); + return false; + } + + // Build the array + { + cdbus_window_t *pcur = arr; + for (win *w = ps->list; w; w = w->next) { + if (!w->destroyed) { + *pcur = w->id; + ++pcur; + assert(pcur <= arr + count); + } + } + assert(pcur == arr + count); + } + + // Append arguments + if (!dbus_message_append_args(msg, DBUS_TYPE_ARRAY, CDBUS_TYPE_WINDOW, + &arr, count, DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + free(arr); + return false; + } + + free(arr); + return true; +} +///@} + +/** + * Send a D-Bus signal. + * + * @param ps current session + * @param name signal name + * @param func a function that modifies the built message, to, for example, + * add an argument + * @param data data pointer to pass to the function + */ +static bool +cdbus_signal(session_t *ps, const char *name, + bool (*func)(session_t *ps, DBusMessage *msg, const void *data), + const void *data) { + DBusMessage* msg = NULL; + + // Create a signal + msg = dbus_message_new_signal(CDBUS_OBJECT_NAME, CDBUS_INTERFACE_NAME, + name); + if (!msg) { + printf_errf("(): Failed to create D-Bus signal."); + return false; + } + + // Append arguments onto message + if (func && !func(ps, msg, data)) { + dbus_message_unref(msg); + return false; + } + + // Send the message and flush the connection + if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { + printf_errf("(): Failed to send D-Bus signal."); + dbus_message_unref(msg); + return false; + } + dbus_connection_flush(ps->dbus_conn); + + // Free the message + dbus_message_unref(msg); + + return true; +} + +/** + * Send a D-Bus reply. + * + * @param ps current session + * @param srcmsg original message + * @param func a function that modifies the built message, to, for example, + * add an argument + * @param data data pointer to pass to the function + */ +static bool +cdbus_reply(session_t *ps, DBusMessage *srcmsg, + bool (*func)(session_t *ps, DBusMessage *msg, const void *data), + const void *data) { + DBusMessage* msg = NULL; + + // Create a reply + msg = dbus_message_new_method_return(srcmsg); + if (!msg) { + printf_errf("(): Failed to create D-Bus reply."); + return false; + } + + // Append arguments onto message + if (func && !func(ps, msg, data)) { + dbus_message_unref(msg); + return false; + } + + // Send the message and flush the connection + if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { + printf_errf("(): Failed to send D-Bus reply."); + dbus_message_unref(msg); + return false; + } + dbus_connection_flush(ps->dbus_conn); + + // Free the message + dbus_message_unref(msg); + + return true; +} + +/** + * Send a D-Bus error reply. + * + * @param ps current session + * @param msg the new error DBusMessage + */ +static bool +cdbus_reply_errm(session_t *ps, DBusMessage *msg) { + if (!msg) { + printf_errf("(): Failed to create D-Bus reply."); + return false; + } + + // Send the message and flush the connection + if (!dbus_connection_send(ps->dbus_conn, msg, NULL)) { + printf_errf("(): Failed to send D-Bus reply."); + dbus_message_unref(msg); + return false; + } + dbus_connection_flush(ps->dbus_conn); + + // Free the message + dbus_message_unref(msg); + + return true; +} + +/** + * Get n-th argument of a D-Bus message. + * + * @param count the position of the argument to get, starting from 0 + * @param type libdbus type number of the type + * @param pdest pointer to the target + * @return true if successful, false otherwise. + */ +static bool +cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest) { + assert(count >= 0); + + DBusMessageIter iter = { }; + if (!dbus_message_iter_init(msg, &iter)) { + printf_errf("(): Message has no argument."); + return false; + } + + { + const int oldcount = count; + while (count) { + if (!dbus_message_iter_next(&iter)) { + printf_errf("(): Failed to find argument %d.", oldcount); + return false; + } + --count; + } + } + + if (type != dbus_message_iter_get_arg_type(&iter)) { + printf_errf("(): Argument has incorrect type."); + return false; + } + + dbus_message_iter_get_basic(&iter, pdest); + + return true; +} + +void +cdbus_loop(session_t *ps) { + dbus_connection_read_write(ps->dbus_conn, 0); + DBusMessage *msg = NULL; + while ((msg = dbus_connection_pop_message(ps->dbus_conn))) + cdbus_process(ps, msg); +} + +/** @name Message processing + */ +///@{ + +/** + * Process a message from D-Bus. + */ +static void +cdbus_process(session_t *ps, DBusMessage *msg) { + bool success = false; + +#define cdbus_m_ismethod(method) \ + dbus_message_is_method_call(msg, CDBUS_INTERFACE_NAME, method) + + if (cdbus_m_ismethod("reset")) { + ps->reset = true; + if (!dbus_message_get_no_reply(msg)) + cdbus_reply_bool(ps, msg, true); + success = true; + } + else if (cdbus_m_ismethod("list_win")) { + success = cdbus_process_list_win(ps, msg); + } + else if (cdbus_m_ismethod("win_get")) { + success = cdbus_process_win_get(ps, msg); + } + else if (cdbus_m_ismethod("win_set")) { + success = cdbus_process_win_set(ps, msg); + } + else if (cdbus_m_ismethod("find_win")) { + success = cdbus_process_find_win(ps, msg); + } + else if (cdbus_m_ismethod("opts_get")) { + success = cdbus_process_opts_get(ps, msg); + } + else if (cdbus_m_ismethod("opts_set")) { + success = cdbus_process_opts_set(ps, msg); + } +#undef cdbus_m_ismethod + else if (dbus_message_is_method_call(msg, + "org.freedesktop.DBus.Introspectable", "Introspect")) { + success = cdbus_process_introspect(ps, msg); + } + else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") + || dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) { + success = true; + } + else { + if (DBUS_MESSAGE_TYPE_ERROR == dbus_message_get_type(msg)) { + printf_errf("(): Error message of path \"%s\" " + "interface \"%s\", member \"%s\", error \"%s\"", + dbus_message_get_path(msg), dbus_message_get_interface(msg), + dbus_message_get_member(msg), dbus_message_get_error_name(msg)); + } + else { + printf_errf("(): Illegal message of type \"%s\", path \"%s\" " + "interface \"%s\", member \"%s\"", + cdbus_repr_msgtype(msg), dbus_message_get_path(msg), + dbus_message_get_interface(msg), dbus_message_get_member(msg)); + } + if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) + && !dbus_message_get_no_reply(msg)) + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S); + success = true; + } + + // If the message could not be processed, and an reply is expected, return + // an empty reply. + if (!success && DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) + && !dbus_message_get_no_reply(msg)) + cdbus_reply_err(ps, msg, CDBUS_ERROR_UNKNOWN, CDBUS_ERROR_UNKNOWN_S); + + // Free the message + dbus_message_unref(msg); +} + +/** + * Process a list_win D-Bus request. + */ +static bool +cdbus_process_list_win(session_t *ps, DBusMessage *msg) { + cdbus_reply(ps, msg, cdbus_apdarg_wids, NULL); + + return true; +} + +/** + * Process a win_get D-Bus request. + */ +static bool +cdbus_process_win_get(session_t *ps, DBusMessage *msg) { + cdbus_window_t wid = None; + const char *target = NULL; + DBusError err = { }; + + if (!dbus_message_get_args(msg, &err, + CDBUS_TYPE_WINDOW, &wid, + DBUS_TYPE_STRING, &target, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to parse argument of \"win_get\" (%s).", + err.message); + dbus_error_free(&err); + return false; + } + + win *w = find_win(ps, wid); + + if (!w) { + printf_errf("(): Window %#010x not found.", wid); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); + return true; + } + +#define cdbus_m_win_get_do(tgt, apdarg_func) \ + if (!strcmp(MSTR(tgt), target)) { \ + apdarg_func(ps, msg, w->tgt); \ + return true; \ + } + + cdbus_m_win_get_do(client_win, cdbus_reply_wid); + cdbus_m_win_get_do(damaged, cdbus_reply_bool); + cdbus_m_win_get_do(destroyed, cdbus_reply_bool); + cdbus_m_win_get_do(window_type, cdbus_reply_enum); + cdbus_m_win_get_do(wmwin, cdbus_reply_bool); + cdbus_m_win_get_do(leader, cdbus_reply_wid); + cdbus_m_win_get_do(focused_real, cdbus_reply_bool); + cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); + cdbus_m_win_get_do(focused_force, cdbus_reply_enum); + cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum); + if (!strcmp("map_state", target)) { + cdbus_reply_bool(ps, msg, w->a.map_state); + return true; + } +#undef cdbus_m_win_get_do + + printf_errf("(): No matching target found."); + + return false; +} + +/** + * Process a win_set D-Bus request. + */ +static bool +cdbus_process_win_set(session_t *ps, DBusMessage *msg) { + cdbus_window_t wid = None; + const char *target = NULL; + DBusError err = { }; + + if (!dbus_message_get_args(msg, &err, + CDBUS_TYPE_WINDOW, &wid, + DBUS_TYPE_STRING, &target, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to parse argument of \"win_set\" (%s).", + err.message); + dbus_error_free(&err); + return false; + } + + win *w = find_win(ps, wid); + + if (!w) { + printf_errf("(): Window %#010x not found.", wid); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADWIN, CDBUS_ERROR_BADWIN_S, wid); + return true; + } + + ps->ev_received = true; + +#define cdbus_m_win_set_do(tgt, type, real_type) \ + if (!strcmp(MSTR(tgt), target)) { \ + real_type val; \ + if (!cdbus_msg_get_arg(msg, 2, type, &val)) \ + return false; \ + w->tgt = val; \ + goto cdbus_process_win_set_success; \ + } + + if (!strcmp("shadow_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_shadow_force(ps, w, val); + goto cdbus_process_win_set_success; + } + + if (!strcmp("focused_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_focused_force(ps, w, val); + goto cdbus_process_win_set_success; + } + + if (!strcmp("invert_color_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_invert_color_force(ps, w, val); + goto cdbus_process_win_set_success; + } +#undef cdbus_m_win_set_do + + printf_errf("(): No matching target found."); + + return false; + +cdbus_process_win_set_success: + if (!dbus_message_get_no_reply(msg)) + cdbus_reply_bool(ps, msg, true); + return true; +} + +/** + * Process a find_win D-Bus request. + */ +static bool +cdbus_process_find_win(session_t *ps, DBusMessage *msg) { + const char *target = NULL; + + if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) + return false; + + Window wid = None; + + // Find window by client window + if (!strcmp("client", target)) { + cdbus_window_t client = None; + if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client)) + return false; + win *w = find_toplevel(ps, client); + if (w) + wid = w->id; + } + // Find focused window + else if (!strcmp("focused", target)) { + win *w = find_focused(ps); + if (w) + wid = w->id; + } + else { + printf_errf("(): No matching target found."); + + return false; + } + + cdbus_reply_wid(ps, msg, wid); + + return true; +} + +/** + * Process a opts_get D-Bus request. + */ +static bool +cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { + const char *target = NULL; + + if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) + return false; + +#define cdbus_m_opts_get_do(tgt, apdarg_func) \ + if (!strcmp(MSTR(tgt), target)) { \ + apdarg_func(ps, msg, ps->o.tgt); \ + return true; \ + } + + cdbus_m_opts_get_do(display, cdbus_reply_string); + cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool); + cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool); + cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); + cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); + cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); + cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); + cdbus_m_opts_get_do(logpath, cdbus_reply_string); + cdbus_m_opts_get_do(synchronize, cdbus_reply_bool); + + cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); +#undef cdbus_m_opts_get_do + + printf_errf("(): No matching target found."); + + return false; +} + +/** + * Process a opts_set D-Bus request. + */ +static bool +cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { + const char *target = NULL; + + if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) + return false; + +#define cdbus_m_opts_set_do(tgt, type, real_type) \ + if (!strcmp(MSTR(tgt), target)) { \ + real_type val; \ + if (!cdbus_msg_get_arg(msg, 1, type, &val)) \ + return false; \ + ps->o.tgt = val; \ + goto cdbus_process_opts_set_success; \ + } + + // unredir_if_possible + if (!strcmp("unredir_if_possible", target)) { + dbus_bool_t val = FALSE; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) + return false; + if (ps->o.unredir_if_possible != val) { + ps->o.unredir_if_possible = val; + ps->ev_received = true; + } + goto cdbus_process_opts_set_success; + } + + // clear_shadow + if (!strcmp("clear_shadow", target)) { + dbus_bool_t val = FALSE; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) + return false; + if (ps->o.clear_shadow != val) { + ps->o.clear_shadow = val; + force_repaint(ps); + } + goto cdbus_process_opts_set_success; + } +#undef cdbus_m_opts_set_do + + printf_errf("(): No matching target found."); + + return false; + +cdbus_process_opts_set_success: + if (!dbus_message_get_no_reply(msg)) + cdbus_reply_bool(ps, msg, true); + return true; +} + +/** + * Process an Introspect D-Bus request. + */ +static bool +cdbus_process_introspect(session_t *ps, DBusMessage *msg) { + const static char *str_introspect = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; + + cdbus_reply_string(ps, msg, str_introspect); + + return true; +} +///@} + +/** @name Core callbacks + */ +///@{ +void +cdbus_ev_win_added(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_added", w->id); +} + +void +cdbus_ev_win_destroyed(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_destroyed", w->id); +} + +void +cdbus_ev_win_mapped(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_mapped", w->id); +} + +void +cdbus_ev_win_unmapped(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_unmapped", w->id); +} +//!@} diff --git a/dbus.h b/dbus.h new file mode 100644 index 000000000..fb51c8cc4 --- /dev/null +++ b/dbus.h @@ -0,0 +1,213 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "common.h" +#include + +#define CDBUS_SERVICE_NAME "com.github.chjj.compton" +#define CDBUS_INTERFACE_NAME CDBUS_SERVICE_NAME +#define CDBUS_OBJECT_NAME "/com/github/chjj/compton" +#define CDBUS_ERROR_PREFIX CDBUS_INTERFACE_NAME ".error" +#define CDBUS_ERROR_UNKNOWN CDBUS_ERROR_PREFIX ".unknown" +#define CDBUS_ERROR_UNKNOWN_S "Well, I don't know what happened. Do you?" +#define CDBUS_ERROR_BADMSG CDBUS_ERROR_PREFIX ".bad_message" +#define CDBUS_ERROR_BADMSG_S "Unrecognized command. Beware compton " \ + "cannot make you a sandwich." +#define CDBUS_ERROR_BADARG CDBUS_ERROR_PREFIX ".bad_argument" +#define CDBUS_ERROR_BADARG_S "Something wrong in arguments?" +#define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window" +#define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found." +#define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden" +#define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied." + +// Window type +typedef uint32_t cdbus_window_t; +#define CDBUS_TYPE_WINDOW DBUS_TYPE_UINT32 +#define CDBUS_TYPE_WINDOW_STR DBUS_TYPE_UINT32_AS_STRING + +typedef uint16_t cdbus_enum_t; +#define CDBUS_TYPE_ENUM DBUS_TYPE_UINT16 +#define CDBUS_TYPE_ENUM_STR DBUS_TYPE_UINT16_AS_STRING + +static dbus_bool_t +cdbus_callback_add_timeout(DBusTimeout *timeout, void *data); + +static void +cdbus_callback_remove_timeout(DBusTimeout *timeout, void *data); + +static void +cdbus_callback_timeout_toggled(DBusTimeout *timeout, void *data); + +static bool +cdbus_callback_handle_timeout(session_t *ps, timeout_t *ptmout); + +/** + * Determine the poll condition of a DBusWatch. + */ +static inline short +cdbus_get_watch_cond(DBusWatch *watch) { + const unsigned flags = dbus_watch_get_flags(watch); + short condition = POLLERR | POLLHUP; + if (flags & DBUS_WATCH_READABLE) + condition |= POLLIN; + if (flags & DBUS_WATCH_WRITABLE) + condition |= POLLOUT; + + return condition; +} + +static dbus_bool_t +cdbus_callback_add_watch(DBusWatch *watch, void *data); + +static void +cdbus_callback_remove_watch(DBusWatch *watch, void *data); + +static void +cdbus_callback_watch_toggled(DBusWatch *watch, void *data); + +static bool +cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data); + +static bool +cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data); + +static bool +cdbus_apdarg_enum(session_t *ps, DBusMessage *msg, const void *data); + +static bool +cdbus_apdarg_string(session_t *ps, DBusMessage *msg, const void *data); + +static bool +cdbus_apdarg_wids(session_t *ps, DBusMessage *msg, const void *data); + +/** @name DBus signal sending + */ +///@{ + +static bool +cdbus_signal(session_t *ps, const char *name, + bool (*func)(session_t *ps, DBusMessage *msg, const void *data), + const void *data); + +/** + * Send a signal with no argument. + */ +static inline bool +cdbus_signal_noarg(session_t *ps, const char *name) { + return cdbus_signal(ps, name, NULL, NULL); +} + +/** + * Send a signal with a Window ID as argument. + */ +static inline bool +cdbus_signal_wid(session_t *ps, const char *name, Window wid) { + return cdbus_signal(ps, name, cdbus_apdarg_wid, &wid); +} + +///@} + +/** @name DBus reply sending + */ +///@{ + +static bool +cdbus_reply(session_t *ps, DBusMessage *srcmsg, + bool (*func)(session_t *ps, DBusMessage *msg, const void *data), + const void *data); + +static bool +cdbus_reply_errm(session_t *ps, DBusMessage *msg); + +#define cdbus_reply_err(ps, srcmsg, err_name, err_format, ...) \ + cdbus_reply_errm((ps), dbus_message_new_error_printf((srcmsg), (err_name), (err_format), ## __VA_ARGS__)) + +/** + * Send a reply with no argument. + */ +static inline bool +cdbus_reply_noarg(session_t *ps, DBusMessage *srcmsg) { + return cdbus_reply(ps, srcmsg, NULL, NULL); +} + +/** + * Send a reply with a bool argument. + */ +static inline bool +cdbus_reply_bool(session_t *ps, DBusMessage *srcmsg, bool bval) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_bool, &bval); +} + +/** + * Send a reply with a wid argument. + */ +static inline bool +cdbus_reply_wid(session_t *ps, DBusMessage *srcmsg, Window wid) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_wid, &wid); +} + +/** + * Send a reply with a string argument. + */ +static inline bool +cdbus_reply_string(session_t *ps, DBusMessage *srcmsg, const char *str) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_string, str); +} + +/** + * Send a reply with a enum argument. + */ +static inline bool +cdbus_reply_enum(session_t *ps, DBusMessage *srcmsg, cdbus_enum_t eval) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_enum, &eval); +} + +///@} + +static bool +cdbus_msg_get_arg(DBusMessage *msg, int count, const int type, void *pdest); + +/** + * Return a string representation of a D-Bus message type. + */ +static inline const char * +cdbus_repr_msgtype(DBusMessage *msg) { + return dbus_message_type_to_string(dbus_message_get_type(msg)); +} + +/** @name Message processing + */ +///@{ + +static void +cdbus_process(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_list_win(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_win_get(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_win_set(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_find_win(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_opts_get(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_opts_set(session_t *ps, DBusMessage *msg); + +static bool +cdbus_process_introspect(session_t *ps, DBusMessage *msg); + +///@} From 56a35506b1f65bd36e4f5b30054b348eea79f515 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 24 Jan 2013 13:38:03 +0800 Subject: [PATCH 104/178] Bug fix #84: Root window not repainted sometimes on wallpaper change - Fix a bug that root window is not repainted on wallpaper change unless an Expose X event is received. Seemingly, if there's no mapped window on a screen, X will not send an Expose event when the wallpaper changes. Thanks to baskerville for reporting. - Fix a X Pixmap leak when there's no existing wallpaper pixmap found. - Fix a bug in mstrncpy() that null character is not added to the end of the copied string. - Make VSYNC_STRS public, for use in src/dbus.c. Adjust the type of WINTYPES array. Add NUM_VSYNC. - Add more targets for various D-Bus methods. Add "bad_target" D-Bus error. Improve error handling. Add more helper functions to append arguments to a D-Bus message. Add Introspect method to D-Bus introspection reply. - Add public declarations of things in the new condition format code to common.h. Move definitions of some inline functions from compton.h to common.h. Make some functions public. Move wid_get_prop_adv() to compton.c. The primary code files of the new format src/c2.{c,h} will be published when ready. - Add support for dumping version string in Makefile (make version), to make snapshot generation easier. - Add repeated inclusion protection to common.h. - Update documentation. - Use gsed instead of sed in dbus-examples/cdbus-driver.sh if possible, as some BSD systems does not come with GNU sed by default. Thanks to DaChiChang for reporting. - Code clean-up. Small type changes in register_cm() to silence warnings. Quit on failure in parse_vsync(). Apply stricter checks in force_repaint(). --- common.h | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++---- compton.c | 123 +++++++++++++++++++++++++++++++++------------- compton.h | 128 ------------------------------------------------ dbus.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++------ dbus.h | 35 +++++++++++++ 5 files changed, 383 insertions(+), 187 deletions(-) diff --git a/common.h b/common.h index 31c9c2002..004d9553b 100644 --- a/common.h +++ b/common.h @@ -8,6 +8,9 @@ * */ +#ifndef COMPTON_COMMON_H +#define COMPTON_COMMON_H + // === Options === // Debug options, enable them using -D in CFLAGS @@ -22,6 +25,7 @@ // #define DEBUG_ALLOC_REG 1 // #define DEBUG_FRAME 1 // #define DEBUG_LEADER 1 +// #define DEBUG_C2 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled @@ -40,6 +44,12 @@ // #define CONFIG_VSYNC_OPENGL 1 // Whether to enable DBus support with libdbus. // #define CONFIG_DBUS 1 +// Whether to enable condition support. +// #define CONFIG_C2 1 + +#if !defined(CONFIG_C2) && defined(DEBUG_C2) +#error Cannot enable c2 debugging without c2 support. +#endif // === Includes === @@ -264,6 +274,7 @@ typedef enum { VSYNC_NONE, VSYNC_DRM, VSYNC_OPENGL, + NUM_VSYNC, } vsync_t; #ifdef CONFIG_VSYNC_OPENGL @@ -286,9 +297,15 @@ struct _timeout_t; struct _win; +#ifdef CONFIG_C2 +typedef struct _c2_lptr c2_lptr_t; +#endif + /// Structure representing all options. typedef struct { // === General === + /// The display name we used. NULL means we are using the value of the + /// DISPLAY environment variable. char *display; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; @@ -534,13 +551,13 @@ typedef struct { /// Nanosecond offset of the first painting. long paint_tm_offset; - #ifdef CONFIG_VSYNC_DRM +#ifdef CONFIG_VSYNC_DRM // === DRM VSync related === /// File descriptor of DRI device file. Used for DRM VSync. int drm_fd; - #endif +#endif - #ifdef CONFIG_VSYNC_OPENGL +#ifdef CONFIG_VSYNC_OPENGL // === OpenGL VSync related === /// GLX context. GLXContext glx_context; @@ -548,7 +565,7 @@ typedef struct { f_GetVideoSync glx_get_video_sync; /// Pointer to glXWaitVideoSyncSGI function. f_WaitVideoSync glx_wait_video_sync; - #endif +#endif // === X extension related === /// Event base number for X Fixes extension. @@ -584,14 +601,14 @@ typedef struct { int randr_event; /// Error base number for X RandR extension. int randr_error; - #ifdef CONFIG_VSYNC_OPENGL +#ifdef CONFIG_VSYNC_OPENGL /// Whether X GLX extension exists. bool glx_exists; /// Event base number for X GLX extension. int glx_event; /// Error base number for X GLX extension. int glx_error; - #endif +#endif /// Whether X DBE extension exists. bool dbe_exists; /// Whether X Render convolution filter exists. @@ -816,7 +833,8 @@ typedef enum { WIN_EVMODE_CLIENT } win_evmode_t; -extern const char *WINTYPES[NUM_WINTYPES]; +extern const char * const WINTYPES[NUM_WINTYPES]; +extern const char * const VSYNC_STRS[NUM_VSYNC]; extern session_t *ps_g; // == Debugging code == @@ -1055,6 +1073,7 @@ mstrncpy(const char *src, unsigned len) { char *str = malloc(sizeof(char) * (len + 1)); strncpy(str, src, len); + str[len] = '\0'; return str; } @@ -1259,6 +1278,14 @@ fds_poll(session_t *ps, struct timeval *ptv) { } #undef CPY_FDS +/** + * Wrapper of XInternAtom() for convenience. + */ +static inline Atom +get_atom(session_t *ps, const char *atom_name) { + return XInternAtom(ps->dpy, atom_name, False); +} + /** * Find a window from window id in window linked list of the session. */ @@ -1326,6 +1353,86 @@ copy_region(const session_t *ps, XserverRegion oldregion) { return region; } +/** + * Determine if a window has a specific property. + * + * @param ps current session + * @param w window to check + * @param atom atom of property to check + * @return 1 if it has the attribute, 0 otherwise + */ +static inline bool +wid_has_prop(const session_t *ps, Window w, Atom atom) { + Atom type = None; + int format; + unsigned long nitems, after; + unsigned char *data; + + if (Success == XGetWindowProperty(ps->dpy, w, atom, 0, 0, False, + AnyPropertyType, &type, &format, &nitems, &after, &data)) { + XFree(data); + if (type) return true; + } + + return false; +} + +winprop_t +wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, + long length, Atom rtype, int rformat); + +/** + * Wrapper of wid_get_prop_adv(). + */ +static inline winprop_t +wid_get_prop(const session_t *ps, Window wid, Atom atom, long length, + Atom rtype, int rformat) { + return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat); +} + +/** + * Get the numeric property value from a win_prop_t. + */ +static inline long +winprop_get_int(winprop_t prop) { + long tgt = 0; + + if (!prop.nitems) + return 0; + + switch (prop.format) { + case 8: tgt = *(prop.data.p8); break; + case 16: tgt = *(prop.data.p16); break; + case 32: tgt = *(prop.data.p32); break; + default: assert(0); + break; + } + + return tgt; +} + +bool +wid_get_text_prop(session_t *ps, Window wid, Atom prop, + char ***pstrlst, int *pnstr); + +/** + * Free a winprop_t. + * + * @param pprop pointer to the winprop_t to free. + */ +static inline void +free_winprop(winprop_t *pprop) { + // Empty the whole structure to avoid possible issues + if (pprop->data.p8) { + XFree(pprop->data.p8); + pprop->data.p8 = NULL; + } + pprop->nitems = 0; +} + +void +force_repaint(session_t *ps); + #ifdef CONFIG_DBUS /** @name DBus handling */ @@ -1363,8 +1470,25 @@ win_set_focused_force(session_t *ps, win *w, switch_t val); void win_set_invert_color_force(session_t *ps, win *w, switch_t val); - -void -force_repaint(session_t *ps); //!@} #endif + +#ifdef CONFIG_C2 +/** @name c2 + */ +///@{ + +c2_lptr_t * +c2_parse(session_t *ps, c2_lptr_t **pcondlst, char *pattern); + +c2_lptr_t * +c2_free_lptr(c2_lptr_t *lp); + +bool +c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, + const c2_lptr_t **cache); +#endif + +///@} + +#endif diff --git a/compton.c b/compton.c index 90133d11c..814bf4a3b 100644 --- a/compton.c +++ b/compton.c @@ -13,7 +13,7 @@ // === Global constants === /// Name strings for window types. -const char *WINTYPES[NUM_WINTYPES] = { +const char * const WINTYPES[NUM_WINTYPES] = { "unknown", "desktop", "dock", @@ -31,9 +31,16 @@ const char *WINTYPES[NUM_WINTYPES] = { "dnd", }; +/// Names of VSync modes +const char * const VSYNC_STRS[NUM_VSYNC] = { + "none", // VSYNC_NONE + "drm", // VSYNC_DRM + "opengl", // VSYNC_OPENGL +}; + /// Names of root window properties that could point to a pixmap of /// background. -const char *background_props_str[] = { +const static char *background_props_str[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", 0, @@ -534,6 +541,52 @@ should_ignore(session_t *ps, unsigned long sequence) { // === Windows === +/** + * Get a specific attribute of a window. + * + * Returns a blank structure if the returned type and format does not + * match the requested type and format. + * + * @param ps current session + * @param w window + * @param atom atom of attribute to fetch + * @param length length to read + * @param rtype atom of the requested type + * @param rformat requested format + * @return a winprop_t structure containing the attribute + * and number of items. A blank one on failure. + */ +winprop_t +wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, + long length, Atom rtype, int rformat) { + Atom type = None; + int format = 0; + unsigned long nitems = 0, after = 0; + unsigned char *data = NULL; + + if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length, + False, rtype, &type, &format, &nitems, &after, &data) + && nitems && (AnyPropertyType == type || type == rtype) + && (!format || format == rformat) + && (8 == format || 16 == format || 32 == format)) { + return (winprop_t) { + .data.p8 = data, + .nitems = nitems, + .type = type, + .format = format, + }; + } + + XFree(data); + + return (winprop_t) { + .data.p8 = NULL, + .nitems = 0, + .type = AnyPropertyType, + .format = 0 + }; +} + /** * Check if a window has rounded corners. */ @@ -966,6 +1019,8 @@ root_tile_f(session_t *ps) { c.alpha = 0xffff; XRenderFillRectangle( ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); + + free_pixmap(ps, &pixmap); } return picture; @@ -2120,10 +2175,8 @@ win_determine_mode(session_t *ps, win *w) { * > window type default opacity (if not opaque) * > inactive_opacity * - * @param dpy X display to use + * @param ps current session * @param w struct _win object representing the window - * @param refetch_prop whether _NET_WM_OPACITY of the window needs to be - * refetched */ static void calc_opacity(session_t *ps, win *w) { @@ -2858,9 +2911,9 @@ root_damaged(session_t *ps) { add_damage(ps, parts); } */ } - // Mark screen damaged if we are painting on overlay - if (ps->o.paint_on_overlay) - add_damage(ps, get_screen_region(ps)); + + // Mark screen damaged + force_repaint(ps); } static void @@ -3133,7 +3186,7 @@ win_get_leader_raw(session_t *ps, win *w, int recursions) { /** * Get the value of a text property of a window. */ -static bool +bool wid_get_text_prop(session_t *ps, Window wid, Atom prop, char ***pstrlst, int *pnstr) { XTextProperty text_prop = { NULL, None, 0, 0 }; @@ -3282,6 +3335,19 @@ win_get_class(session_t *ps, win *w) { return true; } +/** + * Force a full-screen repaint. + */ +void +force_repaint(session_t *ps) { + assert(ps->screen_reg); + XserverRegion reg = None; + if (ps->screen_reg && (reg = copy_region(ps, ps->screen_reg))) { + ps->ev_received = true; + add_damage(ps, reg); + } +} + #ifdef CONFIG_DBUS /** @name DBus hooks */ @@ -3319,18 +3385,6 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val) { win_determine_invert_color(ps, w); } } - -/** - * Force a full-screen repaint. - */ -void -force_repaint(session_t *ps) { - XserverRegion reg = None; - if (ps->screen_reg && (reg = copy_region(ps, ps->screen_reg))) { - ps->ev_received = true; - add_damage(ps, reg); - } -} //!@} #endif @@ -4039,6 +4093,9 @@ usage(void) { "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" + "--dbus\n" + " Enable remote control via D-Bus. See the D-BUS API section in the\n" + " man page for more details.\n" "\n" "Format of a condition:\n" "\n" @@ -4068,7 +4125,6 @@ static void register_cm(session_t *ps, bool want_glxct) { Atom a; char *buf; - int len, s; #ifdef CONFIG_VSYNC_OPENGL // Create a window with the wanted GLX visual @@ -4128,8 +4184,8 @@ register_cm(session_t *ps, bool want_glxct) { Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL, NULL); - len = strlen(REGISTER_PROP) + 2; - s = ps->scr; + unsigned len = strlen(REGISTER_PROP) + 2; + int s = ps->scr; while (s >= 10) { ++len; @@ -4284,21 +4340,15 @@ open_config_file(char *cpath, char **ppath) { */ static inline void parse_vsync(session_t *ps, const char *optarg) { - const static char * const vsync_str[] = { - "none", // VSYNC_NONE - "drm", // VSYNC_DRM - "opengl", // VSYNC_OPENGL - }; - vsync_t i; - for (i = 0; i < (sizeof(vsync_str) / sizeof(vsync_str[0])); ++i) - if (!strcasecmp(optarg, vsync_str[i])) { + for (i = 0; i < (sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); ++i) + if (!strcasecmp(optarg, VSYNC_STRS[i])) { ps->o.vsync = i; break; } - if ((sizeof(vsync_str) / sizeof(vsync_str[0])) == i) { - fputs("Invalid --vsync argument. Ignored.\n", stderr); + if ((sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])) == i) { + printf_errfq(1, "(\"%s\"): Invalid --vsync argument.", optarg); } } @@ -6114,6 +6164,11 @@ main(int argc, char **argv) { printf_errf("Failed to create new session."); return 1; } +#ifdef DEBUG_C2 + // c2_parse(ps_g, NULL, "name ~= \"master\""); + // c2_parse(ps_g, NULL, "n:e:Notification"); + c2_parse(ps_g, NULL, "(WM_NAME:16s = 'Notification' || class_g = 'fox') && class_g = 'fox'"); +#endif session_run(ps_g); ps_old = ps_g; session_destroy(ps_g); diff --git a/compton.h b/compton.h index c30991076..7670424e9 100644 --- a/compton.h +++ b/compton.h @@ -52,14 +52,6 @@ set_ignore_next(session_t *ps) { static int should_ignore(session_t *ps, unsigned long sequence); -/** - * Wrapper of XInternAtom() for convenience. - */ -static inline Atom -get_atom(session_t *ps, const char *atom_name) { - return XInternAtom(ps->dpy, atom_name, False); -} - /** * Return the painting target window. */ @@ -291,122 +283,6 @@ static Picture solid_picture(session_t *ps, bool argb, double a, double r, double g, double b); -/** - * Determine if a window has a specific property. - * - * @param session_t current session - * @param w window to check - * @param atom atom of property to check - * @return 1 if it has the attribute, 0 otherwise - */ -static inline bool -wid_has_prop(const session_t *ps, Window w, Atom atom) { - Atom type = None; - int format; - unsigned long nitems, after; - unsigned char *data; - - if (Success == XGetWindowProperty(ps->dpy, w, atom, 0, 0, False, - AnyPropertyType, &type, &format, &nitems, &after, &data)) { - XFree(data); - if (type) return true; - } - - return false; -} - -/** - * Get a specific attribute of a window. - * - * Returns a blank structure if the returned type and format does not - * match the requested type and format. - * - * @param session_t current session - * @param w window - * @param atom atom of attribute to fetch - * @param length length to read - * @param rtype atom of the requested type - * @param rformat requested format - * @return a winprop_t structure containing the attribute - * and number of items. A blank one on failure. - */ -// TODO: Move to compton.c -static winprop_t -wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, - long length, Atom rtype, int rformat) { - Atom type = None; - int format = 0; - unsigned long nitems = 0, after = 0; - unsigned char *data = NULL; - - if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length, - False, rtype, &type, &format, &nitems, &after, &data) - && nitems && (AnyPropertyType == type || type == rtype) - && (!format || format == rformat) - && (8 == format || 16 == format || 32 == format)) { - return (winprop_t) { - .data.p8 = data, - .nitems = nitems, - .type = type, - .format = format, - }; - } - - XFree(data); - - return (winprop_t) { - .data.p8 = NULL, - .nitems = 0, - .type = AnyPropertyType, - .format = 0 - }; -} - -/** - * Wrapper of wid_get_prop_adv(). - */ -static inline winprop_t -wid_get_prop(const session_t *ps, Window wid, Atom atom, long length, - Atom rtype, int rformat) { - return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat); -} - -/** - * Get the numeric property value from a win_prop_t. - */ -static inline long -winprop_get_int(winprop_t prop) { - long tgt = 0; - - if (!prop.nitems) - return 0; - - switch (prop.format) { - case 8: tgt = *(prop.data.p8); break; - case 16: tgt = *(prop.data.p16); break; - case 32: tgt = *(prop.data.p32); break; - default: assert(0); - break; - } - - return tgt; -} - -/** - * Free a winprop_t. - * - * @param pprop pointer to the winprop_t to free. - */ -static inline void -free_winprop(winprop_t *pprop) { - // Empty the whole structure to avoid possible issues - if (pprop->data.p8) { - XFree(pprop->data.p8); - pprop->data.p8 = NULL; - } - pprop->nitems = 0; -} - /** * Stop listening for events on a particular window. */ @@ -768,10 +644,6 @@ error(Display *dpy, XErrorEvent *ev); static void expose_root(session_t *ps, XRectangle *rects, int nrects); -static bool -wid_get_text_prop(session_t *ps, Window wid, Atom prop, - char ***pstrlst, int *pnstr); - static Window wid_get_prop_window(session_t *ps, Window wid, Atom aprop); diff --git a/dbus.c b/dbus.c index 16be7cc8f..a86ed174d 100644 --- a/dbus.c +++ b/dbus.c @@ -266,6 +266,48 @@ cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data) { return true; } +/** + * Callback to append an int32 argument to a message. + */ +static bool +cdbus_apdarg_int32(session_t *ps, DBusMessage *msg, const void *data) { + if (!dbus_message_append_args(msg, DBUS_TYPE_INT32, data, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append an uint32 argument to a message. + */ +static bool +cdbus_apdarg_uint32(session_t *ps, DBusMessage *msg, const void *data) { + if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, data, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + +/** + * Callback to append a double argument to a message. + */ +static bool +cdbus_apdarg_double(session_t *ps, DBusMessage *msg, const void *data) { + if (!dbus_message_append_args(msg, DBUS_TYPE_DOUBLE, data, + DBUS_TYPE_INVALID)) { + printf_errf("(): Failed to append argument."); + return false; + } + + return true; +} + /** * Callback to append a Window argument to a message. */ @@ -639,6 +681,21 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { return true; \ } + cdbus_m_win_get_do(id, cdbus_reply_wid); + + // next + if (!strcmp("next", target)) { + cdbus_reply_wid(ps, msg, (w->next ? w->next->id: 0)); + return true; + } + + // map_state + if (!strcmp("map_state", target)) { + cdbus_reply_bool(ps, msg, w->a.map_state); + return true; + } + + cdbus_m_win_get_do(mode, cdbus_reply_enum); cdbus_m_win_get_do(client_win, cdbus_reply_wid); cdbus_m_win_get_do(damaged, cdbus_reply_bool); cdbus_m_win_get_do(destroyed, cdbus_reply_bool); @@ -649,15 +706,22 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); cdbus_m_win_get_do(focused_force, cdbus_reply_enum); cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum); - if (!strcmp("map_state", target)) { - cdbus_reply_bool(ps, msg, w->a.map_state); - return true; - } + cdbus_m_win_get_do(name, cdbus_reply_string); + cdbus_m_win_get_do(class_instance, cdbus_reply_string); + cdbus_m_win_get_do(class_general, cdbus_reply_string); + cdbus_m_win_get_do(role, cdbus_reply_string); + cdbus_m_win_get_do(opacity, cdbus_reply_uint32); + cdbus_m_win_get_do(frame_opacity, cdbus_reply_double); + cdbus_m_win_get_do(left_width, cdbus_reply_uint32); + cdbus_m_win_get_do(right_width, cdbus_reply_uint32); + cdbus_m_win_get_do(top_width, cdbus_reply_uint32); + cdbus_m_win_get_do(bottom_width, cdbus_reply_uint32); #undef cdbus_m_win_get_do - printf_errf("(): No matching target found."); + printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); - return false; + return true; } /** @@ -723,9 +787,10 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) { } #undef cdbus_m_win_set_do - printf_errf("(): No matching target found."); + printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); - return false; + return true; cdbus_process_win_set_success: if (!dbus_message_get_no_reply(msg)) @@ -761,9 +826,10 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg) { wid = w->id; } else { - printf_errf("(): No matching target found."); + printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); - return false; + return true; } cdbus_reply_wid(ps, msg, wid); @@ -787,7 +853,12 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { return true; \ } - cdbus_m_opts_get_do(display, cdbus_reply_string); + // display + if (!strcmp("display", target)) { + cdbus_reply_string(ps, msg, DisplayString(ps->dpy)); + return true; + } + cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool); cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool); cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); @@ -797,12 +868,45 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(logpath, cdbus_reply_string); cdbus_m_opts_get_do(synchronize, cdbus_reply_bool); + cdbus_m_opts_get_do(refresh_rate, cdbus_reply_int32); + cdbus_m_opts_get_do(sw_opti, cdbus_reply_bool); + if (!strcmp("vsync", target)) { + assert(ps->o.vsync < sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); + cdbus_reply_string(ps, msg, VSYNC_STRS[ps->o.vsync]); + return true; + } + cdbus_m_opts_get_do(dbe, cdbus_reply_bool); + cdbus_m_opts_get_do(vsync_aggressive, cdbus_reply_bool); + + cdbus_m_opts_get_do(shadow_red, cdbus_reply_double); + cdbus_m_opts_get_do(shadow_green, cdbus_reply_double); + cdbus_m_opts_get_do(shadow_blue, cdbus_reply_double); + cdbus_m_opts_get_do(shadow_radius, cdbus_reply_int32); + cdbus_m_opts_get_do(shadow_offset_x, cdbus_reply_int32); + cdbus_m_opts_get_do(shadow_offset_y, cdbus_reply_int32); + cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double); cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); + + cdbus_m_opts_get_do(blur_background, cdbus_reply_bool); + cdbus_m_opts_get_do(blur_background_frame, cdbus_reply_bool); + cdbus_m_opts_get_do(blur_background_fixed, cdbus_reply_bool); + + cdbus_m_opts_get_do(inactive_dim, cdbus_reply_double); + cdbus_m_opts_get_do(inactive_dim_fixed, cdbus_reply_bool); + + cdbus_m_opts_get_do(use_ewmh_active_win, cdbus_reply_bool); + cdbus_m_opts_get_do(detect_transient, cdbus_reply_bool); + cdbus_m_opts_get_do(detect_client_leader, cdbus_reply_bool); + + cdbus_m_opts_get_do(track_focus, cdbus_reply_bool); + cdbus_m_opts_get_do(track_wdata, cdbus_reply_bool); + cdbus_m_opts_get_do(track_leader, cdbus_reply_bool); #undef cdbus_m_opts_get_do - printf_errf("(): No matching target found."); + printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); - return false; + return true; } /** @@ -849,9 +953,10 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { } #undef cdbus_m_opts_set_do - printf_errf("(): No matching target found."); + printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADTGT, CDBUS_ERROR_BADTGT_S, target); - return false; + return true; cdbus_process_opts_set_success: if (!dbus_message_get_no_reply(msg)) @@ -868,6 +973,11 @@ cdbus_process_introspect(session_t *ps, DBusMessage *msg) { "\n" "\n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" " \n" " \n" diff --git a/dbus.h b/dbus.h index fb51c8cc4..e69a6ae9b 100644 --- a/dbus.h +++ b/dbus.h @@ -24,6 +24,8 @@ #define CDBUS_ERROR_BADARG_S "Something wrong in arguments?" #define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window" #define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found." +#define CDBUS_ERROR_BADTGT CDBUS_ERROR_PREFIX ".bad_target" +#define CDBUS_ERROR_BADTGT_S "Target \"%s\" not found." #define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden" #define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied." @@ -75,6 +77,15 @@ cdbus_callback_watch_toggled(DBusWatch *watch, void *data); static bool cdbus_apdarg_bool(session_t *ps, DBusMessage *msg, const void *data); +static bool +cdbus_apdarg_int32(session_t *ps, DBusMessage *msg, const void *data); + +static bool +cdbus_apdarg_uint32(session_t *ps, DBusMessage *msg, const void *data); + +static bool +cdbus_apdarg_double(session_t *ps, DBusMessage *msg, const void *data); + static bool cdbus_apdarg_wid(session_t *ps, DBusMessage *msg, const void *data); @@ -145,6 +156,30 @@ cdbus_reply_bool(session_t *ps, DBusMessage *srcmsg, bool bval) { return cdbus_reply(ps, srcmsg, cdbus_apdarg_bool, &bval); } +/** + * Send a reply with an int32 argument. + */ +static inline bool +cdbus_reply_int32(session_t *ps, DBusMessage *srcmsg, int32_t val) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_int32, &val); +} + +/** + * Send a reply with an uint32 argument. + */ +static inline bool +cdbus_reply_uint32(session_t *ps, DBusMessage *srcmsg, uint32_t val) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_uint32, &val); +} + +/** + * Send a reply with a double argument. + */ +static inline bool +cdbus_reply_double(session_t *ps, DBusMessage *srcmsg, double val) { + return cdbus_reply(ps, srcmsg, cdbus_apdarg_double, &val); +} + /** * Send a reply with a wid argument. */ From 679bfe3cab740f06b38f606d599304f515b9de4d Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 28 Jan 2013 21:39:38 +0800 Subject: [PATCH 105/178] Feature #16: Advanced window matching - Add advanced window matching system, capable of matching against arbitrary window properties as well as a series of internal properties, with 4 additional operators (>, <, >=, <=) useful for integer targets, and support of logical operators. The old matching system is removed, but compatibility with the format is retained. - As the new matching system is pretty complicated, and I have no past experience in writing a parser, it's pretty possible that bugs are present. It also has inferior performance, but I hope it doesn't matter on modern CPUs. - It's possible to disable matching system at compile time with NO_C2=1 now. - Add ps->o.config_file to track which config file we have actually read. Queryable via D-Bus. - Parse -d in first pass in get_cfg() as c2 needs to query X to get atoms during condition parsing. - Fix a bug in wid_get_prop_adv() that 0 == rformat is not handled correctly. - Fix incompatibility with FreeBSD sed in dbus-examples/cdbus-driver.sh . - Add recipe to generate .clang_complete in Makefile, used by Vim clang_complete plugin. - Add DEBUG_C2 for debugging condition string parsing. DEBUG_WINMATCH is still used for match debugging. - Rename win_on_wdata_change() to win_on_factor_change(). - Extra malloc() failure checks. Add const to matching cache members in session_t. Code clean-up. Documentation update. --- c2.c | 1296 +++++++++++++++++++++++++++++++++++++++++++++++++++++ c2.h | 326 ++++++++++++++ common.h | 58 +-- compton.c | 396 ++++------------ compton.h | 62 +-- dbus.c | 1 + 6 files changed, 1756 insertions(+), 383 deletions(-) create mode 100644 c2.c create mode 100644 c2.h diff --git a/c2.c b/c2.c new file mode 100644 index 000000000..5bbb4e3a8 --- /dev/null +++ b/c2.c @@ -0,0 +1,1296 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "c2.h" + +/** + * Parse a condition string. + */ +c2_lptr_t * +c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) { + if (!pattern) + return NULL; + + // Parse the pattern + c2_ptr_t result = C2_PTR_INIT; + int offset = -1; + + if (strlen(pattern) >= 2 && ':' == pattern[1]) + offset = c2_parse_legacy(ps, pattern, 0, &result); + else + offset = c2_parse_grp(ps, pattern, 0, &result, 0); + + if (offset < 0) { + c2_freep(&result); + return NULL; + } + + // Insert to pcondlst + { + const static c2_lptr_t lptr_def = C2_LPTR_INIT; + c2_lptr_t *plptr = malloc(sizeof(c2_lptr_t)); + if (!plptr) + printf_errfq(1, "(): Failed to allocate memory for new condition linked" + " list element."); + memcpy(plptr, &lptr_def, sizeof(c2_lptr_t)); + plptr->ptr = result; + if (pcondlst) { + plptr->next = *pcondlst; + *pcondlst = plptr; + } + +#ifdef DEBUG_C2 + printf_dbgf("(\"%s\"): ", pattern); + c2_dump(plptr->ptr); +#endif + + return plptr; + } +} + +#undef c2_error +#define c2_error(format, ...) do { \ + printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ + ## __VA_ARGS__); \ + return -1; \ +} while(0) + +#define C2H_SKIP_SPACES() { while (isspace(pattern[offset])) ++offset; } + +/** + * Parse a group in condition string. + * + * @return offset of next character in string + */ +static int +c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level) { + // Check for recursion levels + if (level > C2_MAX_LEVELS) + c2_error("Exceeded maximum recursion levels."); + + if (!pattern) + return -1; + + // Expected end character + const char endchar = (offset ? ')': '\0'); + +#undef c2_error +#define c2_error(format, ...) do { \ + printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ + ## __VA_ARGS__); \ + goto c2_parse_grp_fail; \ +} while(0) + + // We use a system that a maximum of 2 elements are kept. When we find + // the third element, we combine the elements according to operator + // precedence. This design limits operators to have at most two-levels + // of precedence and fixed left-to-right associativity. + + // For storing branch operators. ops[0] is actually unused + c2_b_op_t ops[3] = { }; + // For storing elements + c2_ptr_t eles[2] = { C2_PTR_INIT, C2_PTR_INIT }; + // Index of next free element slot in eles + int elei = 0; + // Pointer to the position of next element + c2_ptr_t *pele = eles; + // Negation flag of next operator + bool neg = false; + // Whether we are expecting an element immediately, is true at first, or + // after encountering a logical operator + bool next_expected = true; + + // Parse the pattern character-by-character + for (; pattern[offset]; ++offset) { + assert(elei <= 2); + + // Jump over spaces + if (isspace(pattern[offset])) + continue; + + // Handle end of group + if (')' == pattern[offset]) + break; + + // Handle "!" + if ('!' == pattern[offset]) { + if (!next_expected) + c2_error("Unexpected \"!\"."); + + neg = !neg; + continue; + } + + // Handle AND and OR + if ('&' == pattern[offset] || '|' == pattern[offset]) { + if (next_expected) + c2_error("Unexpected logical operator."); + + next_expected = true; + if (!mstrncmp("&&", pattern + offset)) { + ops[elei] = C2_B_OAND; + ++offset; + } + else if (!mstrncmp("||", pattern + offset)) { + ops[elei] = C2_B_OOR; + ++offset; + } + else + c2_error("Illegal logical operator."); + + continue; + } + + // Parsing an element + if (!next_expected) + c2_error("Unexpected expression."); + + assert(!elei || ops[elei]); + + // If we are out of space + if (2 == elei) { + --elei; + // If the first operator has higher or equal precedence, combine + // the first two elements + if (c2h_b_opcmp(ops[1], ops[2]) >= 0) { + eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]); + c2_ptr_reset(&eles[1]); + pele = &eles[elei]; + ops[1] = ops[2]; + } + // Otherwise, combine the second and the incoming one + else { + eles[1] = c2h_comb_tree(ops[2], eles[1], C2_PTR_NULL); + assert(eles[1].isbranch); + pele = &eles[1].b->opr2; + } + // The last operator always needs to be reset + ops[2] = C2_B_OUNDEFINED; + } + + // It's a subgroup if it starts with '(' + if ('(' == pattern[offset]) { + if ((offset = c2_parse_grp(ps, pattern, offset + 1, pele, level + 1)) < 0) + goto c2_parse_grp_fail; + } + // Otherwise it's a leaf + else { + if ((offset = c2_parse_target(ps, pattern, offset, pele)) < 0) + goto c2_parse_grp_fail; + + assert(!pele->isbranch && !c2_ptr_isempty(*pele)); + + if ((offset = c2_parse_op(pattern, offset, pele)) < 0) + goto c2_parse_grp_fail; + + if ((offset = c2_parse_pattern(ps, pattern, offset, pele)) < 0) + goto c2_parse_grp_fail; + + if (!c2_l_postprocess(ps, pele->l)) + goto c2_parse_grp_fail; + } + // Decrement offset -- we will increment it in loop update + --offset; + + // Apply negation + if (neg) { + neg = false; + if (pele->isbranch) + pele->b->neg = !pele->b->neg; + else + pele->l->neg = !pele->l->neg; + } + + next_expected = false; + ++elei; + pele = &eles[elei]; + } + + // Wrong end character? + if (pattern[offset] && !endchar) + c2_error("Expected end of string but found '%c'.", pattern[offset]); + if (!pattern[offset] && endchar) + c2_error("Expected '%c' but found end of string.", endchar); + + // Handle end of group + if (!elei) { + c2_error("Empty group."); + } + else if (next_expected) { + c2_error("Missing rule before end of group."); + } + else if (elei > 1) { + assert(2 == elei); + assert(ops[1]); + eles[0] = c2h_comb_tree(ops[1], eles[0], eles[1]); + c2_ptr_reset(&eles[1]); + } + + *presult = eles[0]; + + if (')' == pattern[offset]) + ++offset; + + return offset; + +c2_parse_grp_fail: + c2_freep(&eles[0]); + c2_freep(&eles[1]); + + return -1; +} + +#undef c2_error +#define c2_error(format, ...) do { \ + printf_err("Pattern \"%s\" pos %d: " format, pattern, offset, \ + ## __VA_ARGS__); \ + return -1; \ +} while(0) + +/** + * Parse the target part of a rule. + */ +static int +c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { + // Initialize leaf + presult->isbranch = false; + presult->l = malloc(sizeof(c2_l_t)); + if (!presult->l) + c2_error("Failed to allocate memory for new leaf."); + + c2_l_t * const pleaf = presult->l; + memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); + + // Parse negation marks + while ('!' == pattern[offset]) { + pleaf->neg = !pleaf->neg; + ++offset; + C2H_SKIP_SPACES(); + } + + // Copy target name out + unsigned tgtlen = 0; + for (; pattern[offset] + && (isalnum(pattern[offset]) || '_' == pattern[offset]); ++offset) { + ++tgtlen; + } + if (!tgtlen) + c2_error("Empty target."); + pleaf->tgt = mstrncpy(&pattern[offset - tgtlen], tgtlen); + + // Check for predefined targets + for (unsigned i = 1; i < sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0]); ++i) { + if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) { + pleaf->predef = i; + pleaf->type = C2_PREDEFS[i].type; + pleaf->format = C2_PREDEFS[i].format; + break; + } + } + + // Alias for predefined targets + if (!pleaf->predef) { +#define TGTFILL(pdefid) \ + (pleaf->predef = pdefid, \ + pleaf->type = C2_PREDEFS[pdefid].type, \ + pleaf->format = C2_PREDEFS[pdefid].format) + + // if (!strcmp("WM_NAME", tgt) || !strcmp("_NET_WM_NAME", tgt)) + // TGTFILL(C2_L_PNAME); +#undef TGTFILL + + // Alias for custom properties +#define TGTFILL(target, type, format) \ + (pleaf->target = mstrcpy(target), \ + pleaf->type = type, \ + pleaf->format = format) + + // if (!strcmp("SOME_ALIAS")) + // TGTFILL("ALIAS_TEXT", C2_L_TSTRING, 32); +#undef TGTFILL + } + + C2H_SKIP_SPACES(); + + // Parse target-on-frame flag + if ('@' == pattern[offset]) { + pleaf->tgt_onframe = true; + ++offset; + C2H_SKIP_SPACES(); + } + + // Parse index + if ('[' == pattern[offset]) { + offset++; + + C2H_SKIP_SPACES(); + + int index = -1; + char *endptr = NULL; + + index = strtol(pattern + offset, &endptr, 0); + + if (!endptr || pattern + offset == endptr) + c2_error("No index number found after bracket."); + + if (index < 0) + c2_error("Index number invalid."); + + if (pleaf->predef) + c2_error("Predefined targets can't have index."); + + pleaf->index = index; + offset = endptr - pattern; + + C2H_SKIP_SPACES(); + + if (']' != pattern[offset]) + c2_error("Index end marker not found."); + + ++offset; + + C2H_SKIP_SPACES(); + } + + // Parse target type and format + if (':' == pattern[offset]) { + ++offset; + C2H_SKIP_SPACES(); + + // Look for format + bool hasformat = false; + int format = 0; + { + char *endptr = NULL; + format = strtol(pattern + offset, &endptr, 0); + assert(endptr); + if ((hasformat = (endptr && endptr != pattern + offset))) + offset = endptr - pattern; + C2H_SKIP_SPACES(); + } + + // Look for type + enum c2_l_type type = C2_L_TUNDEFINED; + { + switch (pattern[offset]) { + case 'w': type = C2_L_TWINDOW; break; + case 'd': type = C2_L_TDRAWABLE; break; + case 'c': type = C2_L_TCARDINAL; break; + case 's': type = C2_L_TSTRING; break; + case 'a': type = C2_L_TATOM; break; + default: c2_error("Invalid type character."); + } + + if (type) { + if (pleaf->predef) { + printf_errf("(): Warning: Type specified for a default target will be ignored."); + } + else { + if (pleaf->type && type != pleaf->type) + printf_errf("(): Warning: Default type overridden on target."); + pleaf->type = type; + } + } + + offset++; + C2H_SKIP_SPACES(); + } + + // Default format + if (!pleaf->format) { + switch (pleaf->type) { + case C2_L_TWINDOW: + case C2_L_TDRAWABLE: + case C2_L_TATOM: + pleaf->format = 32; break; + case C2_L_TSTRING: + pleaf->format = 8; break; + default: + break; + } + } + + // Write format + if (hasformat) { + if (pleaf->predef) + printf_errf("(): Warning: Format \"%d\" specified on a default target will be ignored.", format); + else if (C2_L_TSTRING == pleaf->type) + printf_errf("(): Warning: Format \"%d\" specified on a string target will be ignored.", format); + else { + if (pleaf->format && pleaf->format != format) + printf_err("Warning: Default format %d overridden on target.", + pleaf->format); + pleaf->format = format; + } + } + } + + if (!pleaf->type) + c2_error("Target type cannot be determined."); + + // if (!pleaf->predef && !pleaf->format && C2_L_TSTRING != pleaf->type) + // c2_error("Target format cannot be determined."); + + if (pleaf->format && 8 != pleaf->format + && 16 != pleaf->format && 32 != pleaf->format) + c2_error("Invalid format."); + + return offset; +} + +/** + * Parse the operator part of a leaf. + */ +static int +c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) { + c2_l_t * const pleaf = presult->l; + + // Parse negation marks + C2H_SKIP_SPACES(); + while ('!' == pattern[offset]) { + pleaf->neg = !pleaf->neg; + ++offset; + C2H_SKIP_SPACES(); + } + + // Parse qualifiers + if ('*' == pattern[offset] || '^' == pattern[offset] + || '%' == pattern[offset] || '~' == pattern[offset]) { + switch (pattern[offset]) { + case '*': pleaf->match = C2_L_MCONTAINS; break; + case '^': pleaf->match = C2_L_MSTART; break; + case '%': pleaf->match = C2_L_MWILDCARD; break; + case '~': pleaf->match = C2_L_MPCRE; break; + default: assert(0); + } + ++offset; + C2H_SKIP_SPACES(); + } + + // Parse flags + while ('?' == pattern[offset]) { + pleaf->match_ignorecase = true; + ++offset; + C2H_SKIP_SPACES(); + } + + // Parse operator + while ('=' == pattern[offset] || '>' == pattern[offset] + || '<' == pattern[offset]) { + if ('=' == pattern[offset] && C2_L_OGT == pleaf->op) + pleaf->op = C2_L_OGTEQ; + else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op) + pleaf->op = C2_L_OLTEQ; + else if (pleaf->op) { + c2_error("Duplicate operator."); + } + else { + switch (pattern[offset]) { + case '=': pleaf->op = C2_L_OEQ; break; + case '>': pleaf->op = C2_L_OGT; break; + case '<': pleaf->op = C2_L_OLT; break; + default: assert(0); + } + } + ++offset; + C2H_SKIP_SPACES(); + } + + // Check for problems + if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase)) + c2_error("Exists/greater-than/less-than operators cannot have a qualifier."); + + return offset; +} + +/** + * Parse the pattern part of a leaf. + */ +static int +c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { + c2_l_t * const pleaf = presult->l; + + // Exists operator cannot have pattern + if (!pleaf->op) + return offset; + + C2H_SKIP_SPACES(); + + char *endptr = NULL; + // Check for boolean patterns + if (!strcmp_wd("true", &pattern[offset])) { + pleaf->ptntype = C2_L_PTINT; + pleaf->ptnint = true; + offset += strlen("true"); + } + else if (!strcmp_wd("false", &pattern[offset])) { + pleaf->ptntype = C2_L_PTINT; + pleaf->ptnint = false; + offset += strlen("false"); + } + // Check for integer patterns + else if (pleaf->ptnint = strtol(pattern + offset, &endptr, 0), + pattern + offset != endptr) { + pleaf->ptntype = C2_L_PTINT; + offset = endptr - pattern; + // Make sure we are stopping at the end of a word + if (isalnum(pattern[offset])) + c2_error("Trailing characters after a numeric pattern."); + } + // Check for string patterns + else { + bool raw = false; + char delim = '\0'; + + // String flags + if ('r' == tolower(pattern[offset])) { + raw = true; + ++offset; + C2H_SKIP_SPACES(); + } + + // Check for delimiters + if ('\"' == pattern[offset] || '\'' == pattern[offset]) { + pleaf->ptntype = C2_L_PTSTRING; + delim = pattern[offset]; + ++offset; + } + + if (C2_L_PTSTRING != pleaf->ptntype) + c2_error("Invalid pattern type."); + + // Parse the string now + // We can't determine the length of the pattern, so we use the length + // to the end of the pattern string -- currently escape sequences + // cannot be converted to a string longer than itself. + char *tptnstr = malloc((strlen(pattern + offset) + 1) * sizeof(char)); + char *ptptnstr = tptnstr; + pleaf->ptnstr = tptnstr; + for (; pattern[offset] && delim != pattern[offset]; ++offset) { + // Handle escape sequences if it's not a raw string + if ('\\' == pattern[offset] && !raw) { + switch(pattern[++offset]) { + case '\\': *(ptptnstr++) = '\\'; break; + case '\'': *(ptptnstr++) = '\''; break; + case '\"': *(ptptnstr++) = '\"'; break; + case 'a': *(ptptnstr++) = '\a'; break; + case 'b': *(ptptnstr++) = '\b'; break; + case 'f': *(ptptnstr++) = '\f'; break; + case 'n': *(ptptnstr++) = '\n'; break; + case 'r': *(ptptnstr++) = '\r'; break; + case 't': *(ptptnstr++) = '\t'; break; + case 'v': *(ptptnstr++) = '\v'; break; + case 'o': + case 'x': + { + char *tstr = mstrncpy(pattern + offset + 1, 2); + char *pstr = NULL; + long val = strtol(tstr, &pstr, + ('o' == pattern[offset] ? 8: 16)); + free(tstr); + if (pstr != &tstr[2] || val <= 0) + c2_error("Invalid octal/hex escape sequence."); + assert(val < 256 && val >= 0); + *(ptptnstr++) = val; + offset += 2; + break; + } + default: c2_error("Invalid escape sequence."); + } + } + else { + *(ptptnstr++) = pattern[offset]; + } + } + if (!pattern[offset]) + c2_error("Premature end of pattern string."); + ++offset; + *ptptnstr = '\0'; + pleaf->ptnstr = mstrcpy(tptnstr); + free(tptnstr); + } + + C2H_SKIP_SPACES(); + + if (!pleaf->ptntype) + c2_error("Invalid pattern type."); + + // Check if the type is correct + if (!(((C2_L_TSTRING == pleaf->type + || C2_L_TATOM == pleaf->type) + && C2_L_PTSTRING == pleaf->ptntype) + || ((C2_L_TCARDINAL == pleaf->type + || C2_L_TWINDOW == pleaf->type + || C2_L_TDRAWABLE == pleaf->type) + && C2_L_PTINT == pleaf->ptntype))) + c2_error("Pattern type incompatible with target type."); + + if (C2_L_PTINT == pleaf->ptntype && pleaf->match) + c2_error("Integer/boolean pattern cannot have operator qualifiers."); + + if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase) + c2_error("Integer/boolean pattern cannot have flags."); + + if (C2_L_PTSTRING == pleaf->ptntype + && (C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op + || C2_L_OLT == pleaf->op || C2_L_OLTEQ == pleaf->op)) + c2_error("String pattern cannot have an arithmetic operator."); + + return offset; +} + +/** + * Parse a condition with legacy syntax. + */ +static int +c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult) { + unsigned plen = strlen(pattern + offset); + + if (plen < 4 || ':' != pattern[offset + 1] + || !strchr(pattern + offset + 2, ':')) + c2_error("Legacy parser: Invalid format."); + + // Allocate memory for new leaf + c2_l_t *pleaf = malloc(sizeof(c2_l_t)); + if (!pleaf) + printf_errfq(1, "(): Failed to allocate memory for new leaf."); + presult->isbranch = false; + presult->l = pleaf; + memcpy(pleaf, &leaf_def, sizeof(c2_l_t)); + pleaf->type = C2_L_TSTRING; + pleaf->op = C2_L_OEQ; + pleaf->ptntype = C2_L_PTSTRING; + + // Determine the pattern target +#define TGTFILL(pdefid) \ + (pleaf->predef = pdefid, \ + pleaf->type = C2_PREDEFS[pdefid].type, \ + pleaf->format = C2_PREDEFS[pdefid].format) + switch (pattern[offset]) { + case 'n': TGTFILL(C2_L_PNAME); break; + case 'i': TGTFILL(C2_L_PCLASSI); break; + case 'g': TGTFILL(C2_L_PCLASSG); break; + case 'r': TGTFILL(C2_L_PROLE); break; + default: c2_error("Target \"%c\" invalid.\n", pattern[offset]); + } +#undef TGTFILL + + offset += 2; + + // Determine the match type + switch (pattern[offset]) { + case 'e': pleaf->match = C2_L_MEXACT; break; + case 'a': pleaf->match = C2_L_MCONTAINS; break; + case 's': pleaf->match = C2_L_MSTART; break; + case 'w': pleaf->match = C2_L_MWILDCARD; break; + case 'p': pleaf->match = C2_L_MPCRE; break; + default: c2_error("Type \"%c\" invalid.\n", pattern[offset]); + } + ++offset; + + // Determine the pattern flags + while (':' != pattern[offset]) { + switch (pattern[offset]) { + case 'i': pleaf->match_ignorecase = true; break; + default: c2_error("Flag \"%c\" invalid.", pattern[offset]); + } + ++offset; + } + ++offset; + + // Copy the pattern + pleaf->ptnstr = mstrcpy(pattern + offset); + + if (!c2_l_postprocess(ps, pleaf)) + return -1; + + return offset; +} + +#undef c2_error +#define c2_error(format, ...) { \ + printf_err(format, ## __VA_ARGS__); \ + return false; } + +/** + * Do postprocessing on a condition leaf. + */ +static bool +c2_l_postprocess(session_t *ps, c2_l_t *pleaf) { + // Give a pattern type to a leaf with exists operator, if needed + if (C2_L_OEXISTS == pleaf->op && !pleaf->ptntype) { + pleaf->ptntype = + (C2_L_TSTRING == pleaf->type ? C2_L_PTSTRING: C2_L_PTINT); + } + + // Get target atom if it's not a predefined one + if (!pleaf->predef) { + pleaf->tgtatom = get_atom(ps, pleaf->tgt); + if (!pleaf->tgtatom) + c2_error("Failed to get atom for target \"%s\".", pleaf->tgt); + } + + // Insert target Atom into atom track list + if (pleaf->tgtatom) { + bool found = false; + for (latom_t *platom = ps->track_atom_lst; platom; + platom = platom->next) { + if (pleaf->tgtatom == platom->atom) { + found = true; + break; + } + } + if (!found) { + latom_t *pnew = malloc(sizeof(latom_t)); + if (!pnew) + printf_errfq(1, "(): Failed to allocate memory for new track atom."); + pnew->next = ps->track_atom_lst; + pnew->atom = pleaf->tgtatom; + ps->track_atom_lst = pnew; + } + } + + // Enable specific tracking options in compton if needed by the condition + // TODO: Add track_leader + if (pleaf->predef) { + switch (pleaf->predef) { + case C2_L_PFOCUSED: ps->o.track_focus = true; break; + case C2_L_PNAME: + case C2_L_PCLASSG: + case C2_L_PCLASSI: + case C2_L_PROLE: ps->o.track_wdata = true; break; + default: break; + } + } + + // Warn about lower case characters in target name + if (!pleaf->predef) { + for (const char *pc = pleaf->tgt; *pc; ++pc) { + if (islower(*pc)) { + printf_errf("(): Warning: Lowercase character in target name \"%s\".", pleaf->tgt); + break; + } + } + } + + // PCRE patterns + if (C2_L_PTSTRING == pleaf->ptntype && C2_L_MPCRE == pleaf->match) { +#ifdef CONFIG_REGEX_PCRE + const char *error = NULL; + int erroffset = 0; + int options = 0; + + // Ignore case flag + if (pleaf->match_ignorecase) + options |= PCRE_CASELESS; + + // Compile PCRE expression + pleaf->regex_pcre = pcre_compile(pleaf->ptnstr, options, + &error, &erroffset, NULL); + if (!pleaf->regex_pcre) + c2_error("Pattern \"%s\": PCRE regular expression parsing failed on " + "offset %d: %s", pleaf->ptnstr, erroffset, error); +#ifdef CONFIG_REGEX_PCRE_JIT + pleaf->regex_pcre_extra = pcre_study(pleaf->regex_pcre, + PCRE_STUDY_JIT_COMPILE, &error); + if (!pleaf->regex_pcre_extra) { + printf("Pattern \"%s\": PCRE regular expression study failed: %s", + pleaf->ptnstr, error); + } +#endif + + // Free the target string + // free(pleaf->tgt); + // pleaf->tgt = NULL; +#else + c2_error("PCRE regular expression support not compiled in."); +#endif + } + + return true; +} +/** + * Free a condition tree. + */ +static void +c2_free(c2_ptr_t p) { + // For a branch element + if (p.isbranch) { + c2_b_t * const pbranch = p.b; + + if (!pbranch) + return; + + c2_free(pbranch->opr1); + c2_free(pbranch->opr2); + free(pbranch); + } + // For a leaf element + else { + c2_l_t * const pleaf = p.l; + + if (!pleaf) + return; + + free(pleaf->tgt); + free(pleaf->ptnstr); +#ifdef CONFIG_REGEX_PCRE + pcre_free(pleaf->regex_pcre); + LPCRE_FREE_STUDY(pleaf->regex_pcre_extra); +#endif + free(pleaf); + } +} + +/** + * Free a condition tree in c2_lptr_t. + */ +c2_lptr_t * +c2_free_lptr(c2_lptr_t *lp) { + if (!lp) + return NULL; + + c2_lptr_t *pnext = lp->next; + c2_free(lp->ptr); + free(lp); + + return pnext; +} + +/** + * Get a string representation of a rule target. + */ +static const char * +c2h_dump_str_tgt(const c2_l_t *pleaf) { + if (pleaf->predef) + return C2_PREDEFS[pleaf->predef].name; + else + return pleaf->tgt; +} + +/** + * Get a string representation of a target. + */ +static const char * +c2h_dump_str_type(const c2_l_t *pleaf) { + switch (pleaf->type) { + case C2_L_TWINDOW: return "w"; + case C2_L_TDRAWABLE: return "d"; + case C2_L_TCARDINAL: return "c"; + case C2_L_TSTRING: return "s"; + case C2_L_TATOM: return "a"; + case C2_L_TUNDEFINED: break; + } + + return NULL; +} + +/** + * Dump a condition tree. + */ +static void +c2_dump_raw(c2_ptr_t p) { + // For a branch + if (p.isbranch) { + const c2_b_t * const pbranch = p.b; + + if (!pbranch) + return; + + if (pbranch->neg) + putchar('!'); + + printf("("); + c2_dump_raw(pbranch->opr1); + + switch (pbranch->op) { + case C2_B_OAND: printf(" && "); break; + case C2_B_OOR: printf(" || "); break; + case C2_B_OXOR: printf(" XOR "); break; + default: assert(0); break; + } + + c2_dump_raw(pbranch->opr2); + printf(")"); + } + // For a leaf + else { + const c2_l_t * const pleaf = p.l; + + if (!pleaf) + return; + + if (C2_L_OEXISTS == pleaf->op && pleaf->neg) + putchar('!'); + + // Print target name, type, and format + { + printf("%s", c2h_dump_str_tgt(pleaf)); + if (pleaf->tgt_onframe) + putchar('@'); + if (pleaf->index >= 0) + printf("[%d]", pleaf->index); + printf(":%d%s", pleaf->format, c2h_dump_str_type(pleaf)); + } + + // Print operator + putchar(' '); + + if (C2_L_OEXISTS != pleaf->op && pleaf->neg) + putchar('!'); + + switch (pleaf->match) { + case C2_L_MEXACT: break; + case C2_L_MCONTAINS: putchar('*'); break; + case C2_L_MSTART: putchar('^'); break; + case C2_L_MPCRE: putchar('~'); break; + case C2_L_MWILDCARD: putchar('%'); break; + } + + if (pleaf->match_ignorecase) + putchar('?'); + + switch (pleaf->op) { + case C2_L_OEXISTS: break; + case C2_L_OEQ: fputs("=", stdout); break; + case C2_L_OGT: fputs(">", stdout); break; + case C2_L_OGTEQ: fputs(">=", stdout); break; + case C2_L_OLT: fputs("<", stdout); break; + case C2_L_OLTEQ: fputs("<=", stdout); break; + } + + if (C2_L_OEXISTS == pleaf->op) + return; + + // Print pattern + putchar(' '); + switch (pleaf->ptntype) { + case C2_L_PTINT: + printf("%ld", pleaf->ptnint); + break; + case C2_L_PTSTRING: + // TODO: Escape string before printing out? + printf("\"%s\"", pleaf->ptnstr); + break; + default: + assert(0); + break; + } + } +} + +/** + * Get the type atom of a condition. + */ +static Atom +c2_get_atom_type(const c2_l_t *pleaf) { + switch (pleaf->type) { + case C2_L_TCARDINAL: + return XA_CARDINAL; + case C2_L_TWINDOW: + return XA_WINDOW; + case C2_L_TSTRING: + return XA_STRING; + case C2_L_TATOM: + return XA_ATOM; + case C2_L_TDRAWABLE: + return XA_DRAWABLE; + default: + assert(0); + break; + } + + assert(0); + return AnyPropertyType; +} + +/** + * Match a window against a single leaf window condition. + * + * For internal use. + */ +static inline void +c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, + bool *pres, bool *perr) { + assert(pleaf); + + const Window wid = (pleaf->tgt_onframe ? w->client_win: w->id); + + // Return if wid is missing + if (!pleaf->predef && !wid) + return; + + const int idx = (pleaf->index < 0 ? 0: pleaf->index); + + switch (pleaf->ptntype) { + // Deal with integer patterns + case C2_L_PTINT: + { + long tgt = 0; + + // Get the value + // A predefined target + if (pleaf->predef) { + *perr = false; + switch (pleaf->predef) { + case C2_L_PID: tgt = wid; break; + case C2_L_POVREDIR: tgt = w->a.override_redirect; break; + case C2_L_PFOCUSED: tgt = w->focused_real; break; + case C2_L_PWMWIN: tgt = w->wmwin; break; + case C2_L_PCLIENT: tgt = w->client_win; break; + case C2_L_PLEADER: tgt = w->leader; break; + default: *perr = true; assert(0); break; + } + } + // A raw window property + else { + winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom, + idx, 1L, c2_get_atom_type(pleaf), pleaf->format); + if (prop.nitems) { + *perr = false; + tgt = winprop_get_int(prop); + } + free_winprop(&prop); + } + + if (*perr) + return; + + // Do comparison + switch (pleaf->op) { + case C2_L_OEXISTS: + *pres = (pleaf->predef ? tgt: true); + break; + case C2_L_OEQ: *pres = (tgt == pleaf->ptnint); break; + case C2_L_OGT: *pres = (tgt > pleaf->ptnint); break; + case C2_L_OGTEQ: *pres = (tgt >= pleaf->ptnint); break; + case C2_L_OLT: *pres = (tgt < pleaf->ptnint); break; + case C2_L_OLTEQ: *pres = (tgt <= pleaf->ptnint); break; + default: *perr = true; assert(0); break; + } + } + break; + // String patterns + case C2_L_PTSTRING: + { + const char *tgt = NULL; + char *tgt_free = NULL; + + // A predefined target + if (pleaf->predef) { + switch (pleaf->predef) { + case C2_L_PWINDOWTYPE: tgt = WINTYPES[w->window_type]; + break; + case C2_L_PNAME: tgt = w->name; break; + case C2_L_PCLASSG: tgt = w->class_general; break; + case C2_L_PCLASSI: tgt = w->class_instance; break; + case C2_L_PROLE: tgt = w->role; break; + default: assert(0); break; + } + } + // If it's an atom type property, convert atom to string + else if (C2_L_TATOM == pleaf->type) { + winprop_t prop = wid_get_prop_adv(ps, wid, pleaf->tgtatom, + idx, 1L, c2_get_atom_type(pleaf), pleaf->format); + Atom atom = winprop_get_int(prop); + if (atom) { + tgt_free = XGetAtomName(ps->dpy, atom); + } + if (tgt_free) { + tgt = tgt_free; + } + free_winprop(&prop); + } + // Otherwise, just fetch the string list + else { + char **strlst = NULL; + int nstr; + if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, + &nstr) && nstr > idx) { + tgt_free = mstrcpy(strlst[idx]); + tgt = tgt_free; + } + if (strlst) + XFreeStringList(strlst); + } + + if (tgt) { + *perr = false; + } + else { + return; + } + + // Actual matching + switch (pleaf->op) { + case C2_L_OEXISTS: + *pres = true; + break; + case C2_L_OEQ: + switch (pleaf->match) { + case C2_L_MEXACT: + if (pleaf->match_ignorecase) + *pres = !strcasecmp(tgt, pleaf->ptnstr); + else + *pres = !strcmp(tgt, pleaf->ptnstr); + break; + case C2_L_MCONTAINS: + if (pleaf->match_ignorecase) + *pres = strcasestr(tgt, pleaf->ptnstr); + else + *pres = strstr(tgt, pleaf->ptnstr); + break; + case C2_L_MSTART: + if (pleaf->match_ignorecase) + *pres = !strncasecmp(tgt, pleaf->ptnstr, + strlen(pleaf->ptnstr)); + else + *pres = !strncmp(tgt, pleaf->ptnstr, + strlen(pleaf->ptnstr)); + break; + case C2_L_MWILDCARD: + { + int flags = 0; + if (pleaf->match_ignorecase) + flags |= FNM_CASEFOLD; + *pres = !fnmatch(pleaf->ptnstr, tgt, flags); + } + break; + case C2_L_MPCRE: +#ifdef CONFIG_REGEX_PCRE + *pres = (pcre_exec(pleaf->regex_pcre, + pleaf->regex_pcre_extra, + tgt, strlen(tgt), 0, 0, NULL, 0) >= 0); +#else + assert(0); +#endif + break; + } + break; + default: + *perr = true; + assert(0); + } + + // Free the string after usage, if necessary + if (tgt_free) { + if (C2_L_TATOM == pleaf->type) + XFree(tgt_free); + else + free(tgt_free); + } + } + break; + default: + assert(0); + break; + } +} + +/** + * Match a window against a single window condition. + * + * @return true if matched, false otherwise. + */ +static bool +c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { + bool result = false; + bool error = true; + + // Handle a branch + if (cond.isbranch) { + const c2_b_t *pb = cond.b; + + if (!pb) + return false; + + error = false; + + switch (pb->op) { + case C2_B_OAND: + result = (c2_match_once(ps, w, pb->opr1) + && c2_match_once(ps, w, pb->opr2)); + break; + case C2_B_OOR: + result = (c2_match_once(ps, w, pb->opr1) + || c2_match_once(ps, w, pb->opr2)); + break; + case C2_B_OXOR: + result = (c2_match_once(ps, w, pb->opr1) + != c2_match_once(ps, w, pb->opr2)); + break; + default: + error = true; + assert(0); + } + +#ifdef DEBUG_WINMATCH + printf_dbgf("(%#010lx): branch: result = %d, pattern = ", w->id, result); + c2_dump(cond); +#endif + } + // Handle a leaf + else { + const c2_l_t *pleaf = cond.l; + + if (!pleaf) + return false; + + c2_match_once_leaf(ps, w, pleaf, &result, &error); + + // For EXISTS operator, no errors are fatal + if (C2_L_OEXISTS == pleaf->op && error) { + result = false; + error = false; + } + +#ifdef DEBUG_WINMATCH + printf_dbgf("(%#010lx): leaf: result = %d, error = %d, " + "client = %#010lx, pattern = ", + w->id, result, error, w->client_win); + c2_dump(cond); +#endif + } + + // Postprocess the result + if (error) + result = false; + + if (cond.isbranch ? cond.b->neg: cond.l->neg) + result = !result; + + return result; +} + +/** + * Match a window against a condition linked list. + * + * @param cache a place to cache the last matched condition + * @return true if matched, false otherwise. + */ +bool +c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, + const c2_lptr_t **cache) { + // Check if the cached entry matches firstly + if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) + return true; + + // Then go through the whole linked list + for (; condlst; condlst = condlst->next) { + if (c2_match_once(ps, w, condlst->ptr)) { + if (cache) + *cache = condlst; + return true; + } + } + + return false; +} + diff --git a/c2.h b/c2.h new file mode 100644 index 000000000..000c68448 --- /dev/null +++ b/c2.h @@ -0,0 +1,326 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "common.h" + +#include +#include + +// libpcre +#ifdef CONFIG_REGEX_PCRE +#include + +// For compatiblity with opr1 = p1; + p.b->opr2 = p2; + p.b->op = op; + + return p; +} + +/** + * Get the precedence value of a condition branch operator. + */ +static inline int +c2h_b_opp(c2_b_op_t op) { + switch (op) { + case C2_B_OAND: return 2; + case C2_B_OOR: return 1; + case C2_B_OXOR: return 1; + default: break; + } + + assert(0); + return 0; +} + +/** + * Compare precedence of two condition branch operators. + * + * Associativity is left-to-right, forever. + * + * @return positive number if op1 > op2, 0 if op1 == op2 in precedence, + * negative number otherwise + */ +static inline int +c2h_b_opcmp(c2_b_op_t op1, c2_b_op_t op2) { + return c2h_b_opp(op1) - c2h_b_opp(op2); +} + +static int +c2_parse_grp(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult, int level); + +static int +c2_parse_target(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); + +static int +c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult); + +static int +c2_parse_pattern(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); + +static int +c2_parse_legacy(session_t *ps, const char *pattern, int offset, c2_ptr_t *presult); + +static bool +c2_l_postprocess(session_t *ps, c2_l_t *pleaf); + +static void +c2_free(c2_ptr_t p); + +/** + * Wrapper of c2_free(). + */ +static inline void +c2_freep(c2_ptr_t *pp) { + if (pp) { + c2_free(*pp); + c2_ptr_reset(pp); + } +} + +static const char * +c2h_dump_str_tgt(const c2_l_t *pleaf); + +static const char * +c2h_dump_str_type(const c2_l_t *pleaf); + +static void +c2_dump_raw(c2_ptr_t p); + +/** + * Wrapper of c2_dump_raw(). + */ +static inline void +c2_dump(c2_ptr_t p) { + c2_dump_raw(p); + printf("\n"); + fflush(stdout); +} + +static Atom +c2_get_atom_type(const c2_l_t *pleaf); + +static bool +c2_match_once(session_t *ps, win *w, const c2_ptr_t cond); + diff --git a/common.h b/common.h index 004d9553b..bc057aa8d 100644 --- a/common.h +++ b/common.h @@ -76,20 +76,6 @@ #include #include -// libpcre -#ifdef CONFIG_REGEX_PCRE -#include - -// For compatiblity with @@ -257,18 +243,6 @@ enum wincond_type { #define CONDF_IGNORECASE 0x0001 -typedef struct _wincond { - enum wincond_target target; - enum wincond_type type; - char *pattern; -#ifdef CONFIG_REGEX_PCRE - pcre *regex_pcre; - pcre_extra *regex_pcre_extra; -#endif - int16_t flags; - struct _wincond *next; -} wincond_t; - /// VSync modes. typedef enum { VSYNC_NONE, @@ -297,13 +271,13 @@ struct _timeout_t; struct _win; -#ifdef CONFIG_C2 typedef struct _c2_lptr c2_lptr_t; -#endif /// Structure representing all options. typedef struct { // === General === + /// The configuration file we used. + char *config_file; /// The display name we used. NULL means we are using the value of the /// DISPLAY environment variable. char *display; @@ -350,7 +324,7 @@ typedef struct { double shadow_opacity; bool clear_shadow; /// Shadow blacklist. A linked list of conditions. - wincond_t *shadow_blacklist; + c2_lptr_t *shadow_blacklist; /// Whether bounding-shaped window should be ignored. bool shadow_ignore_shaped; /// Whether to respect _COMPTON_SHADOW. @@ -368,7 +342,7 @@ typedef struct { /// Whether to disable fading on window open/close. bool no_fading_openclose; /// Fading blacklist. A linked list of conditions. - wincond_t *fade_blacklist; + c2_lptr_t *fade_blacklist; // === Opacity === /// Default opacity for specific window types @@ -404,7 +378,7 @@ typedef struct { /// based on window opacity. bool inactive_dim_fixed; /// Conditions of windows to have inverted colors. - wincond_t *invert_color_list; + c2_lptr_t *invert_color_list; // === Focus related === /// Consider windows of specific types to be always focused. @@ -412,7 +386,7 @@ typedef struct { /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. bool use_ewmh_active_win; /// A list of windows always to be considered focused. - wincond_t *focus_blacklist; + c2_lptr_t *focus_blacklist; /// Whether to do window grouping with WM_TRANSIENT_FOR. bool detect_transient; /// Whether to do window grouping with WM_CLIENT_LEADER. @@ -736,10 +710,10 @@ typedef struct _win { char *class_general; /// WM_WINDOW_ROLE value of the window. char *role; - wincond_t *cache_sblst; - wincond_t *cache_fblst; - wincond_t *cache_fcblst; - wincond_t *cache_ivclst; + const c2_lptr_t *cache_sblst; + const c2_lptr_t *cache_fblst; + const c2_lptr_t *cache_fcblst; + const c2_lptr_t *cache_ivclst; // Opacity-related members /// Current window opacity. @@ -1056,10 +1030,13 @@ print_timestamp(session_t *ps) { /** * Allocate the space and copy a string. */ -static inline char * __attribute__((const)) +static inline char * mstrcpy(const char *src) { char *str = malloc(sizeof(char) * (strlen(src) + 1)); + if (!str) + printf_errfq(1, "(): Failed to allocate memory."); + strcpy(str, src); return str; @@ -1068,10 +1045,13 @@ mstrcpy(const char *src) { /** * Allocate the space and copy a string. */ -static inline char * __attribute__((const)) +static inline char * mstrncpy(const char *src, unsigned len) { char *str = malloc(sizeof(char) * (len + 1)); + if (!str) + printf_errfq(1, "(): Failed to allocate memory."); + strncpy(str, src, len); str[len] = '\0'; @@ -1479,7 +1459,7 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val); ///@{ c2_lptr_t * -c2_parse(session_t *ps, c2_lptr_t **pcondlst, char *pattern); +c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern); c2_lptr_t * c2_free_lptr(c2_lptr_t *lp); diff --git a/compton.c b/compton.c index 814bf4a3b..cbb5e7b61 100644 --- a/compton.c +++ b/compton.c @@ -567,7 +567,7 @@ wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, if (Success == XGetWindowProperty(ps->dpy, w, atom, offset, length, False, rtype, &type, &format, &nitems, &after, &data) && nitems && (AnyPropertyType == type || type == rtype) - && (!format || format == rformat) + && (!rformat || format == rformat) && (8 == format || 16 == format || 32 == format)) { return (winprop_t) { .data.p8 = data, @@ -629,244 +629,20 @@ win_rounded_corners(session_t *ps, win *w) { XFree(rects); } -/** - * Match a window against a single window condition. - * - * @return true if matched, false otherwise. - */ -static bool -win_match_once(win *w, const wincond_t *cond) { - const char *target; - bool matched = false; - -#ifdef DEBUG_WINMATCH - printf("win_match_once(%#010lx \"%s\"): cond = %p", w->id, w->name, - cond); -#endif - - if (InputOnly == w->a.class) { -#ifdef DEBUG_WINMATCH - printf(": InputOnly\n"); -#endif - return false; - } - - // Determine the target - target = NULL; - switch (cond->target) { - case CONDTGT_NAME: - target = w->name; - break; - case CONDTGT_CLASSI: - target = w->class_instance; - break; - case CONDTGT_CLASSG: - target = w->class_general; - break; - case CONDTGT_ROLE: - target = w->role; - break; - } - - if (!target) { -#ifdef DEBUG_WINMATCH - printf(": Target not found\n"); -#endif - return false; - } - - // Determine pattern type and match - switch (cond->type) { - case CONDTP_EXACT: - if (cond->flags & CONDF_IGNORECASE) - matched = !strcasecmp(target, cond->pattern); - else - matched = !strcmp(target, cond->pattern); - break; - case CONDTP_ANYWHERE: - if (cond->flags & CONDF_IGNORECASE) - matched = strcasestr(target, cond->pattern); - else - matched = strstr(target, cond->pattern); - break; - case CONDTP_FROMSTART: - if (cond->flags & CONDF_IGNORECASE) - matched = !strncasecmp(target, cond->pattern, - strlen(cond->pattern)); - else - matched = !strncmp(target, cond->pattern, - strlen(cond->pattern)); - break; - case CONDTP_WILDCARD: - { - int flags = 0; - if (cond->flags & CONDF_IGNORECASE) - flags = FNM_CASEFOLD; - matched = !fnmatch(cond->pattern, target, flags); - } - break; - case CONDTP_REGEX_PCRE: -#ifdef CONFIG_REGEX_PCRE - matched = (pcre_exec(cond->regex_pcre, cond->regex_pcre_extra, - target, strlen(target), 0, 0, NULL, 0) >= 0); -#endif - break; - } - -#ifdef DEBUG_WINMATCH - printf(", matched = %d\n", matched); -#endif - - return matched; -} - -/** - * Match a window against a condition linked list. - * - * @param cache a place to cache the last matched condition - * @return true if matched, false otherwise. - */ -static bool -win_match(win *w, wincond_t *condlst, wincond_t **cache) { - // Check if the cached entry matches firstly - if (cache && *cache && win_match_once(w, *cache)) - return true; - - // Then go through the whole linked list - for (; condlst; condlst = condlst->next) { - if (win_match_once(w, condlst)) { - if (cache) - *cache = condlst; - return true; - } - } - - return false; -} - /** * Add a pattern to a condition linked list. */ static bool -condlst_add(wincond_t **pcondlst, const char *pattern) { +condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) { if (!pattern) return false; - unsigned plen = strlen(pattern); - wincond_t *cond; - const char *pos; - - if (plen < 4 || ':' != pattern[1] || !strchr(pattern + 2, ':')) { - printf("Pattern \"%s\": Format invalid.\n", pattern); - return false; - } - - // Allocate memory for the new condition - cond = malloc(sizeof(wincond_t)); - - // Determine the pattern target - switch (pattern[0]) { - case 'n': - cond->target = CONDTGT_NAME; - break; - case 'i': - cond->target = CONDTGT_CLASSI; - break; - case 'g': - cond->target = CONDTGT_CLASSG; - break; - case 'r': - cond->target = CONDTGT_ROLE; - break; - default: - printf("Pattern \"%s\": Target \"%c\" invalid.\n", - pattern, pattern[0]); - free(cond); - return false; - } - - // Determine the pattern type - switch (pattern[2]) { - case 'e': - cond->type = CONDTP_EXACT; - break; - case 'a': - cond->type = CONDTP_ANYWHERE; - break; - case 's': - cond->type = CONDTP_FROMSTART; - break; - case 'w': - cond->type = CONDTP_WILDCARD; - break; -#ifdef CONFIG_REGEX_PCRE - case 'p': - cond->type = CONDTP_REGEX_PCRE; - break; -#endif - default: - printf("Pattern \"%s\": Type \"%c\" invalid.\n", - pattern, pattern[2]); - free(cond); - return false; - } - - // Determine the pattern flags - pos = &pattern[3]; - cond->flags = 0; - while (':' != *pos) { - switch (*pos) { - case 'i': - cond->flags |= CONDF_IGNORECASE; - break; - default: - printf("Pattern \"%s\": Flag \"%c\" invalid.\n", - pattern, *pos); - break; - } - ++pos; - } - - // Copy the pattern - ++pos; - cond->pattern = NULL; -#ifdef CONFIG_REGEX_PCRE - cond->regex_pcre = NULL; - cond->regex_pcre_extra = NULL; -#endif - if (CONDTP_REGEX_PCRE == cond->type) { -#ifdef CONFIG_REGEX_PCRE - const char *error = NULL; - int erroffset = 0; - int options = 0; - - if (cond->flags & CONDF_IGNORECASE) - options |= PCRE_CASELESS; - - cond->regex_pcre = pcre_compile(pos, options, &error, &erroffset, - NULL); - if (!cond->regex_pcre) { - printf("Pattern \"%s\": PCRE regular expression parsing failed on " - "offset %d: %s\n", pattern, erroffset, error); - free(cond); - return false; - } -#ifdef CONFIG_REGEX_PCRE_JIT - cond->regex_pcre_extra = pcre_study(cond->regex_pcre, PCRE_STUDY_JIT_COMPILE, &error); - if (!cond->regex_pcre_extra) { - printf("Pattern \"%s\": PCRE regular expression study failed: %s", - pattern, error); - } -#endif +#ifdef CONFIG_C2 + if (!c2_parse(ps, pcondlst, pattern)) + exit(1); +#else + printf_errfq(1, "(): Condition support not compiled in."); #endif - } - else { - cond->pattern = mstrcpy(pos); - } - - // Insert it into the linked list - cond->next = *pcondlst; - *pcondlst = cond; return true; } @@ -2313,7 +2089,7 @@ win_determine_shadow(session_t *ps, win *w) { w->shadow = (UNSET == w->shadow_force ? (ps->o.wintype_shadow[w->window_type] - && !win_match(w, ps->o.shadow_blacklist, &w->cache_sblst) + && !win_match(ps, w, ps->o.shadow_blacklist, &w->cache_sblst) && !(ps->o.shadow_ignore_shaped && w->bounding_shaped && !w->rounded_corners) && !(ps->o.respect_prop_shadow && 0 == w->prop_shadow)) @@ -2347,7 +2123,7 @@ win_determine_invert_color(session_t *ps, win *w) { if (UNSET != w->invert_color_force) w->invert_color = w->invert_color_force; else - w->invert_color = win_match(w, ps->o.invert_color_list, &w->cache_ivclst); + w->invert_color = win_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst); if (w->invert_color != invert_color_old) add_damage_win(ps, w); @@ -2361,13 +2137,15 @@ win_on_wtype_change(session_t *ps, win *w) { win_determine_shadow(ps, w); win_determine_fade(ps, w); win_update_focused(ps, w); + if (ps->o.invert_color_list) + win_determine_invert_color(ps, w); } /** * Function to be called on window data changes. */ static void -win_on_wdata_change(session_t *ps, win *w) { +win_on_factor_change(session_t *ps, win *w) { if (ps->o.shadow_blacklist) win_determine_shadow(ps, w); if (ps->o.fade_blacklist) @@ -2481,9 +2259,11 @@ win_mark_client(session_t *ps, win *w, Window client) { win_get_name(ps, w); win_get_class(ps, w); win_get_role(ps, w); - win_on_wdata_change(ps, w); } + // Update everything related to conditions + win_on_factor_change(ps, w); + // Update window focus state win_update_focused(ps, w); } @@ -3048,7 +2828,7 @@ win_update_focused(session_t *ps, win *w) { || (ps->o.mark_wmwin_focused && w->wmwin) || (ps->o.mark_ovredir_focused && w->id == w->client_win && !w->wmwin) - || win_match(w, ps->o.focus_blacklist, &w->cache_fcblst)) + || win_match(ps, w, ps->o.focus_blacklist, &w->cache_fcblst)) w->focused = true; // If window grouping detection is enabled, mark the window active if @@ -3102,6 +2882,9 @@ win_set_focused(session_t *ps, win *w, bool focused) { else { win_update_focused(ps, w); } + + // Update everything related to conditions + win_on_factor_change(ps, w); } } /** @@ -3153,6 +2936,9 @@ win_set_leader(session_t *ps, win *w, Window nleader) { else { win_update_focused(ps, w); } + + // Update everything related to conditions + win_on_factor_change(ps, w); } } @@ -3746,7 +3532,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { && (ps->atom_name == ev->atom || ps->atom_name_ewmh == ev->atom)) { win *w = find_toplevel(ps, ev->window); if (w && 1 == win_get_name(ps, w)) { - win_on_wdata_change(ps, w); + win_on_factor_change(ps, w); } } @@ -3755,7 +3541,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { win *w = find_toplevel(ps, ev->window); if (w) { win_get_class(ps, w); - win_on_wdata_change(ps, w); + win_on_factor_change(ps, w); } } @@ -3763,7 +3549,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (ps->o.track_wdata && ps->atom_role == ev->atom) { win *w = find_toplevel(ps, ev->window); if (w && 1 == win_get_role(ps, w)) { - win_on_wdata_change(ps, w); + win_on_factor_change(ps, w); } } @@ -3790,7 +3576,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { if (!w) w = find_toplevel(ps, ev->window); if (w) - win_on_wdata_change(ps, w); + win_on_factor_change(ps, w); break; } } @@ -4095,24 +3881,7 @@ usage(void) { " inverted color. Resource-hogging, and is not well tested.\n" "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" - " man page for more details.\n" - "\n" - "Format of a condition:\n" - "\n" - " condition = :[]:\n" - "\n" - " is one of \"n\" (window name), \"i\" (window class\n" - " instance), \"g\" (window general class), and \"r\"\n" - " (window role).\n" - "\n" - " is one of \"e\" (exact match), \"a\" (match anywhere),\n" - " \"s\" (match from start), \"w\" (wildcard), and \"p\" (PCRE\n" - " regular expressions, if compiled with the support).\n" - "\n" - " could be a series of flags. Currently the only defined\n" - " flag is \"i\" (ignore case).\n" - "\n" - " is the actual pattern string.\n"; + " man page for more details.\n"; fputs(usage_text , stderr); exit(1); @@ -4270,8 +4039,6 @@ open_config_file(char *cpath, char **ppath) { f = fopen(path, "r"); if (f && ppath) *ppath = path; - else - free(path); return f; } @@ -4356,7 +4123,7 @@ parse_vsync(session_t *ps, const char *optarg) { * Parse a condition list in configuration file. */ static void -parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, +parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, const char *name) { config_setting_t *setting = config_lookup(pcfg, name); if (setting) { @@ -4364,12 +4131,12 @@ parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) { - condlst_add(pcondlst, config_setting_get_string_elem(setting, i)); + condlst_add(ps, pcondlst, config_setting_get_string_elem(setting, i)); } } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { - condlst_add(pcondlst, config_setting_get_string(setting)); + condlst_add(ps, pcondlst, config_setting_get_string(setting)); } } } @@ -4378,7 +4145,7 @@ parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, * Parse a configuration file from default location. */ static void -parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { +parse_config(session_t *ps, struct options_tmp *pcfgtmp) { char *path = NULL; FILE *f; config_t cfg; @@ -4386,10 +4153,14 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { double dval = 0.0; const char *sval = NULL; - f = open_config_file(cpath, &path); + f = open_config_file(ps->o.config_file, &path); if (!f) { - if (cpath) - printf_errfq(1, "(): Failed to read the specified configuration file."); + if (ps->o.config_file) { + printf_errfq(1, "(): Failed to read configuration file \"%s\".", + ps->o.config_file); + free(ps->o.config_file); + ps->o.config_file = NULL; + } return; } @@ -4417,7 +4188,10 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { } config_set_auto_convert(&cfg, 1); - free(path); + if (path != ps->o.config_file) { + free(ps->o.config_file); + ps->o.config_file = path; + } // Get options from the configuration file. We don't do range checking // right now. It will be done later @@ -4512,11 +4286,11 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { lcfg_lookup_bool(&cfg, "detect-client-leader", &ps->o.detect_client_leader); // --shadow-exclude - parse_cfg_condlst(&cfg, &ps->o.shadow_blacklist, "shadow-exclude"); + parse_cfg_condlst(ps, &cfg, &ps->o.shadow_blacklist, "shadow-exclude"); // --focus-exclude - parse_cfg_condlst(&cfg, &ps->o.focus_blacklist, "focus-exclude"); + parse_cfg_condlst(ps, &cfg, &ps->o.focus_blacklist, "focus-exclude"); // --invert-color-include - parse_cfg_condlst(&cfg, &ps->o.invert_color_list, "invert-color-include"); + parse_cfg_condlst(ps, &cfg, &ps->o.invert_color_list, "invert-color-include"); // --blur-background lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background); // --blur-background-frame @@ -4554,7 +4328,7 @@ parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp) { * Process arguments and configuration files. */ static void -get_cfg(session_t *ps, int argc, char *const *argv) { +get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb"; const static struct option longopts[] = { { "help", no_argument, NULL, 'h' }, @@ -4595,14 +4369,33 @@ get_cfg(session_t *ps, int argc, char *const *argv) { { NULL, 0, NULL, 0 }, }; + int o = 0, longopt_idx = -1, i = 0; + + if (first_pass) { + // Pre-parse the commandline arguments to check for --config and invalid + // switches + // Must reset optind to 0 here in case we reread the commandline + // arguments + optind = 1; + while (-1 != + (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { + if (256 == o) + ps->o.config_file = mstrcpy(optarg); + else if ('d' == o) + ps->o.display = mstrcpy(optarg); + else if ('?' == o || ':' == o) + usage(); + } + + return; + } + struct options_tmp cfgtmp = { .no_dock_shadow = false, .no_dnd_shadow = false, .menu_opacity = 1.0, }; bool shadow_enable = false, fading_enable = false; - int o, longopt_idx, i; - char *config_file = NULL; char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); for (i = 0; i < NUM_WINTYPES; ++i) { @@ -4611,21 +4404,8 @@ get_cfg(session_t *ps, int argc, char *const *argv) { ps->o.wintype_opacity[i] = 1.0; } - // Pre-parse the commandline arguments to check for --config and invalid - // switches - // Must reset optind to 0 here in case we reread the commandline - // arguments - optind = 1; - while (-1 != - (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { - if (256 == o) - config_file = mstrcpy(optarg); - else if ('?' == o || ':' == o) - usage(); - } - #ifdef CONFIG_LIBCONFIG - parse_config(ps, config_file, &cfgtmp); + parse_config(ps, &cfgtmp); #endif // Parse commandline arguments. Range checking will be done later. @@ -4643,7 +4423,6 @@ get_cfg(session_t *ps, int argc, char *const *argv) { usage(); break; case 'd': - ps->o.display = mstrcpy(optarg); break; case 'D': ps->o.fade_delta = atoi(optarg); @@ -4732,7 +4511,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { break; case 263: // --shadow-exclude - condlst_add(&ps->o.shadow_blacklist, optarg); + condlst_add(ps, &ps->o.shadow_blacklist, optarg); break; case 264: // --mark-ovredir-focused @@ -4796,7 +4575,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { break; case 279: // --focus-exclude - condlst_add(&ps->o.focus_blacklist, optarg); + condlst_add(ps, &ps->o.focus_blacklist, optarg); break; case 280: // --inactive-dim-fixed @@ -4832,7 +4611,7 @@ get_cfg(session_t *ps, int argc, char *const *argv) { break; case 288: // --invert-color-include - condlst_add(&ps->o.invert_color_list, optarg); + condlst_add(ps, &ps->o.invert_color_list, optarg); break; default: usage(); @@ -4884,11 +4663,6 @@ get_cfg(session_t *ps, int argc, char *const *argv) { ps->o.track_focus = true; } - // Determine whether we need to track window name and class - if (ps->o.shadow_blacklist || ps->o.fade_blacklist - || ps->o.focus_blacklist || ps->o.invert_color_list) - ps->o.track_wdata = true; - // Determine whether we track window grouping if (ps->o.detect_transient || ps->o.detect_client_leader) { ps->o.track_leader = true; @@ -5702,7 +5476,8 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.wintype_focus[WINTYPE_NORMAL] = false; ps->o.wintype_focus[WINTYPE_UTILITY] = false; - get_cfg(ps, argc, argv); + // First pass + get_cfg(ps, argc, argv, true); // Inherit old Display if possible, primarily for resource leak checking if (ps_old && ps_old->dpy) @@ -5712,11 +5487,13 @@ session_init(session_t *ps_old, int argc, char **argv) { if (!ps->dpy) { ps->dpy = XOpenDisplay(ps->o.display); if (!ps->dpy) { - fprintf(stderr, "Can't open display\n"); - exit(1); + printf_errfq(1, "(): Can't open display."); } } + // Second pass + get_cfg(ps, argc, argv, false); + XSetErrorHandler(error); if (ps->o.synchronize) { XSynchronize(ps->dpy, 1); @@ -5973,11 +5750,24 @@ session_destroy(session_t *ps) { ps->alpha_picts = NULL; } +#ifdef CONFIG_C2 // Free blacklists free_wincondlst(&ps->o.shadow_blacklist); free_wincondlst(&ps->o.fade_blacklist); free_wincondlst(&ps->o.focus_blacklist); free_wincondlst(&ps->o.invert_color_list); +#endif + + // Free tracked atom list + { + latom_t *next = NULL; + for (latom_t *this = ps->track_atom_lst; this; this = next) { + next = this->next; + free(this); + } + + ps->track_atom_lst = NULL; + } // Free ignore linked list { @@ -6025,6 +5815,7 @@ session_destroy(session_t *ps) { free(ps->gaussian_map); free(ps->o.display); free(ps->o.logpath); + free(ps->o.config_file); free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); @@ -6164,11 +5955,6 @@ main(int argc, char **argv) { printf_errf("Failed to create new session."); return 1; } -#ifdef DEBUG_C2 - // c2_parse(ps_g, NULL, "name ~= \"master\""); - // c2_parse(ps_g, NULL, "n:e:Notification"); - c2_parse(ps_g, NULL, "(WM_NAME:16s = 'Notification' || class_g = 'fox') && class_g = 'fox'"); -#endif session_run(ps_g); ps_old = ps_g; session_destroy(ps_g); diff --git a/compton.h b/compton.h index 7670424e9..75d517ec8 100644 --- a/compton.h +++ b/compton.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #ifdef CONFIG_VSYNC_DRM @@ -165,37 +164,16 @@ free_damage(session_t *ps, Damage *p) { } } +#ifdef CONFIG_C2 /** - * Destroy a wincond_t. + * Destroy a condition list. */ -inline static void -free_wincond(wincond_t *cond) { - if (cond->pattern) - free(cond->pattern); -#ifdef CONFIG_REGEX_PCRE - if (cond->regex_pcre_extra) - LPCRE_FREE_STUDY(cond->regex_pcre_extra); - if (cond->regex_pcre) - pcre_free(cond->regex_pcre); -#endif - free(cond); -} - -/** - * Destroy a linked list of wincond_t. - */ -inline static void -free_wincondlst(wincond_t **cond_lst) { - wincond_t *next = NULL; - - for (wincond_t *cond = *cond_lst; cond; cond = next) { - next = cond->next; - - free_wincond(cond); - } - - *cond_lst = NULL; +static inline void +free_wincondlst(c2_lptr_t **pcondlst) { + while ((*pcondlst = c2_free_lptr(*pcondlst))) + continue; } +#endif /** * Destroy all resources in a struct _win. @@ -396,14 +374,20 @@ win_is_fullscreen(session_t *ps, const win *w) { static void win_rounded_corners(session_t *ps, win *w); -static bool -win_match_once(win *w, const wincond_t *cond); - -static bool -win_match(win *w, wincond_t *condlst, wincond_t * *cache); +/** + * Wrapper of c2_match(). + */ +static inline bool +win_match(session_t *ps, win *w, c2_lptr_t *condlst, const c2_lptr_t **cache) { +#ifdef CONFIG_C2 + return c2_match(ps, w, condlst, cache); +#else + return false; +#endif +} static bool -condlst_add(wincond_t **pcondlst, const char *pattern); +condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern); static long determine_evmask(session_t *ps, Window wid, win_evmode_t mode); @@ -594,7 +578,7 @@ static void win_on_wtype_change(session_t *ps, win *w); static void -win_on_wdata_change(session_t *ps, win *w); +win_on_factor_change(session_t *ps, win *w); static void win_upd_run(session_t *ps, win *w, win_upd_t *pupd); @@ -858,15 +842,15 @@ static FILE * open_config_file(char *cpath, char **path); static void -parse_cfg_condlst(const config_t *pcfg, wincond_t **pcondlst, +parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, const char *name); static void -parse_config(session_t *ps, char *cpath, struct options_tmp *pcfgtmp); +parse_config(session_t *ps, struct options_tmp *pcfgtmp); #endif static void -get_cfg(session_t *ps, int argc, char *const *argv); +get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass); static void init_atoms(session_t *ps); diff --git a/dbus.c b/dbus.c index a86ed174d..44f1369d7 100644 --- a/dbus.c +++ b/dbus.c @@ -859,6 +859,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { return true; } + cdbus_m_opts_get_do(config_file, cdbus_reply_string); cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool); cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool); cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); From 370fc4599795a4a67cd69625ad83715e4e360046 Mon Sep 17 00:00:00 2001 From: hasufell Date: Mon, 28 Jan 2013 17:08:34 +0100 Subject: [PATCH 106/178] hide disabled features from help output --- compton.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/compton.c b/compton.c index 814bf4a3b..f93b950e9 100644 --- a/compton.c +++ b/compton.c @@ -4036,13 +4036,17 @@ usage(void) { " Specify refresh rate of the screen. If not specified or 0, compton\n" " will try detecting this with X RandR extension.\n" "--vsync vsync-method\n" - " Set VSync method. There are 2 VSync methods currently available:\n" + " Set VSync method. There are up to 2 VSync methods currently available\n" + " depending on your compile time settings:\n" " none = No VSync\n" +#ifdef CONFIG_VSYNC_DRM " drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n" " drivers. Experimental.\n" +#endif +#ifdef CONFIG_VSYNC_OPENGL " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" " work on some drivers. Experimental.\n" - " (Note some VSync methods may not be enabled at compile time.)\n" +#endif "--alpha-step val\n" " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" " 0.03.\n" @@ -4093,9 +4097,11 @@ usage(void) { "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" +#ifdef CONFIG_DBUS "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" " man page for more details.\n" +#endif "\n" "Format of a condition:\n" "\n" @@ -4106,9 +4112,12 @@ usage(void) { " (window role).\n" "\n" " is one of \"e\" (exact match), \"a\" (match anywhere),\n" - " \"s\" (match from start), \"w\" (wildcard), and \"p\" (PCRE\n" - " regular expressions, if compiled with the support).\n" - "\n" + " \"s\" (match from start), \"w\" (wildcard)" +#ifdef CONFIG_REGEX_PCRE + " and \"p\" (PCRE\n" + " regular expressions)." +#endif + "\n\n" " could be a series of flags. Currently the only defined\n" " flag is \"i\" (ignore case).\n" "\n" From e5264dd40304d434585bffa0933ca5fe285035aa Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 29 Jan 2013 09:57:04 +0800 Subject: [PATCH 107/178] Improvement: Improve color inversion performance & Validate pixmap - Try to improve the performance of color inversion by applying clipping region during color inversion. (#75) - Validate pixmap on window unmap/destruction. Probably slightly helpful for #52. - Change the design of unmap_win() and destroy_win(), a bit. - Add warning message to help messages about features disabled at compile time, instead of dropping their description completely. (#85) - Silence some warnings. Code clean-up. --- compton.c | 99 ++++++++++++++++++++++++++++++++++++++++++------------- compton.h | 9 +++-- 2 files changed, 83 insertions(+), 25 deletions(-) diff --git a/compton.c b/compton.c index b81273cdd..d42ca3e28 100644 --- a/compton.c +++ b/compton.c @@ -629,6 +629,32 @@ win_rounded_corners(session_t *ps, win *w) { XFree(rects); } +/** + * Validate pixmap of a window, and destroy pixmap and picture if invalid. + */ +static void +win_validate_pixmap(session_t *ps, win *w) { + if (!w->pixmap) + return; + + // Detect whether the pixmap is valid with XGetGeometry. Well, maybe there + // are better ways. + bool invalid = false; + { + Window rroot = None; + int rx = 0, ry = 0; + unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0; + invalid = (!XGetGeometry(ps->dpy, w->pixmap, &rroot, &rx, &ry, + &rwid, &rhei, &rborder, &rdepth) || !rwid || !rhei); + } + + // Destroy pixmap and picture, if invalid + if (invalid) { + free_pixmap(ps, &w->pixmap); + free_picture(ps, &w->picture); + } +} + /** * Add a pattern to a condition linked list. */ @@ -678,7 +704,7 @@ determine_evmask(session_t *ps, Window wid, win_evmode_t mode) { * Find out the WM frame of a client window by querying X. * * @param ps current session - * @param w window ID + * @param wid window ID * @return struct _win object of the found window, NULL if not found */ static win * @@ -1360,7 +1386,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, * Paint a window itself and dim it if asked. */ static inline void -win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { +win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint) { int x = w->a.x; int y = w->a.y; int wid = w->widthb; @@ -1374,6 +1400,14 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer) { if (w->invert_color) { Picture newpict = win_build_picture(ps, w, w->a.visual); if (newpict) { + // Apply clipping region to save some CPU + if (reg_paint) { + XserverRegion reg = copy_region(ps, reg_paint); + XFixesTranslateRegion(ps->dpy, reg, -x, -y); + XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg); + free_region(ps, ®); + } + XRenderComposite(ps->dpy, PictOpSrc, pict, None, newpict, 0, 0, 0, 0, 0, 0, wid, hei); XRenderComposite(ps->dpy, PictOpDifference, ps->white_picture, None, @@ -1602,7 +1636,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } // Painting the window - win_paint_win(ps, w, ps->tgt_buffer); + win_paint_win(ps, w, ps->tgt_buffer, reg_paint); } check_fade_fin(ps, w); @@ -1706,12 +1740,10 @@ repair_win(session_t *ps, win *w) { static wintype_t wid_get_prop_wintype(session_t *ps, Window wid) { - int i; - set_ignore_next(ps); winprop_t prop = wid_get_prop(ps, wid, ps->atom_win_type, 32L, XA_ATOM, 32); - for (i = 0; i < prop.nitems; ++i) { + for (unsigned i = 0; i < prop.nitems; ++i) { for (wintype_t j = 1; j < NUM_WINTYPES; ++j) { if (ps->atoms_wintypes[j] == (Atom) prop.data.p32[i]) { free_winprop(&prop); @@ -1862,9 +1894,7 @@ unmap_callback(session_t *ps, win *w) { } static void -unmap_win(session_t *ps, Window id) { - win *w = find_win(ps, id); - +unmap_win(session_t *ps, win *w) { if (!w || IsUnmapped == w->a.map_state) return; // Set focus out @@ -1877,8 +1907,12 @@ unmap_win(session_t *ps, Window id) { set_fade_callback(ps, w, unmap_callback, false); if (ps->o.no_fading_openclose) { w->in_openclose = true; - win_determine_fade(ps, w); } + win_determine_fade(ps, w); + + // Validate pixmap if we have to do fading + if (w->fade) + win_validate_pixmap(ps, w); // don't care about properties anymore win_ev_stop(ps, w); @@ -2612,6 +2646,8 @@ configure_win(session_t *ps, XConfigureEvent *ce) { } } + // override_redirect flag cannot be changed after window creation, as far + // as I know, so there's no point to re-match windows here. w->a.override_redirect = ce->override_redirect; } @@ -2662,10 +2698,11 @@ destroy_win(session_t *ps, Window id) { win *w = find_win(ps, id); if (w) { + unmap_win(ps, w); + w->destroyed = true; - // Fading out the window - w->flags |= WFLAG_OPCT_CHANGE; + // Set fading callback set_fade_callback(ps, w, destroy_callback, false); #ifdef CONFIG_DBUS @@ -3364,7 +3401,10 @@ ev_map_notify(session_t *ps, XMapEvent *ev) { inline static void ev_unmap_notify(session_t *ps, XUnmapEvent *ev) { - unmap_win(ps, ev->window); + win *w = find_win(ps, ev->window); + + if (w) + unmap_win(ps, w); } inline static void @@ -3745,6 +3785,8 @@ ev_handle(session_t *ps, XEvent *ev) { */ static void usage(void) { +#define WARNING_DISABLED " (DISABLED AT COMPILE TIME)" +#define WARNING const static char *usage_text = "compton (" COMPTON_VERSION ")\n" "usage: compton [options]\n" @@ -3825,14 +3867,22 @@ usage(void) { " Set VSync method. There are up to 2 VSync methods currently available\n" " depending on your compile time settings:\n" " none = No VSync\n" -#ifdef CONFIG_VSYNC_DRM +#undef WARNING +#ifndef CONFIG_VSYNC_DRM +#define WARNING WARNING_DISABLED +#else +#define WARNING +#endif " drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n" - " drivers. Experimental.\n" + " drivers. Experimental." WARNING "\n" +#undef WARNING +#ifndef CONFIG_VSYNC_OPENGL +#define WARNING WARNING_DISABLED +#else +#define WARNING #endif -#ifdef CONFIG_VSYNC_OPENGL " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" - " work on some drivers. Experimental.\n" -#endif + " work on some drivers. Experimental." WARNING"\n" "--alpha-step val\n" " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" " 0.03.\n" @@ -3883,13 +3933,18 @@ usage(void) { "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" -#ifdef CONFIG_DBUS +#undef WARNING +#ifndef CONFIG_DBUS +#define WARNING WARNING_DISABLED +#else +#define WARNING +#endif "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" - " man page for more details.\n" -#endif - ; + " man page for more details." WARNING "\n"; fputs(usage_text , stderr); +#undef WARNING +#undef WARNING_DISABLED exit(1); } diff --git a/compton.h b/compton.h index 75d517ec8..eac944127 100644 --- a/compton.h +++ b/compton.h @@ -284,7 +284,7 @@ win_ev_stop(session_t *ps, win *w) { /** * Get the children of a window. * - * @param session_t current session + * @param ps current session * @param w window to check * @param children [out] an array of child window IDs * @param nchildren [out] number of children @@ -374,6 +374,9 @@ win_is_fullscreen(session_t *ps, const win *w) { static void win_rounded_corners(session_t *ps, win *w); +static void +win_validate_pixmap(session_t *ps, win *w); + /** * Wrapper of c2_match(). */ @@ -492,7 +495,7 @@ static void unmap_callback(session_t *ps, win *w); static void -unmap_win(session_t *ps, Window id); +unmap_win(session_t *ps, win *w); static opacity_t wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); @@ -900,7 +903,7 @@ redir_stop(session_t *ps); static inline time_ms_t timeout_get_newrun(const timeout_t *ptmout) { - return ptmout->firstrun + (max_l((ptmout->lastrun + (long) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + ptmout->interval * (1 - TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval) + 1) * ptmout->interval; + return ptmout->firstrun + (max_l((ptmout->lastrun + (time_ms_t) (ptmout->interval * TIMEOUT_RUN_TOLERANCE) - ptmout->firstrun) / ptmout->interval, (ptmout->lastrun + (time_ms_t) (ptmout->interval * (1 - TIMEOUT_RUN_TOLERANCE)) - ptmout->firstrun) / ptmout->interval) + 1) * ptmout->interval; } static time_ms_t From 646390149399214ca725b93328e04e71361caa02 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 30 Jan 2013 13:41:08 +0800 Subject: [PATCH 108/178] Improvement #7: Add GLX_OML_sync_control VSync support - Add "vsync-oml" VSync method, using GLX_OML_sync_control. Untested, because it's not supported by my driver. - Unredirect ps->reg_win, because DRI wiki says it's related to the behavior of OpenGL VSync extensions. - Add glFlush() and glXWaitX() calls, in hope they are slightly helpful for VSync. - Change a few functions to make error handling more graceful. Make some errors fatal. Code clean-up. - Add unused function make_text_prop(). --- common.h | 12 ++- compton.c | 292 +++++++++++++++++++++++++++++++++--------------------- compton.h | 33 +++++- 3 files changed, 219 insertions(+), 118 deletions(-) diff --git a/common.h b/common.h index bc057aa8d..4bc57bb8f 100644 --- a/common.h +++ b/common.h @@ -248,12 +248,16 @@ typedef enum { VSYNC_NONE, VSYNC_DRM, VSYNC_OPENGL, + VSYNC_OPENGL_OML, NUM_VSYNC, } vsync_t; #ifdef CONFIG_VSYNC_OPENGL typedef int (*f_WaitVideoSync) (int, int, unsigned *); typedef int (*f_GetVideoSync) (unsigned *); + +typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc); +typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc); #endif typedef struct { @@ -536,9 +540,13 @@ typedef struct { /// GLX context. GLXContext glx_context; /// Pointer to glXGetVideoSyncSGI function. - f_GetVideoSync glx_get_video_sync; + f_GetVideoSync glXGetVideoSyncSGI; /// Pointer to glXWaitVideoSyncSGI function. - f_WaitVideoSync glx_wait_video_sync; + f_WaitVideoSync glXWaitVideoSyncSGI; + /// Pointer to glXGetSyncValuesOML function. + f_GetSyncValuesOML glXGetSyncValuesOML; + /// Pointer to glXWaitForMscOML function. + f_WaitForMscOML glXWaitForMscOML; #endif // === X extension related === diff --git a/compton.c b/compton.c index d42ca3e28..2389337e4 100644 --- a/compton.c +++ b/compton.c @@ -31,11 +31,30 @@ const char * const WINTYPES[NUM_WINTYPES] = { "dnd", }; -/// Names of VSync modes +/// Names of VSync modes. const char * const VSYNC_STRS[NUM_VSYNC] = { - "none", // VSYNC_NONE - "drm", // VSYNC_DRM - "opengl", // VSYNC_OPENGL + "none", // VSYNC_NONE + "drm", // VSYNC_DRM + "opengl", // VSYNC_OPENGL + "opengl-oml", // VSYNC_OPENGL_OML +}; + +/// Function pointers to init VSync modes. +static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = { + [VSYNC_DRM ] = vsync_drm_init, + [VSYNC_OPENGL ] = vsync_opengl_init, + [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init, +}; + +/// Function pointers to wait for VSync. +static int (* const (VSYNC_FUNCS_WAIT[NUM_VSYNC]))(session_t *ps) = { +#ifdef CONFIG_VSYNC_DRM + [VSYNC_DRM ] = vsync_drm_wait, +#endif +#ifdef CONFIG_VSYNC_OPENGL + [VSYNC_OPENGL ] = vsync_opengl_wait, + [VSYNC_OPENGL_OML ] = vsync_opengl_oml_wait, +#endif }; /// Names of root window properties that could point to a pixmap of @@ -1651,10 +1670,16 @@ paint_all(session_t *ps, XserverRegion region, win *t) { if (!ps->o.dbe) XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, None); - if (VSYNC_NONE != ps->o.vsync) { + if (ps->o.vsync) { // Make sure all previous requests are processed to achieve best // effect XSync(ps->dpy, False); +#ifdef CONFIG_VSYNC_OPENGL + if (ps->glx_context) { + glFlush(); + glXWaitX(); + } +#endif } // Wait for VBlank. We could do it aggressively (send the painting @@ -1685,6 +1710,13 @@ paint_all(session_t *ps, XserverRegion region, win *t) { XFlush(ps->dpy); +#ifdef CONFIG_VSYNC_OPENGL + if (ps->glx_context) { + glFlush(); + glXWaitX(); + } +#endif + #ifdef DEBUG_REPAINT print_timestamp(ps); struct timespec now = get_time_timespec(); @@ -3952,84 +3984,85 @@ usage(void) { /** * Register a window as symbol, and initialize GLX context if wanted. */ -static void -register_cm(session_t *ps, bool want_glxct) { - Atom a; - char *buf; +static bool +register_cm(session_t *ps, bool glx) { + XVisualInfo *pvi = NULL; #ifdef CONFIG_VSYNC_OPENGL // Create a window with the wanted GLX visual - if (want_glxct) { - XVisualInfo *pvi = NULL; - bool ret = false; + if (glx) { // Get visual for the window int attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; pvi = glXChooseVisual(ps->dpy, ps->scr, attribs); if (!pvi) { - fprintf(stderr, "register_cm(): Failed to choose visual required " - "by fake OpenGL VSync window. OpenGL VSync turned off.\n"); + printf_errf("(): Failed to choose GLX visual."); + return false; } - else { - // Create the window - XSetWindowAttributes swa = { - .colormap = XCreateColormap(ps->dpy, ps->root, pvi->visual, AllocNone), - .border_pixel = 0, - }; - - pvi->screen = ps->scr; - ps->reg_win = XCreateWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, pvi->depth, - InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa); - if (!ps->reg_win) - fprintf(stderr, "register_cm(): Failed to create window required " - "by fake OpenGL VSync. OpenGL VSync turned off.\n"); - else { - // Get GLX context - ps->glx_context = glXCreateContext(ps->dpy, pvi, None, GL_TRUE); - if (!ps->glx_context) { - fprintf(stderr, "register_cm(): Failed to get GLX context. " - "OpenGL VSync turned off.\n"); - ps->o.vsync = VSYNC_NONE; - } - else { - // Attach GLX context - if (!(ret = glXMakeCurrent(ps->dpy, ps->reg_win, ps->glx_context))) - fprintf(stderr, "register_cm(): Failed to attach GLX context." - " OpenGL VSync turned off.\n"); - } - } - } - if (pvi) - XFree(pvi); + // Create the window + XSetWindowAttributes swa = { + .colormap = XCreateColormap(ps->dpy, ps->root, pvi->visual, AllocNone), + .border_pixel = 0, + }; - if (!ret) - ps->o.vsync = VSYNC_NONE; + pvi->screen = ps->scr; + ps->reg_win = XCreateWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, pvi->depth, + InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa); } + // Otherwise, create a simple window + else #endif - - if (!ps->reg_win) + { ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, None, None); + } - Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", - NULL, 0, NULL, NULL, NULL); + if (!ps->reg_win) { + printf_errf("(): Failed to create window."); + return false; + } - unsigned len = strlen(REGISTER_PROP) + 2; - int s = ps->scr; +#ifdef CONFIG_VSYNC_OPENGL + if (glx) { + // Get GLX context + ps->glx_context = glXCreateContext(ps->dpy, pvi, None, GL_TRUE); + if (!ps->glx_context) { + printf_errf("(): Failed to get GLX context."); + return false; + } - while (s >= 10) { - ++len; - s /= 10; + // Attach GLX context + if (!glXMakeCurrent(ps->dpy, ps->reg_win, ps->glx_context)) { + printf_errf("(): Failed to attach GLX context."); + return false; + } } +#endif - buf = malloc(len); - snprintf(buf, len, REGISTER_PROP"%d", ps->scr); + if (pvi) + XFree(pvi); - a = get_atom(ps, buf); - free(buf); + Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", + NULL, 0, NULL, NULL, NULL); + + { + unsigned len = strlen(REGISTER_PROP) + 2; + int s = ps->scr; - XSetSelectionOwner(ps->dpy, a, ps->reg_win, 0); + while (s >= 10) { + ++len; + s /= 10; + } + + char *buf = malloc(len); + snprintf(buf, len, REGISTER_PROP "%d", ps->scr); + buf[len - 1] = '\0'; + XSetSelectionOwner(ps->dpy, get_atom(ps, buf), ps->reg_win, 0); + free(buf); + } + + return true; } /** @@ -4873,7 +4906,7 @@ vsync_drm_init(session_t *ps) { #ifdef CONFIG_VSYNC_DRM // Should we always open card0? if ((ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { - fprintf(stderr, "vsync_drm_init(): Failed to open device.\n"); + printf_errf("(): Failed to open device."); return false; } @@ -4882,7 +4915,7 @@ vsync_drm_init(session_t *ps) { return true; #else - fprintf(stderr, "Program not compiled with DRM VSync support.\n"); + printf_errf("(): Program not compiled with DRM VSync support."); return false; #endif } @@ -4927,19 +4960,38 @@ static bool vsync_opengl_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL // Get video sync functions - ps->glx_get_video_sync = (f_GetVideoSync) + ps->glXGetVideoSyncSGI = (f_GetVideoSync) glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI"); - ps->glx_wait_video_sync = (f_WaitVideoSync) + ps->glXWaitVideoSyncSGI = (f_WaitVideoSync) glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI"); - if (!ps->glx_wait_video_sync || !ps->glx_get_video_sync) { - fprintf(stderr, "vsync_opengl_init(): " - "Failed to get glXWait/GetVideoSyncSGI function.\n"); + if (!ps->glXWaitVideoSyncSGI || !ps->glXGetVideoSyncSGI) { + printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function."); return false; } return true; #else - fprintf(stderr, "Program not compiled with OpenGL VSync support.\n"); + printf_errfq(1, "Program not compiled with OpenGL VSync support."); + return false; +#endif +} + +static bool +vsync_opengl_oml_init(session_t *ps) { +#ifdef CONFIG_VSYNC_OPENGL + // Get video sync functions + ps->glXGetSyncValuesOML= (f_GetSyncValuesOML) + glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML"); + ps->glXWaitForMscOML = (f_WaitForMscOML) + glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML"); + if (!ps->glXGetSyncValuesOML || !ps->glXWaitForMscOML) { + printf_errf("(): Failed to get OML_sync_control functions."); + return false; + } + + return true; +#else + printf_errfq(1, "Program not compiled with OpenGL VSync support."); return false; #endif } @@ -4948,13 +5000,31 @@ vsync_opengl_init(session_t *ps) { /** * Wait for next VSync, OpenGL method. */ -static void +static int vsync_opengl_wait(session_t *ps) { - unsigned vblank_count; + unsigned vblank_count = 0; - ps->glx_get_video_sync(&vblank_count); - ps->glx_wait_video_sync(2, (vblank_count + 1) % 2, &vblank_count); + ps->glXGetVideoSyncSGI(&vblank_count); + ps->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count); // I see some code calling glXSwapIntervalSGI(1) afterwards, is it required? + + return 0; +} + +/** + * Wait for next VSync, OpenGL OML method. + * + * https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html + */ +static int +vsync_opengl_oml_wait(session_t *ps) { + int64_t ust = 0, msc = 0, sbc = 0; + + ps->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc); + ps->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, + &ust, &msc, &sbc); + + return 0; } #endif @@ -4963,27 +5033,13 @@ vsync_opengl_wait(session_t *ps) { */ static void vsync_wait(session_t *ps) { - if (VSYNC_NONE == ps->o.vsync) + if (!ps->o.vsync) return; -#ifdef CONFIG_VSYNC_DRM - if (VSYNC_DRM == ps->o.vsync) { - vsync_drm_wait(ps); - return; - } -#endif - -#ifdef CONFIG_VSYNC_OPENGL - if (VSYNC_OPENGL == ps->o.vsync) { - vsync_opengl_wait(ps); - return; - } -#endif + assert(VSYNC_FUNCS_WAIT[ps->o.vsync]); - // This place should not reached! - assert(0); - - return; + if (VSYNC_FUNCS_WAIT[ps->o.vsync]) + VSYNC_FUNCS_WAIT[ps->o.vsync](ps); } /** @@ -5008,15 +5064,16 @@ init_alpha_picts(session_t *ps) { /** * Initialize double buffer. */ -static void +static bool init_dbe(session_t *ps) { if (!(ps->root_dbe = XdbeAllocateBackBufferName(ps->dpy, (ps->o.paint_on_overlay ? ps->overlay: ps->root), XdbeCopied))) { - fprintf(stderr, "Failed to create double buffer. Double buffering " - "turned off.\n"); - ps->o.dbe = false; - return; + printf_errf("(): Failed to create double buffer. Double buffering " + "cannot work."); + return false; } + + return true; } /** @@ -5090,6 +5147,10 @@ redir_start(session_t *ps) { XCompositeRedirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); + // Unredirect reg_win as this may have an effect on VSync: + // < http://dri.freedesktop.org/wiki/CompositeSwap > + XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual); + // Must call XSync() here XSync(ps->dpy, False); @@ -5478,8 +5539,10 @@ session_init(session_t *ps_old, int argc, char **argv) { #ifdef CONFIG_VSYNC_OPENGL .glx_context = None, - .glx_get_video_sync = NULL, - .glx_wait_video_sync = NULL, + .glXGetVideoSyncSGI = NULL, + .glXWaitVideoSyncSGI = NULL, + .glXGetSyncValuesOML = NULL, + .glXWaitForMscOML = NULL, #endif .xfixes_event = 0, @@ -5567,6 +5630,9 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->vis = DefaultVisual(ps->dpy, ps->scr); ps->depth = DefaultDepth(ps->dpy, ps->scr); + bool want_glx = (VSYNC_OPENGL == ps->o.vsync + || VSYNC_OPENGL_OML == ps->o.vsync); + if (!XRenderQueryExtension(ps->dpy, &ps->render_event, &ps->render_error)) { fprintf(stderr, "No render extension\n"); @@ -5609,21 +5675,22 @@ session_init(session_t *ps_old, int argc, char **argv) { if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) ps->randr_exists = true; else - fprintf(stderr, "No XRandR extension, automatic refresh rate " - "detection impossible.\n"); + printf_errf("(): No XRandR extension, automatic refresh rate " + "detection impossible."); } -#ifdef CONFIG_VSYNC_OPENGL // Query X GLX extension - if (VSYNC_OPENGL == ps->o.vsync) { + if (want_glx) { +#ifdef CONFIG_VSYNC_OPENGL if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) ps->glx_exists = true; else { - fprintf(stderr, "No GLX extension, OpenGL VSync impossible.\n"); - ps->o.vsync = VSYNC_NONE; + printf_errfq(1, "(): No GLX extension, OpenGL VSync impossible."); } - } +#else + printf_errfq(1, "(): OpenGL VSync support not compiled in."); #endif + } // Query X DBE extension if (ps->o.dbe) { @@ -5641,23 +5708,24 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } - register_cm(ps, (VSYNC_OPENGL == ps->o.vsync)); + if (!register_cm(ps, want_glx)) + exit(1); // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); - // Initialize DRM/OpenGL VSync - if ((VSYNC_DRM == ps->o.vsync && !vsync_drm_init(ps)) - || (VSYNC_OPENGL == ps->o.vsync && !vsync_opengl_init(ps))) - ps->o.vsync = VSYNC_NONE; + // Initialize VSync + if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync] + && !VSYNC_FUNCS_INIT[ps->o.vsync](ps)) + exit(1); // Overlay must be initialized before double buffer if (ps->o.paint_on_overlay) init_overlay(ps); - if (ps->o.dbe) - init_dbe(ps); + if (ps->o.dbe && !init_dbe(ps)) + exit(1); init_atoms(ps); init_alpha_picts(ps); diff --git a/compton.h b/compton.h index eac944127..ddc17044f 100644 --- a/compton.h +++ b/compton.h @@ -216,6 +216,25 @@ ms_to_tv(int timeout) { }; } +/** + * Create a XTextProperty of a single string. + */ +static inline XTextProperty * +make_text_prop(session_t *ps, char *str) { + XTextProperty *pprop = malloc(sizeof(XTextProperty)); + if (!pprop) + printf_errfq(1, "(): Failed to allocate memory."); + + if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { + if (pprop->value) + XFree(pprop->value); + free(pprop); + pprop = NULL; + } + + return pprop; +} + static void run_fade(session_t *ps, win *w, unsigned steps); @@ -685,8 +704,8 @@ ev_window(session_t *ps, XEvent *ev); static void __attribute__ ((noreturn)) usage(void); -static void -register_cm(session_t *ps, bool want_glxct); +static bool +register_cm(session_t *ps, bool glx); inline static void ev_focus_in(session_t *ps, XFocusChangeEvent *ev); @@ -878,9 +897,15 @@ vsync_drm_wait(session_t *ps); static bool vsync_opengl_init(session_t *ps); +static bool +vsync_opengl_oml_init(session_t *ps); + #ifdef CONFIG_VSYNC_OPENGL -static void +static int vsync_opengl_wait(session_t *ps); + +static int +vsync_opengl_oml_wait(session_t *ps); #endif static void @@ -889,7 +914,7 @@ vsync_wait(session_t *ps); static void init_alpha_picts(session_t *ps); -static void +static bool init_dbe(session_t *ps); static void From d1fb8649a4e5d2ec165bd4226d53703043678eb2 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 31 Jan 2013 22:53:44 +0800 Subject: [PATCH 109/178] Improvement: Change VSync mode with D-Bus & Makefile update & Misc - Add on-the-fly VSync option modification via D-Bus, as requested by kunitoki (#80). Expose parse_vsync(), create vsync_init() and ensure_glx_context(). - Change default value of ps->drm_fd to -1. - Update Makefile. Change the install/uninstall rules and add doc installation, requested by hasufell in #85. - Mark window not damaged in map_win(). It helps in reducing flickering with inverted window color, but I'm not completely sure if it's safe. - Avoid modifying w->invert_color when window is unmapped. - Update documentation. Thanks to hasufell for pointing out. --- common.h | 17 ++++++++ compton.c | 120 ++++++++++++++++++++++++++++-------------------------- compton.h | 33 +++++++++++++++ dbus.c | 19 +++++++++ dbus.h | 4 +- 5 files changed, 135 insertions(+), 58 deletions(-) diff --git a/common.h b/common.h index 4bc57bb8f..995553bae 100644 --- a/common.h +++ b/common.h @@ -1167,6 +1167,20 @@ normalize_d(double d) { return normalize_d_range(d, 0.0, 1.0); } +/** + * Parse a VSync option argument. + */ +static inline bool +parse_vsync(session_t *ps, const char *str) { + for (vsync_t i = 0; i < (sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); ++i) + if (!strcasecmp(str, VSYNC_STRS[i])) { + ps->o.vsync = i; + return true; + } + printf_errf("(\"%s\"): Invalid vsync argument.", str); + return false; +} + timeout_t * timeout_insert(session_t *ps, time_ms_t interval, bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); @@ -1421,6 +1435,9 @@ free_winprop(winprop_t *pprop) { void force_repaint(session_t *ps); +bool +vsync_init(session_t *ps); + #ifdef CONFIG_DBUS /** @name DBus handling */ diff --git a/compton.c b/compton.c index 2389337e4..12dc72ff3 100644 --- a/compton.c +++ b/compton.c @@ -1875,7 +1875,7 @@ map_win(session_t *ps, Window id) { } win_determine_fade(ps, w); - w->damaged = true; + w->damaged = false; /* if any configure events happened while the window was unmapped, then configure @@ -2184,6 +2184,11 @@ win_determine_shadow(session_t *ps, win *w) { */ static void win_determine_invert_color(session_t *ps, win *w) { + // Do not change window invert color state when the window is unmapped, + // unless it comes from w->invert_color_force. + if (UNSET == w->invert_color_force && IsViewable != w->a.map_state) + return; + bool invert_color_old = w->invert_color; if (UNSET != w->invert_color_force) @@ -3896,7 +3901,7 @@ usage(void) { " Specify refresh rate of the screen. If not specified or 0, compton\n" " will try detecting this with X RandR extension.\n" "--vsync vsync-method\n" - " Set VSync method. There are up to 2 VSync methods currently available\n" + " Set VSync method. There are up to 3 VSync methods currently available\n" " depending on your compile time settings:\n" " none = No VSync\n" #undef WARNING @@ -3915,6 +3920,8 @@ usage(void) { #endif " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" " work on some drivers. Experimental." WARNING"\n" + " opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n" + " Only work on some drivers. Experimental." WARNING"\n" "--alpha-step val\n" " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" " 0.03.\n" @@ -3986,6 +3993,8 @@ usage(void) { */ static bool register_cm(session_t *ps, bool glx) { + assert(!ps->reg_win); + XVisualInfo *pvi = NULL; #ifdef CONFIG_VSYNC_OPENGL @@ -4023,6 +4032,10 @@ register_cm(session_t *ps, bool glx) { return false; } + // Unredirect the window if it's redirected, just in case + if (ps->redirected) + XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual); + #ifdef CONFIG_VSYNC_OPENGL if (glx) { // Get GLX context @@ -4197,23 +4210,6 @@ open_config_file(char *cpath, char **ppath) { return NULL; } -/** - * Parse a VSync option argument. - */ -static inline void -parse_vsync(session_t *ps, const char *optarg) { - vsync_t i; - - for (i = 0; i < (sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); ++i) - if (!strcasecmp(optarg, VSYNC_STRS[i])) { - ps->o.vsync = i; - break; - } - if ((sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])) == i) { - printf_errfq(1, "(\"%s\"): Invalid --vsync argument.", optarg); - } -} - /** * Parse a condition list in configuration file. */ @@ -4357,8 +4353,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --refresh-rate lcfg_lookup_int(&cfg, "refresh-rate", &ps->o.refresh_rate); // --vsync - if (config_lookup_string(&cfg, "vsync", &sval)) - parse_vsync(ps, sval); + if (config_lookup_string(&cfg, "vsync", &sval) && !parse_vsync(ps, sval)) + exit(1); // --alpha-step config_lookup_float(&cfg, "alpha-step", &ps->o.alpha_step); // --dbe @@ -4634,7 +4630,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; case 270: // --vsync - parse_vsync(ps, optarg); + if (!parse_vsync(ps, optarg)) + exit(1); break; case 271: // --alpha-step @@ -4905,7 +4902,7 @@ static bool vsync_drm_init(session_t *ps) { #ifdef CONFIG_VSYNC_DRM // Should we always open card0? - if ((ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { + if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) { printf_errf("(): Failed to open device."); return false; } @@ -4959,11 +4956,16 @@ vsync_drm_wait(session_t *ps) { static bool vsync_opengl_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL + if (!ensure_glx_context(ps)) + return false; + // Get video sync functions - ps->glXGetVideoSyncSGI = (f_GetVideoSync) - glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI"); - ps->glXWaitVideoSyncSGI = (f_WaitVideoSync) - glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI"); + if (!ps->glXWaitVideoSyncSGI) + ps->glXGetVideoSyncSGI = (f_GetVideoSync) + glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI"); + if (!ps->glXWaitVideoSyncSGI) + ps->glXWaitVideoSyncSGI = (f_WaitVideoSync) + glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI"); if (!ps->glXWaitVideoSyncSGI || !ps->glXGetVideoSyncSGI) { printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function."); return false; @@ -4971,7 +4973,7 @@ vsync_opengl_init(session_t *ps) { return true; #else - printf_errfq(1, "Program not compiled with OpenGL VSync support."); + printf_errf("(): Program not compiled with OpenGL VSync support."); return false; #endif } @@ -4979,11 +4981,16 @@ vsync_opengl_init(session_t *ps) { static bool vsync_opengl_oml_init(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL + if (!ensure_glx_context(ps)) + return false; + // Get video sync functions - ps->glXGetSyncValuesOML= (f_GetSyncValuesOML) - glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML"); - ps->glXWaitForMscOML = (f_WaitForMscOML) - glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML"); + if (!ps->glXGetSyncValuesOML) + ps->glXGetSyncValuesOML = (f_GetSyncValuesOML) + glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML"); + if (!ps->glXWaitForMscOML) + ps->glXWaitForMscOML = (f_WaitForMscOML) + glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML"); if (!ps->glXGetSyncValuesOML || !ps->glXWaitForMscOML) { printf_errf("(): Failed to get OML_sync_control functions."); return false; @@ -4991,7 +4998,7 @@ vsync_opengl_oml_init(session_t *ps) { return true; #else - printf_errfq(1, "Program not compiled with OpenGL VSync support."); + printf_errf("(): Program not compiled with OpenGL VSync support."); return false; #endif } @@ -5028,6 +5035,20 @@ vsync_opengl_oml_wait(session_t *ps) { } #endif +/** + * Initialize current VSync method. + */ +bool +vsync_init(session_t *ps) { + if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync] + && !VSYNC_FUNCS_INIT[ps->o.vsync](ps)) { + ps->o.vsync = VSYNC_NONE; + return false; + } + else + return true; +} + /** * Wait for next VSync. */ @@ -5534,7 +5555,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .paint_tm_offset = 0L, #ifdef CONFIG_VSYNC_DRM - .drm_fd = 0, + .drm_fd = -1, #endif #ifdef CONFIG_VSYNC_OPENGL @@ -5630,9 +5651,6 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->vis = DefaultVisual(ps->dpy, ps->scr); ps->depth = DefaultDepth(ps->dpy, ps->scr); - bool want_glx = (VSYNC_OPENGL == ps->o.vsync - || VSYNC_OPENGL_OML == ps->o.vsync); - if (!XRenderQueryExtension(ps->dpy, &ps->render_event, &ps->render_error)) { fprintf(stderr, "No render extension\n"); @@ -5679,19 +5697,6 @@ session_init(session_t *ps_old, int argc, char **argv) { "detection impossible."); } - // Query X GLX extension - if (want_glx) { -#ifdef CONFIG_VSYNC_OPENGL - if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) - ps->glx_exists = true; - else { - printf_errfq(1, "(): No GLX extension, OpenGL VSync impossible."); - } -#else - printf_errfq(1, "(): OpenGL VSync support not compiled in."); -#endif - } - // Query X DBE extension if (ps->o.dbe) { int dbe_ver_major = 0, dbe_ver_minor = 0; @@ -5708,16 +5713,12 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } - if (!register_cm(ps, want_glx)) - exit(1); - // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); // Initialize VSync - if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync] - && !VSYNC_FUNCS_INIT[ps->o.vsync](ps)) + if (!vsync_init(ps)) exit(1); // Overlay must be initialized before double buffer @@ -5727,6 +5728,11 @@ session_init(session_t *ps_old, int argc, char **argv) { if (ps->o.dbe && !init_dbe(ps)) exit(1); + // Create registration window + // Must not precede VSync init functions because they may create the window + if (!ps->reg_win && !register_cm(ps, false)) + exit(1); + init_atoms(ps); init_alpha_picts(ps); @@ -5970,9 +5976,9 @@ session_destroy(session_t *ps) { #ifdef CONFIG_VSYNC_DRM // Close file opened for DRM VSync - if (ps->drm_fd) { + if (ps->drm_fd >= 0) { close(ps->drm_fd); - ps->drm_fd = 0; + ps->drm_fd = -1; } #endif diff --git a/compton.h b/compton.h index ddc17044f..b4f5bfd44 100644 --- a/compton.h +++ b/compton.h @@ -707,6 +707,39 @@ usage(void); static bool register_cm(session_t *ps, bool glx); +#ifdef CONFIG_VSYNC_OPENGL +/** + * Ensure we have a GLX context. + */ +static inline bool +ensure_glx_context(session_t *ps) { + if (ps->glx_context) + return true; + + // Check for GLX extension + if (!ps->glx_exists) { + if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) + ps->glx_exists = true; + else { + printf_errf("(): No GLX extension."); + return false; + } + } + + // Create GLX context + if (ps->reg_win) { + XDestroyWindow(ps->dpy, ps->reg_win); + ps->reg_win = None; + } + if (!register_cm(ps, true) || !ps->glx_context) { + printf_errf("(): Failed to acquire GLX context."); + return false; + } + + return true; +} +#endif + inline static void ev_focus_in(session_t *ps, XFocusChangeEvent *ev); diff --git a/dbus.c b/dbus.c index 44f1369d7..39e4cf3ad 100644 --- a/dbus.c +++ b/dbus.c @@ -952,6 +952,25 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { } goto cdbus_process_opts_set_success; } + + // vsync + if (!strcmp("vsync", target)) { + const char * val = NULL; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_STRING, &val)) + return false; + if (!parse_vsync(ps, val)) { + printf_errf("(): " CDBUS_ERROR_BADARG_S, 1, "Value invalid."); + cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1, "Value invalid."); + } + else if (!vsync_init(ps)) { + printf_errf("(): " CDBUS_ERROR_CUSTOM_S, "Failed to initialize specified VSync method."); + cdbus_reply_err(ps, msg, CDBUS_ERROR_CUSTOM, CDBUS_ERROR_CUSTOM_S, "Failed to initialize specified VSync method."); + } + else + goto cdbus_process_opts_set_success; + return true; + } + #undef cdbus_m_opts_set_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); diff --git a/dbus.h b/dbus.h index e69a6ae9b..50770d63c 100644 --- a/dbus.h +++ b/dbus.h @@ -21,13 +21,15 @@ #define CDBUS_ERROR_BADMSG_S "Unrecognized command. Beware compton " \ "cannot make you a sandwich." #define CDBUS_ERROR_BADARG CDBUS_ERROR_PREFIX ".bad_argument" -#define CDBUS_ERROR_BADARG_S "Something wrong in arguments?" +#define CDBUS_ERROR_BADARG_S "Failed to parse argument %d: %s" #define CDBUS_ERROR_BADWIN CDBUS_ERROR_PREFIX ".bad_window" #define CDBUS_ERROR_BADWIN_S "Requested window %#010lx not found." #define CDBUS_ERROR_BADTGT CDBUS_ERROR_PREFIX ".bad_target" #define CDBUS_ERROR_BADTGT_S "Target \"%s\" not found." #define CDBUS_ERROR_FORBIDDEN CDBUS_ERROR_PREFIX ".forbidden" #define CDBUS_ERROR_FORBIDDEN_S "Incorrect password, access denied." +#define CDBUS_ERROR_CUSTOM CDBUS_ERROR_PREFIX ".custom" +#define CDBUS_ERROR_CUSTOM_S "%s" // Window type typedef uint32_t cdbus_window_t; From 71fdda46e615df29ac1b2c8d3d1984a7be3b44fd Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 1 Mar 2013 12:41:16 +0800 Subject: [PATCH 110/178] Bug fix #91: Using pkg-config to find drm.h & OpenGL changes - #91: Use pkg-config to find drm.h to avoid issues on FreeBSD. Thanks to hun7err for pointing out and providing patch. - #89: Add default shadow exclusion rule for notify-osd. Thanks to DanielRS. - Check for abundant positional arguments. - Use paint target window (root window / overlay window) instead of ps->reg_win to create GLXContext. (May have negative effects on OpenGL VSync.) Add new OpenGL helpers functions, to prepare for the new OpenGL backend. - Dump more info of a PropertyNotify with DEBUG_EVENTS. --- common.h | 18 ++++- compton.c | 236 ++++++++++++++++++++++++++++++++++++++++-------------- compton.h | 76 ++++++++++-------- 3 files changed, 234 insertions(+), 96 deletions(-) diff --git a/common.h b/common.h index 995553bae..0d4f5a541 100644 --- a/common.h +++ b/common.h @@ -258,6 +258,14 @@ typedef int (*f_GetVideoSync) (unsigned *); typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc); typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc); + +typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); +typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer); + +struct glx_fbconfig { + GLXFBConfig cfg; + bool y_inverted; +}; #endif typedef struct { @@ -536,7 +544,7 @@ typedef struct { #endif #ifdef CONFIG_VSYNC_OPENGL - // === OpenGL VSync related === + // === OpenGL related === /// GLX context. GLXContext glx_context; /// Pointer to glXGetVideoSyncSGI function. @@ -547,6 +555,14 @@ typedef struct { f_GetSyncValuesOML glXGetSyncValuesOML; /// Pointer to glXWaitForMscOML function. f_WaitForMscOML glXWaitForMscOML; + /// Pointer to glXBindTexImageEXT function. + f_BindTexImageEXT glXBindTexImageEXT; + /// Pointer to glXReleaseTexImageEXT function. + f_ReleaseTexImageEXT glXReleaseTexImageEXT; + /// FBConfig for RGB GLX pixmap. + struct glx_fbconfig *glx_fbconfig_rgb; + /// FBConfig for RGBA GLX pixmap. + struct glx_fbconfig *glx_fbconfig_rgba; #endif // === X extension related === diff --git a/compton.c b/compton.c index 12dc72ff3..edd387823 100644 --- a/compton.c +++ b/compton.c @@ -3542,6 +3542,16 @@ update_ewmh_active_win(session_t *ps) { inline static void ev_property_notify(session_t *ps, XPropertyEvent *ev) { +#ifdef DEBUG_EVENTS + { + // Print out changed atom + char *name = XGetAtomName(ps->dpy, ev->atom); + printf_dbg(" { atom = %s }\n", name); + if (name) + XFree(name); + } +#endif + if (ps->root == ev->window) { if (ps->o.track_focus && ps->o.use_ewmh_active_win && ps->atom_ewmh_active_win == ev->atom) { @@ -3992,40 +4002,11 @@ usage(void) { * Register a window as symbol, and initialize GLX context if wanted. */ static bool -register_cm(session_t *ps, bool glx) { +register_cm(session_t *ps) { assert(!ps->reg_win); - XVisualInfo *pvi = NULL; - -#ifdef CONFIG_VSYNC_OPENGL - // Create a window with the wanted GLX visual - if (glx) { - // Get visual for the window - int attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; - pvi = glXChooseVisual(ps->dpy, ps->scr, attribs); - - if (!pvi) { - printf_errf("(): Failed to choose GLX visual."); - return false; - } - - // Create the window - XSetWindowAttributes swa = { - .colormap = XCreateColormap(ps->dpy, ps->root, pvi->visual, AllocNone), - .border_pixel = 0, - }; - - pvi->screen = ps->scr; - ps->reg_win = XCreateWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, pvi->depth, - InputOutput, pvi->visual, CWBorderPixel | CWColormap, &swa); - } - // Otherwise, create a simple window - else -#endif - { - ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, + ps->reg_win = XCreateSimpleWindow(ps->dpy, ps->root, 0, 0, 1, 1, 0, None, None); - } if (!ps->reg_win) { printf_errf("(): Failed to create window."); @@ -4036,26 +4017,6 @@ register_cm(session_t *ps, bool glx) { if (ps->redirected) XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual); -#ifdef CONFIG_VSYNC_OPENGL - if (glx) { - // Get GLX context - ps->glx_context = glXCreateContext(ps->dpy, pvi, None, GL_TRUE); - if (!ps->glx_context) { - printf_errf("(): Failed to get GLX context."); - return false; - } - - // Attach GLX context - if (!glXMakeCurrent(ps->dpy, ps->reg_win, ps->glx_context)) { - printf_errf("(): Failed to attach GLX context."); - return false; - } - } -#endif - - if (pvi) - XFree(pvi); - Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", NULL, 0, NULL, NULL, NULL); @@ -4478,6 +4439,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { usage(); } + // Check for abundant positional arguments + if (optind < argc) + printf_errfq(1, "(): compton doesn't accept positional arguments."); + return; } @@ -4893,6 +4858,155 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv) { } } +#ifdef CONFIG_VSYNC_OPENGL +/** + * Get a GLX FBConfig. + */ +static inline bool +opengl_update_fbconfig(session_t *ps, bool alpha, struct glx_fbconfig **ppcfg) { + const int FBCONFIG_ATTRS[] = { + (alpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT: GLX_BIND_TO_TEXTURE_RGB_EXT), True, + GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, + GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + GLX_DOUBLEBUFFER, True, + GLX_Y_INVERTED_EXT, GLX_DONT_CARE, + None + }; + + if (*ppcfg) { + free(*ppcfg); + *ppcfg = NULL; + } + + int count = 0; + GLXFBConfig *cfgs = glXChooseFBConfig(ps->dpy, ps->scr, FBCONFIG_ATTRS, &count); + if (!count || !cfgs) { + if (cfgs) + XFree(cfgs); + return false; + }; + + *ppcfg = malloc(sizeof(struct glx_fbconfig)); + (*ppcfg)->cfg = cfgs[0]; + + { + int inverted = 0; + glXGetFBConfigAttrib(ps->dpy, (*ppcfg)->cfg, GLX_Y_INVERTED_EXT, &inverted); + (*ppcfg)->y_inverted = inverted; + } + + XFree(cfgs); + + return true; +} + +/** + * Initialize OpenGL. + */ +static bool +opengl_init(session_t *ps, bool need_render) { + // Check for GLX extension + if (!ps->glx_exists) { + if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) + ps->glx_exists = true; + else { + printf_errf("(): No GLX extension."); + goto opengl_init_err; + } + } + + // Ensure GLX_EXT_texture_from_pixmap exists + if (need_render && !opengl_hasext(ps, "GLX_EXT_texture_from_pixmap")) { + goto opengl_init_err; + } + + { + // Get XVisualInfo + XVisualInfo *pvis = NULL; + { + XVisualInfo vreq = { .visualid = XVisualIDFromVisual(ps->vis) }; + int nitems = 0; + pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems); + } + + if (!pvis) { + printf_errf("(): Failed to acquire XVisualInfo for current visual."); + goto opengl_init_err; + } + + // Ensure the visual is double-buffered + if (need_render) { + int value = 0; + glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value); + if (!value) { + XFree(pvis); + printf_errf("(): Default GLX visual is not double-buffered."); + goto opengl_init_err; + } + } + + // Get GLX context + ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); + XFree(pvis); + } + + if (!ps->glx_context) { + printf_errf("(): Failed to get GLX context."); + goto opengl_init_err; + } + + // Attach GLX context + if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { + printf_errf("(): Failed to attach GLX context."); + goto opengl_init_err; + } + + // Acquire function addresses + if (need_render) { + ps->glXBindTexImageEXT = (f_BindTexImageEXT) + glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT"); + ps->glXReleaseTexImageEXT = (f_ReleaseTexImageEXT) + glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT"); + if (!ps->glXBindTexImageEXT || !ps->glXReleaseTexImageEXT) { + printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); + goto opengl_init_err; + } + } + + // Acquire FBConfigs + if (need_render + && (!(ps->glx_fbconfig_rgb || opengl_update_fbconfig(ps, false, &ps->glx_fbconfig_rgb)) + || !(ps->glx_fbconfig_rgba || opengl_update_fbconfig(ps, true, &ps->glx_fbconfig_rgba)))) { + printf_errf("(): Failed to acquire GLX fbconfig."); + goto opengl_init_err; + } + + if (need_render) { + glViewport(0, 0, ps->root_width, ps->root_height); + } + + return true; + +opengl_init_err: + opengl_destroy(ps); + + return false; +} + +static void +opengl_destroy(session_t *ps) { + if (ps->glx_context) { + glXDestroyContext(ps->dpy, ps->glx_context); + ps->glx_context = None; + } + + free(ps->glx_fbconfig_rgb); + ps->glx_fbconfig_rgb = NULL; + free(ps->glx_fbconfig_rgba); + ps->glx_fbconfig_rgba = NULL; +} +#endif + /** * Initialize DRM VSync. * @@ -4962,10 +5076,10 @@ vsync_opengl_init(session_t *ps) { // Get video sync functions if (!ps->glXWaitVideoSyncSGI) ps->glXGetVideoSyncSGI = (f_GetVideoSync) - glXGetProcAddress ((const GLubyte *) "glXGetVideoSyncSGI"); + glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI"); if (!ps->glXWaitVideoSyncSGI) ps->glXWaitVideoSyncSGI = (f_WaitVideoSync) - glXGetProcAddress ((const GLubyte *) "glXWaitVideoSyncSGI"); + glXGetProcAddress((const GLubyte *) "glXWaitVideoSyncSGI"); if (!ps->glXWaitVideoSyncSGI || !ps->glXGetVideoSyncSGI) { printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function."); return false; @@ -5168,9 +5282,14 @@ redir_start(session_t *ps) { XCompositeRedirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); - // Unredirect reg_win as this may have an effect on VSync: + /* + // Unredirect GL context window as this may have an effect on VSync: // < http://dri.freedesktop.org/wiki/CompositeSwap > XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual); + if (ps->o.paint_on_overlay && ps->overlay) { + XCompositeUnredirectWindow(ps->dpy, ps->overlay, + CompositeRedirectManual); + } */ // Must call XSync() here XSync(ps->dpy, False); @@ -5729,8 +5848,7 @@ session_init(session_t *ps_old, int argc, char **argv) { exit(1); // Create registration window - // Must not precede VSync init functions because they may create the window - if (!ps->reg_win && !register_cm(ps, false)) + if (!ps->reg_win && !register_cm(ps)) exit(1); init_atoms(ps); @@ -5961,11 +6079,9 @@ session_destroy(session_t *ps) { XDestroyWindow(ps->dpy, ps->reg_win); ps->reg_win = None; } + #ifdef CONFIG_VSYNC_OPENGL - if (ps->glx_context) { - glXDestroyContext(ps->dpy, ps->glx_context); - ps->glx_context = None; - } + opengl_destroy(ps); #endif // Free double buffer diff --git a/compton.h b/compton.h index b4f5bfd44..f0af56482 100644 --- a/compton.h +++ b/compton.h @@ -9,6 +9,7 @@ #include "common.h" +#include #include #include #include @@ -22,7 +23,7 @@ // We references some definitions in drm.h, which could also be found in // /usr/src/linux/include/drm/drm.h, but that path is probably even less // reliable than libdrm -#include +#include #include #include #endif @@ -705,40 +706,7 @@ static void __attribute__ ((noreturn)) usage(void); static bool -register_cm(session_t *ps, bool glx); - -#ifdef CONFIG_VSYNC_OPENGL -/** - * Ensure we have a GLX context. - */ -static inline bool -ensure_glx_context(session_t *ps) { - if (ps->glx_context) - return true; - - // Check for GLX extension - if (!ps->glx_exists) { - if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) - ps->glx_exists = true; - else { - printf_errf("(): No GLX extension."); - return false; - } - } - - // Create GLX context - if (ps->reg_win) { - XDestroyWindow(ps->dpy, ps->reg_win); - ps->reg_win = None; - } - if (!register_cm(ps, true) || !ps->glx_context) { - printf_errf("(): Failed to acquire GLX context."); - return false; - } - - return true; -} -#endif +register_cm(session_t *ps); inline static void ev_focus_in(session_t *ps, XFocusChangeEvent *ev); @@ -919,6 +887,44 @@ swopti_init(session_t *ps); static void swopti_handle_timeout(session_t *ps, struct timeval *ptv); +static bool +opengl_init(session_t *ps, bool need_render); + +static void +opengl_destroy(session_t *ps); + +#ifdef CONFIG_VSYNC_OPENGL +/** + * Check if a GLX extension exists. + */ +static inline bool +opengl_hasext(session_t *ps, const char *ext) { + const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr); + const char *pos = strstr(glx_exts, ext); + // Make sure the extension string is matched as a whole word + if (!pos + || ((pos - glx_exts) && !isspace(*(pos - 1))) + || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) { + printf_errf("(): Missing OpenGL extension %s.", ext); + return false; + } + + return true; +} + +/** + * Ensure we have a GLX context. + */ +static inline bool +ensure_glx_context(session_t *ps) { + // Create GLX context + if (!ps->glx_context) + opengl_init(ps, false); + + return ps->glx_context; +} +#endif + static bool vsync_drm_init(session_t *ps); From dc4da095bb876c4ba2b77050b35883e59af944a6 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 3 Mar 2013 21:53:08 +0800 Subject: [PATCH 111/178] Bug fix #93: Assertion failure when window reparented - Fix an assertion failure that occurs when a window is reparented to the root window then immediately to another window. Thanks to smlx for reporting. - Add extra debugging info for ReparentNotify. --- compton.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/compton.c b/compton.c index edd387823..ff1dd4975 100644 --- a/compton.c +++ b/compton.c @@ -2505,9 +2505,11 @@ add_win(session_t *ps, Window id, Window prev) { new->id = id; set_ignore_next(ps); - if (!XGetWindowAttributes(ps->dpy, id, &new->a)) { - // Failed to get window attributes. Which probably means, the window - // is gone already. + if (!XGetWindowAttributes(ps->dpy, id, &new->a) + || IsUnviewable == new->a.map_state) { + // Failed to get window attributes probably means the window is gone + // already. IsUnviewable means the window is already reparented + // elsewhere. free(new); return false; } @@ -3446,6 +3448,11 @@ ev_unmap_notify(session_t *ps, XUnmapEvent *ev) { inline static void ev_reparent_notify(session_t *ps, XReparentEvent *ev) { +#ifdef DEBUG_EVENTS + printf_dbg(" { new_parent: %#010lx, override_redirect: %d }\n", + ev->parent, ev->override_redirect); +#endif + if (ev->parent == ps->root) { add_win(ps, ev->window, 0); } else { From 07ed9901e7a6ff65a6baa2dcf95e206dc6970ca5 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 10 Mar 2013 18:45:54 +0800 Subject: [PATCH 112/178] Improvement: ARGB window matching & Enable track_focus with D-Bus - Add predefined matching target "argb" to match ARGB windows. - Make it possible to enable focus tracking on-the-fly with D-Bus method. --- c2.c | 1 + c2.h | 2 ++ common.h | 3 +++ compton.c | 28 ++++++++++++++++++++++++++-- dbus.c | 12 ++++++++++++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/c2.c b/c2.c index 5bbb4e3a8..4e2b0dfb3 100644 --- a/c2.c +++ b/c2.c @@ -1042,6 +1042,7 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, switch (pleaf->predef) { case C2_L_PID: tgt = wid; break; case C2_L_POVREDIR: tgt = w->a.override_redirect; break; + case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break; case C2_L_PFOCUSED: tgt = w->focused_real; break; case C2_L_PWMWIN: tgt = w->wmwin; break; case C2_L_PCLIENT: tgt = w->client_win; break; diff --git a/c2.h b/c2.h index 000c68448..c794da1c0 100644 --- a/c2.h +++ b/c2.h @@ -100,6 +100,7 @@ struct _c2_l { C2_L_PUNDEFINED, C2_L_PID, C2_L_POVREDIR, + C2_L_PARGB, C2_L_PFOCUSED, C2_L_PWMWIN, C2_L_PCLIENT, @@ -175,6 +176,7 @@ typedef struct { const static c2_predef_t C2_PREDEFS[] = { [C2_L_PID ] = { "id" , C2_L_TCARDINAL , 0 }, [C2_L_POVREDIR ] = { "override_redirect" , C2_L_TCARDINAL , 0 }, + [C2_L_PARGB ] = { "argb" , C2_L_TCARDINAL , 0 }, [C2_L_PFOCUSED ] = { "focused" , C2_L_TCARDINAL , 0 }, [C2_L_PWMWIN ] = { "wmwin" , C2_L_TCARDINAL , 0 }, [C2_L_PCLIENT ] = { "client" , C2_L_TWINDOW , 0 }, diff --git a/common.h b/common.h index 0d4f5a541..96ca6021c 100644 --- a/common.h +++ b/common.h @@ -1491,6 +1491,9 @@ win_set_focused_force(session_t *ps, win *w, switch_t val); void win_set_invert_color_force(session_t *ps, win *w, switch_t val); + +void +opts_init_track_focus(session_t *ps); //!@} #endif diff --git a/compton.c b/compton.c index ff1dd4975..8bd853384 100644 --- a/compton.c +++ b/compton.c @@ -1822,6 +1822,9 @@ map_win(session_t *ps, Window id) { // Make sure the XSelectInput() requests are sent XSync(ps->dpy, False); + // Update window mode here to do matching of ARGB + win_determine_mode(ps, w); + // Detect client window here instead of in add_win() as the client // window should have been prepared at this point if (!w->client_win) { @@ -1982,8 +1985,6 @@ win_determine_mode(session_t *ps, win *w) { winmode_t mode = WMODE_SOLID; XRenderPictFormat *format; - /* if trans prop == -1 fall back on previous tests */ - if (w->a.class == InputOnly) { format = 0; } else { @@ -3247,6 +3248,29 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val) { win_determine_invert_color(ps, w); } } + +/** + * Enable focus tracking. + */ +void +opts_init_track_focus(session_t *ps) { + // Already tracking focus + if (ps->o.track_focus) + return; + + ps->o.track_focus = true; + + if (!ps->o.use_ewmh_active_win) { + // Start listening to FocusChange events + for (win *w = ps->list; w; w = w->next) + if (IsViewable == w->a.map_state) + XSelectInput(ps->dpy, w->id, + determine_evmask(ps, w->id, WIN_EVMODE_FRAME)); + } + + // Recheck focus + recheck_focus(ps); +} //!@} #endif diff --git a/dbus.c b/dbus.c index 39e4cf3ad..4dd6143f8 100644 --- a/dbus.c +++ b/dbus.c @@ -953,6 +953,18 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { goto cdbus_process_opts_set_success; } + // track_focus + if (!strcmp("track_focus", target)) { + dbus_bool_t val = FALSE; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) + return false; + // You could enable this option, but never turn if off + if (val) { + opts_init_track_focus(ps); + } + goto cdbus_process_opts_set_success; + } + // vsync if (!strcmp("vsync", target)) { const char * val = NULL; From 690589bb343f25eec4727748a990b0b60972ed8d Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 12 Mar 2013 09:43:14 +0800 Subject: [PATCH 113/178] Bug fix: Create OpenGL context only after initializing overlay - Create OpenGL context only after initializing overlay, which fixes a bug that --paint-on-overlay does not work with OpenGL VSync. Thanks to tsmithe for reporting. (#7) --- compton.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/compton.c b/compton.c index 8bd853384..58a716c50 100644 --- a/compton.c +++ b/compton.c @@ -5863,6 +5863,14 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } + // Overlay must be initialized before double buffer, and before creation + // of OpenGL context. + if (ps->o.paint_on_overlay) + init_overlay(ps); + + if (ps->o.dbe && !init_dbe(ps)) + exit(1); + // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); @@ -5871,13 +5879,6 @@ session_init(session_t *ps_old, int argc, char **argv) { if (!vsync_init(ps)) exit(1); - // Overlay must be initialized before double buffer - if (ps->o.paint_on_overlay) - init_overlay(ps); - - if (ps->o.dbe && !init_dbe(ps)) - exit(1); - // Create registration window if (!ps->reg_win && !register_cm(ps)) exit(1); From f9f1e1f228ec21be08833f6aa86fe6ea2c64b625 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 15 Mar 2013 23:16:23 +0800 Subject: [PATCH 114/178] Feature: OpenGL backend - Add experimental OpenGL backend (--opengl). --blur-background is currently not possible with this backend, because I'm still trying to find a proper way to do blur with OpenGL. Flipping backend on-the-fly is really hard, so it isn't supported right now. No configuration file option exists to enable this, because it isn't stable enough. - Add `opengl-swc` VSync method that uses SGI_swap_control to control buffer swap, with OpenGL backend. (#7) - Fix a potential read-from-freed-memory issue in paint_all(). - Correctly reattach GLX context after fork. - Dump error text in error(). Add GLX error code handling. - Code clean-up. - Known issues: Region operations take a lot of time in glx_render(). I'm hesitating about what to do. --- common.h | 174 +++++++++++-- compton.c | 712 ++++++++++++++++++++++++++++-------------------------- compton.h | 137 +++++++---- opengl.c | 521 +++++++++++++++++++++++++++++++++++++++ opengl.h | 46 ++++ 5 files changed, 1169 insertions(+), 421 deletions(-) create mode 100644 opengl.c create mode 100644 opengl.h diff --git a/common.h b/common.h index 96ca6021c..30c09e3a5 100644 --- a/common.h +++ b/common.h @@ -130,6 +130,9 @@ #error libXcomposite version unsupported #endif +/// @brief Length of generic buffers. +#define BUF_LEN 80 + #define ROUNDED_PERCENT 0.05 #define ROUNDED_PIXELS 10 @@ -152,6 +155,9 @@ #define XRFILTER_GUASSIAN "gaussian" #define XRFILTER_BINOMIAL "binomial" +/// @brief Maximum OpenGL FBConfig depth. +#define OPENGL_MAX_DEPTH 32 + // Window flags // Window size is changed @@ -249,9 +255,18 @@ typedef enum { VSYNC_DRM, VSYNC_OPENGL, VSYNC_OPENGL_OML, + VSYNC_OPENGL_SWC, NUM_VSYNC, } vsync_t; +/// @brief Possible backends of compton. +enum backend { + BKEND_XRENDER, + BKEND_GLX, +}; + +typedef struct _glx_texture glx_texture_t; + #ifdef CONFIG_VSYNC_OPENGL typedef int (*f_WaitVideoSync) (int, int, unsigned *); typedef int (*f_GetVideoSync) (unsigned *); @@ -259,15 +274,39 @@ typedef int (*f_GetVideoSync) (unsigned *); typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc); typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc); +typedef int (*f_SwapIntervalSGI) (int interval); + typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer); -struct glx_fbconfig { +/// @brief Wrapper of a GLX FBConfig. +typedef struct { GLXFBConfig cfg; + GLint texture_fmt; + GLint texture_tgts; + bool y_inverted; +} glx_fbconfig_t; + +/// @brief Wrapper of a binded GLX texture. +struct _glx_texture { + GLuint texture; + GLXPixmap glpixmap; + Pixmap pixmap; + int width; + int height; + int depth; bool y_inverted; }; #endif +typedef struct { + Pixmap pixmap; + Picture pict; + glx_texture_t *ptex; +} paint_t; + +#define PAINT_INIT { .pixmap = None, .pict = None } + typedef struct { int size; double *data; @@ -293,6 +332,8 @@ typedef struct { /// The display name we used. NULL means we are using the value of the /// DISPLAY environment variable. char *display; + /// The backend in use. + enum backend backend; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -434,8 +475,10 @@ typedef struct { // Damage root_damage; /// X Composite overlay window. Used if --paint-on-overlay. Window overlay; + /// Whether the root tile is filled by compton. + bool root_tile_fill; /// Picture of the root window background. - Picture root_tile; + paint_t root_tile_paint; /// A region of the size of the screen. XserverRegion screen_reg; /// Picture of root window. Destination of painting in no-DBE painting @@ -488,6 +531,10 @@ typedef struct { /// Pointer to the next member of tail element of the error /// ignore linked list. ignore_t **ignore_tail; +#ifdef CONFIG_VSYNC_OPENGL + /// Current GLX Z value. + int glx_z; +#endif /// Reset program after next paint. bool reset; @@ -551,18 +598,18 @@ typedef struct { f_GetVideoSync glXGetVideoSyncSGI; /// Pointer to glXWaitVideoSyncSGI function. f_WaitVideoSync glXWaitVideoSyncSGI; - /// Pointer to glXGetSyncValuesOML function. + /// Pointer to glXGetSyncValuesOML function. f_GetSyncValuesOML glXGetSyncValuesOML; /// Pointer to glXWaitForMscOML function. f_WaitForMscOML glXWaitForMscOML; + /// Pointer to glXSwapIntervalSGI function. + f_SwapIntervalSGI glXSwapIntervalProc; /// Pointer to glXBindTexImageEXT function. - f_BindTexImageEXT glXBindTexImageEXT; + f_BindTexImageEXT glXBindTexImageProc; /// Pointer to glXReleaseTexImageEXT function. - f_ReleaseTexImageEXT glXReleaseTexImageEXT; - /// FBConfig for RGB GLX pixmap. - struct glx_fbconfig *glx_fbconfig_rgb; - /// FBConfig for RGBA GLX pixmap. - struct glx_fbconfig *glx_fbconfig_rgba; + f_ReleaseTexImageEXT glXReleaseTexImageProc; + /// FBConfig-s for GLX pixmap of different depths. + glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; #endif // === X extension related === @@ -664,16 +711,16 @@ typedef struct _win { Window id; /// Window attributes. XWindowAttributes a; + /// Window visual pict format; + XRenderPictFormat *pictfmt; /// Window painting mode. winmode_t mode; /// Whether the window has been damaged at least once. bool damaged; /// Damage of the window. Damage damage; - /// NameWindowPixmap of the window. - Pixmap pixmap; - /// Picture of the window. - Picture picture; + /// Paint info of the window. + paint_t paint; /// Bounding shape of the window. XserverRegion border_size; /// Region of the whole window, shadow region included. @@ -750,8 +797,6 @@ typedef struct _win { /// broken window managers not transferring client window's /// _NET_WM_OPACITY value opacity_t opacity_prop_client; - /// Alpha mask Picture to render window with opacity. - Picture alpha_pict; // Fading-related members /// Do not fade if it's false. Change on window type change. @@ -763,8 +808,6 @@ typedef struct _win { // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. double frame_opacity; - /// Alpha mask Picture to render window frame with opacity. - Picture frame_alpha_pict; /// Frame widths. Determined by client window attributes. unsigned int left_width, right_width, top_width, bottom_width; @@ -784,9 +827,7 @@ typedef struct _win { /// Height of shadow. Affected by window size and commandline argument. int shadow_height; /// Picture to render shadow. Affected by window size. - Picture shadow_pict; - /// Alpha mask Picture to render shadow. Affected by shadow opacity. - Picture shadow_alpha_pict; + paint_t shadow_paint; /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for /// none. long prop_shadow; @@ -896,6 +937,20 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, // === Functions === +/** + * @brief Quit if the passed-in pointer is empty. + */ +static inline void +allocchk_(const char *func_name, void *ptr) { + if (!ptr) { + printf_err("%s(): Failed to allocate memory.", func_name); + exit(1); + } +} + +/// @brief Wrapper of allocchk_(). +#define allocchk(ptr) allocchk_(__func__, ptr) + /** * Return whether a struct timeval value is empty. */ @@ -1304,6 +1359,14 @@ get_atom(session_t *ps, const char *atom_name) { return XInternAtom(ps->dpy, atom_name, False); } +/** + * Return the painting target window. + */ +static inline Window +get_tgt_window(session_t *ps) { + return ps->o.paint_on_overlay ? ps->overlay: ps->root; +} + /** * Find a window from window id in window linked list of the session. */ @@ -1371,6 +1434,17 @@ copy_region(const session_t *ps, XserverRegion oldregion) { return region; } +/** + * Destroy a XserverRegion. + */ +static inline void +free_region(session_t *ps, XserverRegion *p) { + if (*p) { + XFixesDestroyRegion(ps->dpy, *p); + *p = None; + } +} + /** * Determine if a window has a specific property. * @@ -1454,6 +1528,66 @@ force_repaint(session_t *ps); bool vsync_init(session_t *ps); +#ifdef CONFIG_VSYNC_OPENGL +/** @name GLX + */ +///@{ + +bool +glx_init(session_t *ps, bool need_render); + +void +glx_destroy(session_t *ps); + +void +glx_on_root_change(session_t *ps); + +bool +glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, + int width, int height, int depth); + +void +glx_release_pixmap(session_t *ps, glx_texture_t *ptex); + +static inline bool +glx_tex_binded(const glx_texture_t *ptex) { + return ptex && ptex->glpixmap && ptex->texture; +} + +bool +glx_render(session_t *ps, const glx_texture_t *ptex, + int x, int y, int dx, int dy, int width, int height, int z, + double opacity, bool neg, XserverRegion reg_tgt); +#endif + +static inline void +free_texture(session_t *ps, glx_texture_t **pptex) { +#ifdef CONFIG_VSYNC_OPENGL + glx_texture_t *ptex = *pptex; + + // Quit if there's nothing + if (!ptex) + return; + + glx_release_pixmap(ps, ptex); + + // Free texture + if (ptex->texture) { + glDeleteTextures(1, &ptex->texture); + ptex->texture = 0; + } + + // Free structure itself + free(ptex); + *pptex = NULL; +#endif +} + +///@} + +/** @name DBus handling + */ +///@{ #ifdef CONFIG_DBUS /** @name DBus handling */ diff --git a/compton.c b/compton.c index 58a716c50..f8c33d265 100644 --- a/compton.c +++ b/compton.c @@ -37,6 +37,7 @@ const char * const VSYNC_STRS[NUM_VSYNC] = { "drm", // VSYNC_DRM "opengl", // VSYNC_OPENGL "opengl-oml", // VSYNC_OPENGL_OML + "opengl-swc", // VSYNC_OPENGL_SWC }; /// Function pointers to init VSync modes. @@ -44,6 +45,7 @@ static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = { [VSYNC_DRM ] = vsync_drm_init, [VSYNC_OPENGL ] = vsync_opengl_init, [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init, + [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init, }; /// Function pointers to wait for VSync. @@ -425,8 +427,11 @@ make_shadow(session_t *ps, double opacity, /** * Generate shadow Picture for a window. */ -static Picture -shadow_picture(session_t *ps, double opacity, int width, int height) { +static bool +win_build_shadow(session_t *ps, win *w, double opacity) { + const int width = w->widthb; + const int height = w->heightb; + XImage *shadow_image = NULL; Pixmap shadow_pixmap = None, shadow_pixmap_argb = None; Picture shadow_picture = None, shadow_picture_argb = None; @@ -461,13 +466,16 @@ shadow_picture(session_t *ps, double opacity, int width, int height) { shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width, shadow_image->height); + w->shadow_paint.pixmap = shadow_pixmap_argb; + w->shadow_paint.pict = shadow_picture_argb; + bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32); + XFreeGC(ps->dpy, gc); XDestroyImage(shadow_image); XFreePixmap(ps->dpy, shadow_pixmap); - XFreePixmap(ps->dpy, shadow_pixmap_argb); XRenderFreePicture(ps->dpy, shadow_picture); - return shadow_picture_argb; + return success; shadow_picture_err: if (shadow_image) @@ -482,7 +490,8 @@ shadow_picture_err: XRenderFreePicture(ps->dpy, shadow_picture_argb); if (gc) XFreeGC(ps->dpy, gc); - return None; + + return false; } /** @@ -653,7 +662,7 @@ win_rounded_corners(session_t *ps, win *w) { */ static void win_validate_pixmap(session_t *ps, win *w) { - if (!w->pixmap) + if (!w->paint.pixmap) return; // Detect whether the pixmap is valid with XGetGeometry. Well, maybe there @@ -663,15 +672,13 @@ win_validate_pixmap(session_t *ps, win *w) { Window rroot = None; int rx = 0, ry = 0; unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0; - invalid = (!XGetGeometry(ps->dpy, w->pixmap, &rroot, &rx, &ry, + invalid = (!XGetGeometry(ps->dpy, w->paint.pixmap, &rroot, &rx, &ry, &rwid, &rhei, &rborder, &rdepth) || !rwid || !rhei); } // Destroy pixmap and picture, if invalid - if (invalid) { - free_pixmap(ps, &w->pixmap); - free_picture(ps, &w->picture); - } + if (invalid) + free_paint(ps, &w->paint); } /** @@ -794,23 +801,21 @@ recheck_focus(session_t *ps) { return NULL; } -static Picture -root_tile_f(session_t *ps) { +static bool +get_root_tile(session_t *ps) { /* if (ps->o.paint_on_overlay) { return ps->root_picture; } */ - Picture picture; - Pixmap pixmap; - bool fill = false; - XRenderPictureAttributes pa; - int p; + assert(!ps->root_tile_paint.pixmap); + ps->root_tile_fill = false; - pixmap = None; + bool fill = false; + Pixmap pixmap = None; // Get the values of background attributes - for (p = 0; background_props_str[p]; p++) { + for (int p = 0; background_props_str[p]; p++) { winprop_t prop = wid_get_prop(ps, ps->root, get_atom(ps, background_props_str[p]), 1L, XA_PIXMAP, 32); @@ -823,43 +828,52 @@ root_tile_f(session_t *ps) { free_winprop(&prop); } + // Create a pixmap if there isn't any if (!pixmap) { pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, ps->depth); fill = true; } - pa.repeat = True; - picture = XRenderCreatePicture( - ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis), - CPRepeat, &pa); + // Create Picture + { + XRenderPictureAttributes pa = { + .repeat = True, + }; + ps->root_tile_paint.pict = XRenderCreatePicture( + ps->dpy, pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis), + CPRepeat, &pa); + } + // Fill pixmap if needed if (fill) { XRenderColor c; c.red = c.green = c.blue = 0x8080; c.alpha = 0xffff; - XRenderFillRectangle( - ps->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); - - free_pixmap(ps, &pixmap); + XRenderFillRectangle(ps->dpy, PictOpSrc, ps->root_tile_paint.pict, &c, 0, 0, 1, 1); } - return picture; + ps->root_tile_fill = fill; + ps->root_tile_paint.pixmap = pixmap; +#ifdef CONFIG_VSYNC_OPENGL + if (BKEND_GLX == ps->o.backend) + return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, ps->root_tile_paint.pixmap, + ps->root_width, ps->root_height, ps->depth); +#endif + + return true; } /** * Paint root window content. */ static void -paint_root(session_t *ps, Picture tgt_buffer) { - if (!ps->root_tile) { - ps->root_tile = root_tile_f(ps); - } +paint_root(session_t *ps, XserverRegion reg_paint) { + if (!ps->root_tile_paint.pixmap) + get_root_tile(ps); - XRenderComposite( - ps->dpy, PictOpSrc, ps->root_tile, None, - tgt_buffer, 0, 0, 0, 0, 0, 0, - ps->root_width, ps->root_height); + win_render(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, + ps->root_tile_paint.pict); } /** @@ -1106,12 +1120,12 @@ paint_preprocess(session_t *ps, win *list) { run_fade(ps, w, steps); // Give up if it's not damaged or invisible, or it's unmapped and its - // picture is gone (for example due to a ConfigureNotify) + // pixmap is gone (for example due to a ConfigureNotify) if (!w->damaged || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= ps->root_width || w->a.y >= ps->root_height || ((IsUnmapped == w->a.map_state || w->destroyed) - && !w->picture)) { + && !w->paint.pixmap)) { to_paint = false; } @@ -1122,12 +1136,8 @@ paint_preprocess(session_t *ps, win *list) { add_damage_win(ps, w); } - w->alpha_pict = get_alpha_pict_o(ps, w->opacity); - - // End the game if we are using the 0 opacity alpha_pict - if (w->alpha_pict == ps->alpha_picts[0]) { + if (get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0]) to_paint = false; - } } if (to_paint) { @@ -1161,23 +1171,19 @@ paint_preprocess(session_t *ps, win *list) { ps->reg_ignore_expire = true; } - w->frame_alpha_pict = get_alpha_pict_d(ps, w->frame_opacity); - // Calculate shadow opacity if (w->frame_opacity) w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity; else w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w); - // Rebuild shadow_pict if necessary - if (w->flags & WFLAG_SIZE_CHANGE) - free_picture(ps, &w->shadow_pict); - - if (w->shadow && !w->shadow_pict) { - w->shadow_pict = shadow_picture(ps, 1, w->widthb, w->heightb); + // Rebuild shadow if necessary + if (w->flags & WFLAG_SIZE_CHANGE) { + free_paint(ps, &w->shadow_paint); } - w->shadow_alpha_pict = get_alpha_pict_d(ps, w->shadow_opacity); + if (w->shadow && !paint_isvalid(ps, &w->shadow_paint)) + win_build_shadow(ps, w, 1); // Dimming preprocessing if (w->dim) { @@ -1272,22 +1278,29 @@ paint_preprocess(session_t *ps, win *list) { // Fetch pictures only if windows are redirected if (ps->redirected) { for (w = t; w; w = w->prev_trans) { - // Fetch the picture and pixmap if needed - if (!w->picture) { - XRenderPictureAttributes pa; - XRenderPictFormat *format; - Drawable draw = w->id; - - if (ps->has_name_pixmap && !w->pixmap) { + // Fetch Pixmap + if (!w->paint.pixmap && ps->has_name_pixmap) { set_ignore_next(ps); - w->pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); + w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); + } + // XRender: Build picture + if (BKEND_XRENDER == ps->o.backend && !w->paint.pict) { + Drawable draw = w->paint.pixmap; + if (!draw) + draw = w->id; + { + XRenderPictureAttributes pa = { + .subwindow_mode = IncludeInferiors, + }; + + w->paint.pict = XRenderCreatePicture(ps->dpy, draw, w->pictfmt, + CPSubwindowMode, &pa); } - if (w->pixmap) draw = w->pixmap; - - format = XRenderFindVisualFormat(ps->dpy, w->a.visual); - pa.subwindow_mode = IncludeInferiors; - w->picture = XRenderCreatePicture( - ps->dpy, draw, format, CPSubwindowMode, &pa); + } + // OpenGL: Build texture + if (!paint_bind_tex(ps, &w->paint, w->widthb, w->heightb, + w->pictfmt->depth)) { + printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id); } } } @@ -1299,41 +1312,36 @@ paint_preprocess(session_t *ps, win *list) { * Paint the shadow of a window. */ static inline void -win_paint_shadow(session_t *ps, win *w, Picture tgt_buffer) { - XRenderComposite( - ps->dpy, PictOpOver, w->shadow_pict, w->shadow_alpha_pict, - tgt_buffer, 0, 0, 0, 0, - w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, - w->shadow_width, w->shadow_height); +win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint) { + if (!paint_isvalid(ps, &w->shadow_paint)) { + printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id); + return; + } + + render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, + w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, + w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint); } /** * Create an alternative picture for a window. */ static inline Picture -win_build_picture(session_t *ps, win *w, Visual *visual) { +win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { const int wid = w->widthb; const int hei = w->heightb; - int depth = 0; - XRenderPictFormat *pictformat = NULL; - if (visual && ps->vis != visual) { - pictformat = XRenderFindVisualFormat(ps->dpy, visual); - depth = pictformat->depth; - } - else { - pictformat = XRenderFindVisualFormat(ps->dpy, ps->vis); - } + if (!pictfmt) + pictfmt = XRenderFindVisualFormat(ps->dpy, ps->vis); - if (!depth) - depth = ps->depth; + int depth = pictfmt->depth; Pixmap tmp_pixmap = XCreatePixmap(ps->dpy, ps->root, wid, hei, depth); if (!tmp_pixmap) return None; Picture tmp_picture = XRenderCreatePicture(ps->dpy, tmp_pixmap, - pictformat, 0, 0); + pictfmt, 0, 0); free_pixmap(ps, &tmp_pixmap); return tmp_picture; @@ -1401,23 +1409,53 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, free_picture(ps, &tmp_picture); } +static void +render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, + double opacity, bool argb, bool neg, + Picture pict, glx_texture_t *ptex, XserverRegion reg_paint) { + switch (ps->o.backend) { + case BKEND_XRENDER: + { + Picture alpha_pict = get_alpha_pict_d(ps, opacity); + if (alpha_pict != ps->alpha_picts[0]) { + int op = ((!argb && !alpha_pict) ? PictOpSrc: PictOpOver); + XRenderComposite(ps->dpy, op, pict, alpha_pict, + ps->tgt_buffer, x, y, 0, 0, dx, dy, wid, hei); + } + break; + } +#ifdef CONFIG_VSYNC_OPENGL + case BKEND_GLX: + glx_render(ps, ptex, x, y, dx, dy, wid, hei, + ps->glx_z, opacity, neg, reg_paint); + ps->glx_z += 1; + break; +#endif + default: + assert(0); + } +} + /** * Paint a window itself and dim it if asked. */ static inline void -win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint) { - int x = w->a.x; - int y = w->a.y; - int wid = w->widthb; - int hei = w->heightb; +win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { + if (!paint_isvalid(ps, &w->paint)) { + printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id); + return; + } + + const int x = w->a.x; + const int y = w->a.y; + const int wid = w->widthb; + const int hei = w->heightb; - Picture alpha_mask = (OPAQUE == w->opacity ? None: w->alpha_pict); - int op = (w->mode == WMODE_SOLID ? PictOpSrc: PictOpOver); - Picture pict = w->picture; + Picture pict = w->paint.pict; // Invert window color, if required - if (w->invert_color) { - Picture newpict = win_build_picture(ps, w, w->a.visual); + if (BKEND_XRENDER == ps->o.backend && w->invert_color) { + Picture newpict = win_build_picture(ps, w, w->pictfmt); if (newpict) { // Apply clipping region to save some CPU if (reg_paint) { @@ -1440,9 +1478,10 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint } } + double dopacity = get_opacity_percent(w);; + if (!w->frame_opacity) { - XRenderComposite(ps->dpy, op, pict, alpha_mask, - tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict); } else { // Painting parameters @@ -1452,8 +1491,8 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint const int r = w->a.border_width + w->right_width; #define COMP_BDR(cx, cy, cwid, chei) \ - XRenderComposite(ps->dpy, PictOpOver, pict, w->frame_alpha_pict, \ - tgt_buffer, (cx), (cy), 0, 0, x + (cx), y + (cy), (cwid), (chei)) + win_render(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity, \ + reg_paint, pict) // The following complicated logic is required because some broken // window managers (I'm talking about you, Openbox!) that makes @@ -1488,8 +1527,7 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint pwid = wid - l - pwid; if (pwid > 0) { // body - XRenderComposite(ps->dpy, op, pict, alpha_mask, - tgt_buffer, l, t, 0, 0, x + l, y + t, pwid, phei); + win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pict); } } } @@ -1498,13 +1536,13 @@ win_paint_win(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint #undef COMP_BDR - if (pict != w->picture) + if (pict != w->paint.pict) free_picture(ps, &pict); // Dimming the window if needed if (w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) { XRenderComposite(ps->dpy, PictOpOver, ps->black_picture, - w->dim_alpha_pict, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + w->dim_alpha_pict, ps->tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } } @@ -1523,10 +1561,19 @@ paint_all(session_t *ps, XserverRegion region, win *t) { #ifdef DEBUG_REPAINT static struct timespec last_paint = { 0 }; #endif - - win *w; + win *w = NULL; XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; +#ifdef CONFIG_VSYNC_OPENGL + // GLX backend: OpenGL doesn't support partial repaint without + // GLX_MESA_copy_sub_buffer + if (BKEND_GLX == ps->o.backend) { + free_region(ps, ®ion); + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + ps->glx_z = 0.0; + } +#endif + if (!region) { region = get_screen_region(ps); } @@ -1583,7 +1630,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); - paint_root(ps, ps->tgt_buffer); + paint_root(ps, reg_paint); // Create temporary regions for use during painting if (!reg_tmp) @@ -1622,7 +1669,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); - win_paint_shadow(ps, w, ps->tgt_buffer); + win_paint_shadow(ps, w, reg_paint); } } @@ -1655,10 +1702,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } // Painting the window - win_paint_win(ps, w, ps->tgt_buffer, reg_paint); + win_paint_win(ps, w, reg_paint); } - - check_fade_fin(ps, w); } // Free up all temporary regions @@ -1688,21 +1733,32 @@ paint_all(session_t *ps, XserverRegion region, win *t) { if (!ps->o.vsync_aggressive) vsync_wait(ps); - // DBE painting mode, only need to swap the buffer - if (ps->o.dbe) { - XdbeSwapInfo swap_info = { - .swap_window = get_tgt_window(ps), - // Is it safe to use XdbeUndefined? - .swap_action = XdbeCopied - }; - XdbeSwapBuffers(ps->dpy, &swap_info, 1); - } - // No-DBE painting mode - else if (ps->tgt_buffer != ps->tgt_picture) { - XRenderComposite( - ps->dpy, PictOpSrc, ps->tgt_buffer, None, - ps->tgt_picture, 0, 0, 0, 0, - 0, 0, ps->root_width, ps->root_height); + switch (ps->o.backend) { + case BKEND_XRENDER: + // DBE painting mode, only need to swap the buffer + if (ps->o.dbe) { + XdbeSwapInfo swap_info = { + .swap_window = get_tgt_window(ps), + // Is it safe to use XdbeUndefined? + .swap_action = XdbeCopied + }; + XdbeSwapBuffers(ps->dpy, &swap_info, 1); + } + // No-DBE painting mode + else if (ps->tgt_buffer != ps->tgt_picture) { + XRenderComposite( + ps->dpy, PictOpSrc, ps->tgt_buffer, None, + ps->tgt_picture, 0, 0, 0, 0, + 0, 0, ps->root_width, ps->root_height); + } + break; +#ifdef CONFIG_VSYNC_OPENGL + case BKEND_GLX: + glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + break; +#endif + default: + assert(0); } if (ps->o.vsync_aggressive) @@ -1730,6 +1786,15 @@ paint_all(session_t *ps, XserverRegion region, win *t) { putchar('\n'); fflush(stdout); #endif + + // Check if fading is finished on all painted windows + { + win *pprev = NULL; + for (w = t; w; w = pprev) { + pprev = w->prev_trans; + check_fade_fin(ps, w); + } + } } static void @@ -1822,7 +1887,7 @@ map_win(session_t *ps, Window id) { // Make sure the XSelectInput() requests are sent XSync(ps->dpy, False); - // Update window mode here to do matching of ARGB + // Update window mode here to check for ARGB windows win_determine_mode(ps, w); // Detect client window here instead of in add_win() as the client @@ -1917,10 +1982,9 @@ finish_unmap_win(session_t *ps, win *w) { w->extents = None; } - free_pixmap(ps, &w->pixmap); - free_picture(ps, &w->picture); + free_paint(ps, &w->paint); free_region(ps, &w->border_size); - free_picture(ps, &w->shadow_pict); + free_paint(ps, &w->shadow_paint); } static void @@ -1983,16 +2047,9 @@ get_opacity_percent(win *w) { static void win_determine_mode(session_t *ps, win *w) { winmode_t mode = WMODE_SOLID; - XRenderPictFormat *format; - - if (w->a.class == InputOnly) { - format = 0; - } else { - format = XRenderFindVisualFormat(ps->dpy, w->a.visual); - } - if (format && format->type == PictTypeDirect - && format->direct.alphaMask) { + if (w->pictfmt && w->pictfmt->type == PictTypeDirect + && w->pictfmt->direct.alphaMask) { mode = WMODE_ARGB; } else if (w->opacity != OPAQUE) { mode = WMODE_TRANS; @@ -2107,7 +2164,7 @@ win_update_shape(session_t *ps, win *w) { // If clear_shadow state on the window possibly changed, destroy the old // shadow_pict if (ps->o.clear_shadow && w->bounding_shaped != bounding_shaped_old) - free_picture(ps, &w->shadow_pict); + free_paint(ps, &w->shadow_paint); */ } } @@ -2404,11 +2461,11 @@ add_win(session_t *ps, Window id, Window prev) { .id = None, .a = { }, + .pictfmt = NULL, .mode = WMODE_TRANS, .damaged = false, .damage = None, - .pixmap = None, - .picture = None, + .paint = PAINT_INIT, .border_size = None, .extents = None, .flags = 0, @@ -2446,13 +2503,11 @@ add_win(session_t *ps, Window id, Window prev) { .opacity_tgt = 0, .opacity_prop = OPAQUE, .opacity_prop_client = OPAQUE, - .alpha_pict = None, .fade = false, .fade_callback = NULL, .frame_opacity = 0.0, - .frame_alpha_pict = None, .left_width = 0, .right_width = 0, .top_width = 0, @@ -2465,8 +2520,7 @@ add_win(session_t *ps, Window id, Window prev) { .shadow_dy = 0, .shadow_width = 0, .shadow_height = 0, - .shadow_pict = None, - .shadow_alpha_pict = None, + .shadow_paint = PAINT_INIT, .prop_shadow = -1, .dim = false, @@ -2520,6 +2574,10 @@ add_win(session_t *ps, Window id, Window prev) { assert(IsViewable == map_state || IsUnmapped == map_state); new->a.map_state = IsUnmapped; + // Get window picture format + if (InputOutput == new->a.class) + new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual); + // Create Damage for window if (InputOutput == new->a.class) { set_ignore_next(ps); @@ -2608,6 +2666,7 @@ restack_win(session_t *ps, win *w, Window new_above) { static void configure_win(session_t *ps, XConfigureEvent *ce) { + // On root window changes if (ce->window == ps->root) { if (ps->tgt_buffer) { XRenderFreePicture(ps->dpy, ps->tgt_buffer); @@ -2618,9 +2677,15 @@ configure_win(session_t *ps, XConfigureEvent *ce) { rebuild_screen_reg(ps); +#ifdef CONFIG_VSYNC_OPENGL + if (BKEND_GLX == ps->o.backend) + glx_on_root_change(ps); +#endif + return; } + // Other window changes win *w = find_win(ps, ce->window); XserverRegion damage = None; @@ -2660,10 +2725,8 @@ configure_win(session_t *ps, XConfigureEvent *ce) { w->a.y = ce->y; if (w->a.width != ce->width || w->a.height != ce->height - || w->a.border_width != ce->border_width) { - free_pixmap(ps, &w->pixmap); - free_picture(ps, &w->picture); - } + || w->a.border_width != ce->border_width) + free_paint(ps, &w->paint); if (w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) { @@ -2756,11 +2819,10 @@ destroy_win(session_t *ps, Window id) { static inline void root_damaged(session_t *ps) { - if (ps->root_tile) { + if (ps->root_tile_paint.pixmap) { XClearArea(ps->dpy, ps->root, 0, 0, 0, 0, true); // if (ps->root_picture != ps->root_tile) { - XRenderFreePicture(ps->dpy, ps->root_tile); - ps->root_tile = None; + free_root_tile(ps); /* } if (root_damage) { XserverRegion parts = XFixesCreateRegion(ps->dpy, 0, 0); @@ -2829,6 +2891,21 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { CASESTRRET2(BadGlyph); } +#ifdef CONFIG_VSYNC_OPENGL + if (ps->glx_exists) { + o = ev->error_code - ps->glx_error; + switch (o) { + CASESTRRET2(GLX_BAD_SCREEN); + CASESTRRET2(GLX_BAD_ATTRIBUTE); + CASESTRRET2(GLX_NO_EXTENSION); + CASESTRRET2(GLX_BAD_VISUAL); + CASESTRRET2(GLX_BAD_CONTEXT); + CASESTRRET2(GLX_BAD_VALUE); + CASESTRRET2(GLX_BAD_ENUM); + } + } +#endif + switch (ev->error_code) { CASESTRRET2(BadAccess); CASESTRRET2(BadAlloc); @@ -2852,9 +2929,13 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { #undef CASESTRRET2 print_timestamp(ps); - printf("error %d (%s) request %d minor %d serial %lu\n", - ev->error_code, name, ev->request_code, - ev->minor_code, ev->serial); + { + char buf[BUF_LEN] = ""; + XGetErrorText(ps->dpy, ev->error_code, buf, BUF_LEN); + printf("error %d (%s) request %d minor %d serial %lu (\"%s\")\n", + ev->error_code, name, ev->request_code, + ev->minor_code, ev->serial, buf); + } return 0; } @@ -3942,8 +4023,7 @@ usage(void) { " Specify refresh rate of the screen. If not specified or 0, compton\n" " will try detecting this with X RandR extension.\n" "--vsync vsync-method\n" - " Set VSync method. There are up to 3 VSync methods currently available\n" - " depending on your compile time settings:\n" + " Set VSync method. There are up to 4 VSync methods currently available.\n" " none = No VSync\n" #undef WARNING #ifndef CONFIG_VSYNC_DRM @@ -3952,7 +4032,7 @@ usage(void) { #define WARNING #endif " drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n" - " drivers. Experimental." WARNING "\n" + " drivers." WARNING "\n" #undef WARNING #ifndef CONFIG_VSYNC_OPENGL #define WARNING WARNING_DISABLED @@ -3960,9 +4040,14 @@ usage(void) { #define WARNING #endif " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" - " work on some drivers. Experimental." WARNING"\n" + " work on some drivers." WARNING"\n" " opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n" " Only work on some drivers. Experimental." WARNING"\n" + " opengl-swc = Try to VSync with SGI_swap_control OpenGL extension.\n" + " Only work on some drivers. Works only with OpenGL backend.\n" + " Does not actually control paint timing, only buffer swap is\n" + " affected, so it doesn't have the effect of --sw-opti unlike\n" + " other methods. Experimental." WARNING "\n" "--alpha-step val\n" " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" " 0.03.\n" @@ -4013,6 +4098,8 @@ usage(void) { "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" + "--opengl\n" + " Enable the highly experimental OpenGL backend." WARNING "\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4096,6 +4183,14 @@ fork_after(session_t *ps) { if (getppid() == 1) return true; +#ifdef CONFIG_VSYNC_OPENGL + // GLX context must be released and reattached on fork + if (ps->glx_context && !glXMakeCurrent(ps->dpy, None, NULL)) { + printf_errf("(): Failed to detach GLx context."); + return false; + } +#endif + int pid = fork(); if (-1 == pid) { @@ -4107,6 +4202,14 @@ fork_after(session_t *ps) { setsid(); +#ifdef CONFIG_VSYNC_OPENGL + if (ps->glx_context + && !glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { + printf_errf("(): Failed to make GLX context current."); + return false; + } +#endif + // Mainly to suppress the _FORTIFY_SOURCE warning bool success = freopen("/dev/null", "r", stdin); if (!success) { @@ -4448,6 +4551,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "dbus", no_argument, NULL, 286 }, { "logpath", required_argument, NULL, 287 }, { "invert-color-include", required_argument, NULL, 288 }, + { "opengl", no_argument, NULL, 289 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4701,6 +4805,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --invert-color-include condlst_add(ps, &ps->o.invert_color_list, optarg); break; + case 289: + // --opengl + ps->o.backend = BKEND_GLX; + break; default: usage(); break; @@ -4889,155 +4997,6 @@ swopti_handle_timeout(session_t *ps, struct timeval *ptv) { } } -#ifdef CONFIG_VSYNC_OPENGL -/** - * Get a GLX FBConfig. - */ -static inline bool -opengl_update_fbconfig(session_t *ps, bool alpha, struct glx_fbconfig **ppcfg) { - const int FBCONFIG_ATTRS[] = { - (alpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT: GLX_BIND_TO_TEXTURE_RGB_EXT), True, - GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT, - GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, - GLX_DOUBLEBUFFER, True, - GLX_Y_INVERTED_EXT, GLX_DONT_CARE, - None - }; - - if (*ppcfg) { - free(*ppcfg); - *ppcfg = NULL; - } - - int count = 0; - GLXFBConfig *cfgs = glXChooseFBConfig(ps->dpy, ps->scr, FBCONFIG_ATTRS, &count); - if (!count || !cfgs) { - if (cfgs) - XFree(cfgs); - return false; - }; - - *ppcfg = malloc(sizeof(struct glx_fbconfig)); - (*ppcfg)->cfg = cfgs[0]; - - { - int inverted = 0; - glXGetFBConfigAttrib(ps->dpy, (*ppcfg)->cfg, GLX_Y_INVERTED_EXT, &inverted); - (*ppcfg)->y_inverted = inverted; - } - - XFree(cfgs); - - return true; -} - -/** - * Initialize OpenGL. - */ -static bool -opengl_init(session_t *ps, bool need_render) { - // Check for GLX extension - if (!ps->glx_exists) { - if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) - ps->glx_exists = true; - else { - printf_errf("(): No GLX extension."); - goto opengl_init_err; - } - } - - // Ensure GLX_EXT_texture_from_pixmap exists - if (need_render && !opengl_hasext(ps, "GLX_EXT_texture_from_pixmap")) { - goto opengl_init_err; - } - - { - // Get XVisualInfo - XVisualInfo *pvis = NULL; - { - XVisualInfo vreq = { .visualid = XVisualIDFromVisual(ps->vis) }; - int nitems = 0; - pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems); - } - - if (!pvis) { - printf_errf("(): Failed to acquire XVisualInfo for current visual."); - goto opengl_init_err; - } - - // Ensure the visual is double-buffered - if (need_render) { - int value = 0; - glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value); - if (!value) { - XFree(pvis); - printf_errf("(): Default GLX visual is not double-buffered."); - goto opengl_init_err; - } - } - - // Get GLX context - ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); - XFree(pvis); - } - - if (!ps->glx_context) { - printf_errf("(): Failed to get GLX context."); - goto opengl_init_err; - } - - // Attach GLX context - if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { - printf_errf("(): Failed to attach GLX context."); - goto opengl_init_err; - } - - // Acquire function addresses - if (need_render) { - ps->glXBindTexImageEXT = (f_BindTexImageEXT) - glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT"); - ps->glXReleaseTexImageEXT = (f_ReleaseTexImageEXT) - glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT"); - if (!ps->glXBindTexImageEXT || !ps->glXReleaseTexImageEXT) { - printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); - goto opengl_init_err; - } - } - - // Acquire FBConfigs - if (need_render - && (!(ps->glx_fbconfig_rgb || opengl_update_fbconfig(ps, false, &ps->glx_fbconfig_rgb)) - || !(ps->glx_fbconfig_rgba || opengl_update_fbconfig(ps, true, &ps->glx_fbconfig_rgba)))) { - printf_errf("(): Failed to acquire GLX fbconfig."); - goto opengl_init_err; - } - - if (need_render) { - glViewport(0, 0, ps->root_width, ps->root_height); - } - - return true; - -opengl_init_err: - opengl_destroy(ps); - - return false; -} - -static void -opengl_destroy(session_t *ps) { - if (ps->glx_context) { - glXDestroyContext(ps->dpy, ps->glx_context); - ps->glx_context = None; - } - - free(ps->glx_fbconfig_rgb); - ps->glx_fbconfig_rgb = NULL; - free(ps->glx_fbconfig_rgba); - ps->glx_fbconfig_rgba = NULL; -} -#endif - /** * Initialize DRM VSync. * @@ -5148,6 +5107,34 @@ vsync_opengl_oml_init(session_t *ps) { #endif } +static bool +vsync_opengl_swc_init(session_t *ps) { +#ifdef CONFIG_VSYNC_OPENGL + if (!ensure_glx_context(ps)) + return false; + + if (BKEND_GLX != ps->o.backend) { + printf_errf("(): I'm afraid glXSwapIntervalSGI wouldn't help if you are " + "not using OpenGL backend. You could try, nonetheless."); + } + + // Get video sync functions + if (!ps->glXSwapIntervalProc) + ps->glXSwapIntervalProc = (f_SwapIntervalSGI) + glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI"); + if (!ps->glXSwapIntervalProc) { + printf_errf("(): Failed to get SGI_swap_control function."); + return false; + } + ps->glXSwapIntervalProc(1); + + return true; +#else + printf_errf("(): Program not compiled with OpenGL VSync support."); + return false; +#endif +} + #ifdef CONFIG_VSYNC_OPENGL /** * Wait for next VSync, OpenGL method. @@ -5202,8 +5189,6 @@ vsync_wait(session_t *ps) { if (!ps->o.vsync) return; - assert(VSYNC_FUNCS_WAIT[ps->o.vsync]); - if (VSYNC_FUNCS_WAIT[ps->o.vsync]) VSYNC_FUNCS_WAIT[ps->o.vsync](ps); } @@ -5271,29 +5256,42 @@ init_overlay(session_t *ps) { } /** - * Query needed X Render filters to check for their existence. + * Query needed X Render / OpenGL filters to check for their existence. */ -static void +static bool init_filters(session_t *ps) { + // Blur filter if (ps->o.blur_background || ps->o.blur_background_frame) { - // Query filters - XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps)); - if (pf) { - for (int i = 0; i < pf->nfilter; ++i) { - // Convolution filter - if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION)) - ps->xrfilter_convolution_exists = true; - } - } - XFree(pf); + switch (ps->o.backend) { + case BKEND_XRENDER: + { + // Query filters + XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps)); + if (pf) { + for (int i = 0; i < pf->nfilter; ++i) { + // Convolution filter + if (!strcmp(pf->filter[i], XRFILTER_CONVOLUTION)) + ps->xrfilter_convolution_exists = true; + } + } + XFree(pf); - // Turn features off if any required filter is not present - if (!ps->xrfilter_convolution_exists) { - fprintf(stderr, "X Render convolution filter unsupported by your X server. Background blur disabled.\n"); - ps->o.blur_background = false; - ps->o.blur_background_frame = false; + // Turn features off if any required filter is not present + if (!ps->xrfilter_convolution_exists) { + printf_errf("(): X Render convolution filter unsupported by your X server. Background blur is not possible."); + return false; + } + break; + } + case BKEND_GLX: + { + printf_errf("(): OpenGL blur is not implemented yet."); + return false; + } } } + + return true; } /** @@ -5477,10 +5475,8 @@ redir_stop(session_t *ps) { // Destroy all Pictures as they expire once windows are unredirected // If we don't destroy them here, looks like the resources are just // kept inaccessible somehow - for (win *w = ps->list; w; w = w->next) { - free_pixmap(ps, &w->pixmap); - free_picture(ps, &w->picture); - } + for (win *w = ps->list; w; w = w->next) + free_paint(ps, &w->paint); XCompositeUnredirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); // Unmap overlay window @@ -5597,14 +5593,17 @@ session_init(session_t *ps_old, int argc, char **argv) { .root_width = 0, // .root_damage = None, .overlay = None, - .root_tile = None, + .root_tile_fill = false, + .root_tile_paint = PAINT_INIT, .screen_reg = None, .tgt_picture = None, .tgt_buffer = None, .root_dbe = None, .reg_win = None, .o = { + .config_file = NULL, .display = NULL, + .backend = BKEND_XRENDER, .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, @@ -5863,14 +5862,35 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } + ps->root_width = DisplayWidth(ps->dpy, ps->scr); + ps->root_height = DisplayHeight(ps->dpy, ps->scr); + + rebuild_screen_reg(ps); + // Overlay must be initialized before double buffer, and before creation // of OpenGL context. if (ps->o.paint_on_overlay) init_overlay(ps); + // Initialize DBE + if (ps->o.dbe && BKEND_GLX == ps->o.backend) { + printf_errf("(): DBE couldn't be used on OpenGL backend."); + ps->o.dbe = false; + } + if (ps->o.dbe && !init_dbe(ps)) exit(1); + // Initialize OpenGL as early as possible + if (BKEND_GLX == ps->o.backend) { +#ifdef CONFIG_VSYNC_OPENGL + if (!glx_init(ps, true)) + exit(1); +#else + printf_errfq(1, "(): OpenGL backend support not compiled in."); +#endif + } + // Initialize software optimization if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); @@ -5889,11 +5909,6 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->gaussian_map = make_gaussian_map(ps->o.shadow_radius); presum_gaussian(ps, ps->gaussian_map); - ps->root_width = DisplayWidth(ps->dpy, ps->scr); - ps->root_height = DisplayHeight(ps->dpy, ps->scr); - - rebuild_screen_reg(ps); - { XRenderPictureAttributes pa; pa.subwindow_mode = IncludeInferiors; @@ -5911,7 +5926,8 @@ session_init(session_t *ps_old, int argc, char **argv) { } } - init_filters(ps); + if (!init_filters(ps)) + exit(1); ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0); ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1); @@ -6092,7 +6108,7 @@ session_destroy(session_t *ps) { free_picture(ps, &ps->root_picture); // Free other X resources - free_picture(ps, &ps->root_tile); + free_root_tile(ps); free_region(ps, &ps->screen_reg); free_region(ps, &ps->all_damage); free(ps->expose_rects); @@ -6106,14 +6122,8 @@ session_destroy(session_t *ps) { free(ps->pfds_write); free(ps->pfds_except); - // Free reg_win and glx_context - if (ps->reg_win) { - XDestroyWindow(ps->dpy, ps->reg_win); - ps->reg_win = None; - } - #ifdef CONFIG_VSYNC_OPENGL - opengl_destroy(ps); + glx_destroy(ps); #endif // Free double buffer @@ -6136,6 +6146,12 @@ session_destroy(session_t *ps) { ps->overlay = None; } + // Free reg_win + if (ps->reg_win) { + XDestroyWindow(ps->dpy, ps->reg_win); + ps->reg_win = None; + } + // Flush all events XSync(ps->dpy, True); @@ -6236,7 +6252,7 @@ main(int argc, char **argv) { while (1) { ps_g = session_init(ps_old, argc, argv); if (!ps_g) { - printf_errf("Failed to create new session."); + printf_errf("(): Failed to create new session."); return 1; } session_run(ps_g); diff --git a/compton.h b/compton.h index f0af56482..0479b3d0a 100644 --- a/compton.h +++ b/compton.h @@ -9,7 +9,6 @@ #include "common.h" -#include #include #include #include @@ -52,14 +51,6 @@ set_ignore_next(session_t *ps) { static int should_ignore(session_t *ps, unsigned long sequence); -/** - * Return the painting target window. - */ -static inline Window -get_tgt_window(session_t *ps) { - return ps->o.paint_on_overlay ? ps->overlay: ps->root; -} - /** * Reset filter on a Picture. */ @@ -119,16 +110,6 @@ array_wid_exists(const Window *arr, int count, Window wid) { return false; } -/** - * Destroy a XserverRegion. - */ -inline static void -free_region(session_t *ps, XserverRegion *p) { - if (*p) { - XFixesDestroyRegion(ps->dpy, *p); - *p = None; - } -} /** * Destroy a Picture. @@ -176,16 +157,58 @@ free_wincondlst(c2_lptr_t **pcondlst) { } #endif +/** + * Check whether a paint_t contains enough data. + */ +static inline bool +paint_isvalid(session_t *ps, const paint_t *ppaint) { + if (!ppaint || !ppaint->pixmap) + return false; + + if (BKEND_XRENDER == ps->o.backend && !ppaint->pict) + return false; + +#ifdef CONFIG_VSYNC_OPENGL + if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex)) + return false; +#endif + + return true; +} +/** + * Bind texture in paint_t if we are using OpenGL backend. + */ +static inline bool +paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth) { +#ifdef CONFIG_VSYNC_OPENGL + // TODO: Make sure we have the same Pixmap binded? + if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex)) { + return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); + } +#endif + + return true; +} + +/** + * Free paint_t. + */ +static inline void +free_paint(session_t *ps, paint_t *ppaint) { + free_texture(ps, &ppaint->ptex); + free_picture(ps, &ppaint->pict); + free_pixmap(ps, &ppaint->pixmap); +} + /** * Destroy all resources in a struct _win. */ -inline static void +static inline void free_win_res(session_t *ps, win *w) { free_region(ps, &w->extents); - free_pixmap(ps, &w->pixmap); - free_picture(ps, &w->picture); + free_paint(ps, &w->paint); free_region(ps, &w->border_size); - free_picture(ps, &w->shadow_pict); + free_paint(ps, &w->shadow_paint); free_damage(ps, &w->damage); free_region(ps, &w->reg_ignore); free(w->name); @@ -194,6 +217,19 @@ free_win_res(session_t *ps, win *w) { free(w->role); } +/** + * Free root tile related things. + */ +static inline void +free_root_tile(session_t *ps) { + free_picture(ps, &ps->root_tile_paint.pict); + free_texture(ps, &ps->root_tile_paint.ptex); + if (ps->root_tile_fill) + free_pixmap(ps, &ps->root_tile_paint.pixmap); + ps->root_tile_paint.pixmap = None; + ps->root_tile_fill = false; +} + /** * Get current system clock in milliseconds. */ @@ -274,8 +310,8 @@ presum_gaussian(session_t *ps, conv *map); static XImage * make_shadow(session_t *ps, double opacity, int width, int height); -static Picture -shadow_picture(session_t *ps, double opacity, int width, int height); +static bool +win_build_shadow(session_t *ps, win *w, double opacity); static Picture solid_picture(session_t *ps, bool argb, double a, @@ -463,11 +499,11 @@ group_is_focused(session_t *ps, Window leader) { static win * recheck_focus(session_t *ps); -static Picture -root_tile_f(session_t *ps); +static bool +get_root_tile(session_t *ps); static void -paint_root(session_t *ps, Picture tgt_buffer); +paint_root(session_t *ps, XserverRegion reg_paint); static XserverRegion win_get_region(session_t *ps, win *w, bool use_offset); @@ -490,6 +526,22 @@ get_frame_extents(session_t *ps, win *w, Window client); static win * paint_preprocess(session_t *ps, win *list); +static void +render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, + double opacity, bool argb, bool neg, + Picture pict, glx_texture_t *ptex, XserverRegion reg_paint); + +static inline void +win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, Picture pict) { + const int dx = (w ? w->a.x: 0) + x; + const int dy = (w ? w->a.y: 0) + y; + const bool argb = (w && w->mode == WMODE_ARGB); + const bool neg = (w && w->invert_color); + + render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, + pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint); +} + static void paint_all(session_t *ps, XserverRegion region, win *t); @@ -887,31 +939,7 @@ swopti_init(session_t *ps); static void swopti_handle_timeout(session_t *ps, struct timeval *ptv); -static bool -opengl_init(session_t *ps, bool need_render); - -static void -opengl_destroy(session_t *ps); - #ifdef CONFIG_VSYNC_OPENGL -/** - * Check if a GLX extension exists. - */ -static inline bool -opengl_hasext(session_t *ps, const char *ext) { - const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr); - const char *pos = strstr(glx_exts, ext); - // Make sure the extension string is matched as a whole word - if (!pos - || ((pos - glx_exts) && !isspace(*(pos - 1))) - || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) { - printf_errf("(): Missing OpenGL extension %s.", ext); - return false; - } - - return true; -} - /** * Ensure we have a GLX context. */ @@ -919,7 +947,7 @@ static inline bool ensure_glx_context(session_t *ps) { // Create GLX context if (!ps->glx_context) - opengl_init(ps, false); + glx_init(ps, false); return ps->glx_context; } @@ -939,6 +967,9 @@ vsync_opengl_init(session_t *ps); static bool vsync_opengl_oml_init(session_t *ps); +static bool +vsync_opengl_swc_init(session_t *ps); + #ifdef CONFIG_VSYNC_OPENGL static int vsync_opengl_wait(session_t *ps); diff --git a/opengl.c b/opengl.c new file mode 100644 index 000000000..16e99de02 --- /dev/null +++ b/opengl.c @@ -0,0 +1,521 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "opengl.h" + +/** + * Initialize OpenGL. + */ +bool +glx_init(session_t *ps, bool need_render) { + bool success = false; + XVisualInfo *pvis = NULL; + + // Check for GLX extension + if (!ps->glx_exists) { + if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error)) + ps->glx_exists = true; + else { + printf_errf("(): No GLX extension."); + goto glx_init_end; + } + } + + // Get XVisualInfo + pvis = get_visualinfo_from_visual(ps, ps->vis); + if (!pvis) { + printf_errf("(): Failed to acquire XVisualInfo for current visual."); + goto glx_init_end; + } + + // Ensure the visual is double-buffered + if (need_render) { + int value = 0; + if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) { + printf_errf("(): Root visual is not a GL visual."); + goto glx_init_end; + } + + if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) + || !value) { + printf_errf("(): Root visual is not a double buffered GL visual."); + goto glx_init_end; + } + } + + // Ensure GLX_EXT_texture_from_pixmap exists + if (need_render && !glx_hasext(ps, "GLX_EXT_texture_from_pixmap")) + goto glx_init_end; + + // Get GLX context + ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); + + if (!ps->glx_context) { + printf_errf("(): Failed to get GLX context."); + goto glx_init_end; + } + + // Attach GLX context + if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { + printf_errf("(): Failed to attach GLX context."); + goto glx_init_end; + } + + // Acquire function addresses + if (need_render) { + ps->glXBindTexImageProc = (f_BindTexImageEXT) + glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT"); + ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT) + glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT"); + if (!ps->glXBindTexImageProc || !ps->glXReleaseTexImageProc) { + printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); + goto glx_init_end; + } + } + + // Acquire FBConfigs + if (need_render && !glx_update_fbconfig(ps)) + goto glx_init_end; + + if (need_render) { + // Adjust viewport + glViewport(0, 0, ps->root_width, ps->root_height); + + // Initialize settings, copied from dcompmgr + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + // glEnable(GL_DEPTH_TEST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + + // Clear screen + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + } + + success = true; + +glx_init_end: + if (pvis) + XFree(pvis); + + if (!success) + glx_destroy(ps); + + return success; +} + +/** + * Destroy GLX related resources. + */ +void +glx_destroy(session_t *ps) { + // Free FBConfigs + for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { + free(ps->glx_fbconfigs[i]); + ps->glx_fbconfigs[i] = NULL; + } + + // Destroy GLX context + if (ps->glx_context) { + glXDestroyContext(ps->dpy, ps->glx_context); + ps->glx_context = NULL; + } +} + +/** + * Callback to run on root window size change. + */ +void +glx_on_root_change(session_t *ps) { + glViewport(0, 0, ps->root_width, ps->root_height); +} + +/** + * @brief Update the FBConfig of given depth. + */ +static inline void +glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) { + // Make sure the depth is sane + if (depth < 0 || depth > OPENGL_MAX_DEPTH) + return; + + // Compare new FBConfig with current one + if (glx_cmp_fbconfig(ps, ps->glx_fbconfigs[depth], pfbcfg) < 0) { +#ifdef DEBUG_GLX + printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->glx_fbconfigs[depth] ? (unsigned) ps->glx_fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts); +#endif + if (!ps->glx_fbconfigs[depth]) { + ps->glx_fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t)); + allocchk(ps->glx_fbconfigs[depth]); + } + (*ps->glx_fbconfigs[depth]) = *pfbcfg; + } +} + +/** + * Get GLX FBConfigs for all depths. + */ +static bool +glx_update_fbconfig(session_t *ps) { + // Acquire all FBConfigs and loop through them + int nele = 0; + GLXFBConfig* pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele); + + for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) { + glx_fbconfig_t fbinfo = { + .cfg = *pcur, + .texture_fmt = 0, + .texture_tgts = 0, + .y_inverted = false, + }; + int depth = 0, depth_alpha = 0, val = 0; + + if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth) + || Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) { + printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", (int) (pcur - pfbcfgs)); + continue; + } + if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) { + printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", (int) (pcur - pfbcfgs)); + continue; + } + + bool rgb = false; + bool rgba = false; + + if (depth >= 32 && depth_alpha && Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val) + rgba = true; + + if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val) + rgb = true; + + if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val)) + fbinfo.y_inverted = val; + + if ((depth - depth_alpha) < 32 && rgb) { + fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT; + glx_update_fbconfig_bydepth(ps, depth - depth_alpha, &fbinfo); + } + + if (rgba) { + fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT; + glx_update_fbconfig_bydepth(ps, depth, &fbinfo); + } + } + + if (pfbcfgs) + XFree(pfbcfgs); + + // Sanity checks + if (!ps->glx_fbconfigs[ps->depth]) { + printf_errf("(): No FBConfig found for default depth %d.", ps->depth); + return false; + } + + if (!ps->glx_fbconfigs[32]) { + printf_errf("(): No FBConfig found for depth 32. Expect crazy things."); + } + + return true; +} + +static inline int +glx_cmp_fbconfig_cmpattr(session_t *ps, + const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b, + int attr) { + int attr_a = 0, attr_b = 0; + + // TODO: Error checking + glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a); + glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b); + + return attr_a - attr_b; +} + +/** + * Compare two GLX FBConfig's to find the preferred one. + */ +static int +glx_cmp_fbconfig(session_t *ps, + const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) { + int result = 0; + + if (!pfbc_a) + return -1; + if (!pfbc_b) + return 1; + +#define P_CMPATTR_LT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return -result; } +#define P_CMPATTR_GT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return result; } + + P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT); + P_CMPATTR_LT(GLX_DOUBLEBUFFER); + P_CMPATTR_LT(GLX_STENCIL_SIZE); + P_CMPATTR_LT(GLX_DEPTH_SIZE); + P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT); + + return 0; +} + +/** + * Bind a X pixmap to an OpenGL texture. + */ +bool +glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, + int width, int height, int depth) { + if (depth > OPENGL_MAX_DEPTH) { + printf_errf("(%d): Requested depth higher than %d.", depth, + OPENGL_MAX_DEPTH); + return false; + } + const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth]; + if (!pcfg) { + printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth); + return false; + } + + const GLenum target = GL_TEXTURE_2D; + glx_texture_t *ptex = *pptex; + bool need_release = true; + + // Allocate structure + if (!ptex) { + static const glx_texture_t GLX_TEX_DEF = { + .texture = 0, + .glpixmap = 0, + .pixmap = 0, + .width = 0, + .height = 0, + .depth = 0, + .y_inverted = false, + }; + + ptex = malloc(sizeof(glx_texture_t)); + allocchk(ptex); + memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t)); + *pptex = ptex; + } + + glEnable(target); + + // Release pixmap if parameters are inconsistent + if (ptex->texture && !(width == ptex->width && height == ptex->height + && ptex->pixmap == pixmap && depth == ptex->depth)) { + glx_release_pixmap(ps, ptex); + } + + // Create texture + if (!ptex->texture) { + need_release = false; + + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(target, texture); + + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(target, 0); + + ptex->texture = texture; + ptex->y_inverted = pcfg->y_inverted; + } + if (!ptex->texture) { + printf_errf("(): Failed to allocate texture."); + return false; + } + + glBindTexture(target, ptex->texture); + + // Create GLX pixmap + if (!ptex->glpixmap) { + need_release = false; + +#ifdef DEBUG_GLX + printf_dbgf("(): depth %d rgba %d\n", depth, (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt)); +#endif + + int attrs[] = { + GLX_TEXTURE_FORMAT_EXT, + pcfg->texture_fmt, + // GLX_TEXTURE_TARGET_EXT, + // , + 0, + }; + + ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs); + } + if (!ptex->glpixmap) { + printf_errf("(): Failed to allocate GLX pixmap."); + return false; + } + + // The specification requires rebinding whenever the content changes... + // We can't follow this, too slow. + if (need_release) + ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); + + ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); + + // Cleanup + glBindTexture(target, 0); + glDisable(target); + + ptex->width = width; + ptex->height = height; + ptex->depth = depth; + ptex->pixmap = pixmap; + + return true; +} + +/** + * @brief Release binding of a texture. + */ +void +glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { + // Release binding + if (ptex->glpixmap && ptex->texture) { + glBindTexture(GL_TEXTURE_2D, ptex->texture); + ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // Free GLX Pixmap + if (ptex->glpixmap) { + glXDestroyPixmap(ps->dpy, ptex->glpixmap); + ptex->glpixmap = 0; + } +} + +/** + * @brief Render a region with texture data. + */ +bool +glx_render(session_t *ps, const glx_texture_t *ptex, + int x, int y, int dx, int dy, int width, int height, int z, + double opacity, bool neg, XserverRegion reg_tgt) { + if (!ptex || !ptex->texture) { + printf_errf("(): Missing texture."); + return false; + } + + // Enable blending if needed + if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == + ps->glx_fbconfigs[ptex->depth]->texture_fmt) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // Needed for handling opacity of ARGB texture + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f(1.0f, 1.0f, 1.0f, opacity); + } + + // Color negation + if (neg) { + // Simple color negation + if (!glIsEnabled(GL_BLEND)) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_COPY_INVERTED); + } + // Blending color negation + else { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + } + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ptex->texture); + + glBegin(GL_QUADS); + + { + XserverRegion reg_new = None; + + XRectangle rec_all = { + .x = dx, + .y = dy, + .width = width, + .height = height + }; + + XRectangle *rects = &rec_all; + int nrects = 1; + +#ifdef DEBUG_GLX + printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d \n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); +#endif + + if (reg_tgt) { + reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1); + XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt); + + nrects = 0; + rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); + } + + for (int i = 0; i < nrects; ++i) { + GLfloat rx = (double) (rects[i].x - dx + x) / ptex->width; + GLfloat ry = (double) (rects[i].y - dy + y) / ptex->height; + GLfloat rxe = rx + (double) rects[i].width / ptex->width; + GLfloat rye = ry + (double) rects[i].height / ptex->height; + GLint rdx = rects[i].x; + GLint rdy = rects[i].y; + GLint rdxe = rdx + rects[i].width; + GLint rdye = rdy + rects[i].height; + + // Invert Y if needed, this may not work as expected, though. I don't + // have such a FBConfig to test with. + if (!ptex->y_inverted) { + ry = 1.0 - ry; + rye = 1.0 - rye; + } + +#ifdef DEBUG_GLX + printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); +#endif + + glTexCoord2f(rx, ry); + glVertex3i(rdx, rdy, z); + + glTexCoord2f(rxe, ry); + glVertex3i(rdxe, rdy, z); + + glTexCoord2f(rxe, rye); + glVertex3i(rdxe, rdye, z); + + glTexCoord2f(rx, rye); + glVertex3i(rdx, rdye, z); + } + + if (rects && rects != &rec_all) + XFree(rects); + free_region(ps, ®_new); + } + glEnd(); + + // Cleanup + glBindTexture(GL_TEXTURE_2D, 0); + glColor4f(0.0f, 0.0f, 0.0f, 0.0f); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_BLEND); + glDisable(GL_COLOR_LOGIC_OP); + + return true; +} diff --git a/opengl.h b/opengl.h new file mode 100644 index 000000000..b48b441d0 --- /dev/null +++ b/opengl.h @@ -0,0 +1,46 @@ +/* + * Compton - a compositor for X11 + * + * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard + * + * Copyright (c) 2011-2013, Christopher Jeffrey + * See LICENSE for more information. + * + */ + +#include "common.h" + +#include + +/** + * Check if a GLX extension exists. + */ +static inline bool +glx_hasext(session_t *ps, const char *ext) { + const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr); + const char *pos = strstr(glx_exts, ext); + // Make sure the extension string is matched as a whole word + if (!pos + || ((pos - glx_exts) && !isspace(*(pos - 1))) + || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) { + printf_errf("(): Missing OpenGL extension %s.", ext); + return false; + } + + return true; +} + +static inline XVisualInfo * +get_visualinfo_from_visual(session_t *ps, Visual *visual) { + XVisualInfo vreq = { .visualid = XVisualIDFromVisual(visual) }; + int nitems = 0; + + return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems); +} + +static bool +glx_update_fbconfig(session_t *ps); + +static int +glx_cmp_fbconfig(session_t *ps, + const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b); From 74792903de7a88eda20aad031ca1f36cb56e2c2b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 16 Mar 2013 22:54:43 +0800 Subject: [PATCH 115/178] Bug fix: GLX backend incompatibility with mesa & others - Fix a bug that glx_bind_pixmap() doesn't work with mesa drivers. Thanks to Janhouse and mkraemer for reporting. (#7) - Use stencil buffer to attempt to eliminate potential double-paint issue in glx_render(). X Fixes doesn't guarantee the rectangles in a region do not overlap, and this may cause some regions to be painted twice, which would be a problem if we are painting transparent things. Now the target window must have a stencil buffer. Compiz uses its own region implementation to deal with this, but as a lightweight compositor we can't really do the same. It may have a positive or negative effort over performance. Callgrind result indicates basically no change in performance, but this may or may not be true. - Correctly distinguish GL extensions and GLX extensions. Sorry. :-D - Handle screen size. Thanks to tsmithe for reporting. (#7) - Rename OpenGL backend to GLX backend, because, we might have a EGL backend someday. - Add configuration file option `backend` to specify backend. Add `backend` to D-Bus `opts_get`. - Add OpenGL shader compilation code, but currently unused. - Minor adjustments. - Known issue: Window content doesn't get updated in VirtualBox, probably because its OpenGL implementation requires constant rebinding of texture. But that's really slow... - Known issue: Blur feature is still unimplemented in GLX backend. --- common.h | 28 +++++++ compton.c | 59 +++++++++----- compton.h | 16 +++- dbus.c | 5 ++ opengl.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++------ opengl.h | 56 +++++++++++-- 6 files changed, 346 insertions(+), 52 deletions(-) diff --git a/common.h b/common.h index 30c09e3a5..f36edf4ba 100644 --- a/common.h +++ b/common.h @@ -89,6 +89,10 @@ // libGL #ifdef CONFIG_VSYNC_OPENGL +#ifdef CONFIG_VSYNC_OPENGL_GLSL +#define GL_GLEXT_PROTOTYPES +#endif + #include #endif @@ -263,6 +267,7 @@ typedef enum { enum backend { BKEND_XRENDER, BKEND_GLX, + NUM_BKEND, }; typedef struct _glx_texture glx_texture_t; @@ -594,6 +599,8 @@ typedef struct { // === OpenGL related === /// GLX context. GLXContext glx_context; + /// Whether we have GL_ARB_texture_non_power_of_two. + bool glx_has_texture_non_power_of_two; /// Pointer to glXGetVideoSyncSGI function. f_GetVideoSync glXGetVideoSyncSGI; /// Pointer to glXWaitVideoSyncSGI function. @@ -874,6 +881,7 @@ typedef enum { extern const char * const WINTYPES[NUM_WINTYPES]; extern const char * const VSYNC_STRS[NUM_VSYNC]; +extern const char * const BACKEND_STRS[NUM_BKEND]; extern session_t *ps_g; // == Debugging code == @@ -1252,6 +1260,20 @@ parse_vsync(session_t *ps, const char *str) { return false; } +/** + * Parse a backend option argument. + */ +static inline bool +parse_backend(session_t *ps, const char *str) { + for (enum backend i = 0; i < (sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); ++i) + if (!strcasecmp(str, BACKEND_STRS[i])) { + ps->o.backend = i; + return true; + } + printf_errf("(\"%s\"): Invalid backend argument.", str); + return false; +} + timeout_t * timeout_insert(session_t *ps, time_ms_t interval, bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); @@ -1542,6 +1564,9 @@ glx_destroy(session_t *ps); void glx_on_root_change(session_t *ps); +bool +glx_init_blur(session_t *ps); + bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, int width, int height, int depth); @@ -1554,6 +1579,9 @@ glx_tex_binded(const glx_texture_t *ptex) { return ptex && ptex->glpixmap && ptex->texture; } +void +glx_set_clip(session_t *ps, XserverRegion reg); + bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, diff --git a/compton.c b/compton.c index f8c33d265..df39a36f4 100644 --- a/compton.c +++ b/compton.c @@ -40,6 +40,12 @@ const char * const VSYNC_STRS[NUM_VSYNC] = { "opengl-swc", // VSYNC_OPENGL_SWC }; +/// Names of backends. +const char * const BACKEND_STRS[NUM_BKEND] = { + "xrender", // BKEND_XRENDER + "glx", // BKEND_GLX +}; + /// Function pointers to init VSync modes. static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = { [VSYNC_DRM ] = vsync_drm_init, @@ -1628,8 +1634,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { reg_paint = region; } - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); - + set_tgt_clip(ps, reg_paint); paint_root(ps, reg_paint); // Create temporary regions for use during painting @@ -1666,8 +1671,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { // Detect if the region is empty before painting if (region == reg_paint || !is_region_empty(ps, reg_paint)) { - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, - reg_paint); + set_tgt_clip(ps, reg_paint); win_paint_shadow(ps, w, reg_paint); } @@ -1694,7 +1698,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } if (!is_region_empty(ps, reg_paint)) { - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg_paint); + set_tgt_clip(ps, reg_paint); // Blur window background if ((ps->o.blur_background && WMODE_SOLID != w->mode) || (ps->o.blur_background_frame && w->frame_opacity)) { @@ -1713,7 +1717,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { // Do this as early as possible if (!ps->o.dbe) - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, None); + set_tgt_clip(ps, None); if (ps->o.vsync) { // Make sure all previous requests are processed to achieve best @@ -4044,7 +4048,7 @@ usage(void) { " opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n" " Only work on some drivers. Experimental." WARNING"\n" " opengl-swc = Try to VSync with SGI_swap_control OpenGL extension.\n" - " Only work on some drivers. Works only with OpenGL backend.\n" + " Only work on some drivers. Works only with GLX backend.\n" " Does not actually control paint timing, only buffer swap is\n" " affected, so it doesn't have the effect of --sw-opti unlike\n" " other methods. Experimental." WARNING "\n" @@ -4098,8 +4102,8 @@ usage(void) { "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" - "--opengl\n" - " Enable the highly experimental OpenGL backend." WARNING "\n" + "--backend backend\n" + " Choose backend. Possible choices are xrender and glx" WARNING ".\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4450,6 +4454,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --vsync if (config_lookup_string(&cfg, "vsync", &sval) && !parse_vsync(ps, sval)) exit(1); + // --backend + if (config_lookup_string(&cfg, "backend", &sval) && !parse_backend(ps, sval)) + exit(1); // --alpha-step config_lookup_float(&cfg, "alpha-step", &ps->o.alpha_step); // --dbe @@ -4552,6 +4559,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "logpath", required_argument, NULL, 287 }, { "invert-color-include", required_argument, NULL, 288 }, { "opengl", no_argument, NULL, 289 }, + { "backend", required_argument, NULL, 290 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4809,6 +4817,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --opengl ps->o.backend = BKEND_GLX; break; + case 290: + // --backend + if (!parse_backend(ps, optarg)) + exit(1); + break; default: usage(); break; @@ -5115,7 +5128,7 @@ vsync_opengl_swc_init(session_t *ps) { if (BKEND_GLX != ps->o.backend) { printf_errf("(): I'm afraid glXSwapIntervalSGI wouldn't help if you are " - "not using OpenGL backend. You could try, nonetheless."); + "not using GLX backend. You could try, nonetheless."); } // Get video sync functions @@ -5283,11 +5296,13 @@ init_filters(session_t *ps) { } break; } +#ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: { - printf_errf("(): OpenGL blur is not implemented yet."); - return false; + if (!glx_init_blur(ps)) + return false; } +#endif } } @@ -5709,6 +5724,7 @@ session_init(session_t *ps_old, int argc, char **argv) { #ifdef CONFIG_VSYNC_OPENGL .glx_context = None, + .glx_has_texture_non_power_of_two = false, .glXGetVideoSyncSGI = NULL, .glXWaitVideoSyncSGI = NULL, .glXGetSyncValuesOML = NULL, @@ -5862,6 +5878,14 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } + // Start listening to events on root earlier to catch all possible + // root geometry changes + XSelectInput(ps->dpy, ps->root, + SubstructureNotifyMask + | ExposureMask + | StructureNotifyMask + | PropertyChangeMask); + ps->root_width = DisplayWidth(ps->dpy, ps->scr); ps->root_height = DisplayHeight(ps->dpy, ps->scr); @@ -5874,7 +5898,7 @@ session_init(session_t *ps_old, int argc, char **argv) { // Initialize DBE if (ps->o.dbe && BKEND_GLX == ps->o.backend) { - printf_errf("(): DBE couldn't be used on OpenGL backend."); + printf_errf("(): DBE couldn't be used on GLX backend."); ps->o.dbe = false; } @@ -5887,7 +5911,7 @@ session_init(session_t *ps_old, int argc, char **argv) { if (!glx_init(ps, true)) exit(1); #else - printf_errfq(1, "(): OpenGL backend support not compiled in."); + printf_errfq(1, "(): GLX backend support not compiled in."); #endif } @@ -5926,6 +5950,7 @@ session_init(session_t *ps_old, int argc, char **argv) { } } + // Initialize filters, must be preceded by OpenGL context creation if (!init_filters(ps)) exit(1); @@ -5947,12 +5972,6 @@ session_init(session_t *ps_old, int argc, char **argv) { redir_start(ps); - XSelectInput(ps->dpy, ps->root, - SubstructureNotifyMask - | ExposureMask - | StructureNotifyMask - | PropertyChangeMask); - { Window root_return, parent_return; Window *children; diff --git a/compton.h b/compton.h index 0479b3d0a..49ffcb88f 100644 --- a/compton.h +++ b/compton.h @@ -176,7 +176,7 @@ paint_isvalid(session_t *ps, const paint_t *ppaint) { return true; } /** - * Bind texture in paint_t if we are using OpenGL backend. + * Bind texture in paint_t if we are using GLX backend. */ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth) { @@ -542,6 +542,20 @@ win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint); } +static inline void +set_tgt_clip(session_t *ps, XserverRegion reg) { + switch (ps->o.backend) { + case BKEND_XRENDER: + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg); + break; +#ifdef CONFIG_VSYNC_OPENGL + case BKEND_GLX: + glx_set_clip(ps, reg); + break; +#endif + } +} + static void paint_all(session_t *ps, XserverRegion region, win *t); diff --git a/dbus.c b/dbus.c index 4dd6143f8..25b37fdf3 100644 --- a/dbus.c +++ b/dbus.c @@ -876,6 +876,11 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_reply_string(ps, msg, VSYNC_STRS[ps->o.vsync]); return true; } + if (!strcmp("backend", target)) { + assert(ps->o.backend < sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); + cdbus_reply_string(ps, msg, BACKEND_STRS[ps->o.backend]); + return true; + } cdbus_m_opts_get_do(dbe, cdbus_reply_bool); cdbus_m_opts_get_do(vsync_aggressive, cdbus_reply_bool); diff --git a/opengl.c b/opengl.c index 16e99de02..2ec0b3313 100644 --- a/opengl.c +++ b/opengl.c @@ -51,7 +51,7 @@ glx_init(session_t *ps, bool need_render) { } // Ensure GLX_EXT_texture_from_pixmap exists - if (need_render && !glx_hasext(ps, "GLX_EXT_texture_from_pixmap")) + if (need_render && !glx_hasglxext(ps, "GLX_EXT_texture_from_pixmap")) goto glx_init_end; // Get GLX context @@ -68,6 +68,24 @@ glx_init(session_t *ps, bool need_render) { goto glx_init_end; } + // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles + // in regions don't overlap, so we must use stencil buffer to make sure + // we don't paint a region for more than one time, I think? + if (need_render) { + GLint val = 0; + glGetIntegerv(GL_STENCIL_BITS, &val); + if (!val) { + printf_errf("(): Target window doesn't have stencil buffer."); + goto glx_init_end; + } + } + + // Check GL_ARB_texture_non_power_of_two, requires a GLX context and + // must precede FBConfig fetching + if (need_render) + ps->glx_has_texture_non_power_of_two = glx_hasglext(ps, + "GL_ARB_texture_non_power_of_two"); + // Acquire function addresses if (need_render) { ps->glXBindTexImageProc = (f_BindTexImageEXT) @@ -85,24 +103,23 @@ glx_init(session_t *ps, bool need_render) { goto glx_init_end; if (need_render) { - // Adjust viewport - glViewport(0, 0, ps->root_width, ps->root_height); - - // Initialize settings, copied from dcompmgr - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + glx_on_root_change(ps); + // glEnable(GL_DEPTH_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); + // Initialize stencil buffer + glClear(GL_STENCIL_BUFFER_BIT); + glEnable(GL_STENCIL_TEST); + glStencilMask(0x1); + glStencilFunc(GL_EQUAL, 0x1, 0x1); + // Clear screen glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // glXSwapBuffers(ps->dpy, get_tgt_window(ps)); } success = true; @@ -141,6 +158,34 @@ glx_destroy(session_t *ps) { void glx_on_root_change(session_t *ps) { glViewport(0, 0, ps->root_width, ps->root_height); + + // Initialize matrix, copied from dcompmgr + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +/** + * Initialize GLX blur filter. + */ +bool +glx_init_blur(session_t *ps) { + printf_errf("(): Blur on GLX backend isn't implemented yet, sorry."); + return false; + +// #ifdef CONFIG_VSYNC_OPENGL_GLSL +// static const char *FRAG_SHADER_BLUR = ""; +// GLuint frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR); +// if (!frag_shader) { +// printf_errf("(): Failed to create fragment shader for blurring."); +// return false; +// } +// #else +// printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); +// return false; +// #endif } /** @@ -346,15 +391,28 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, if (!ptex->glpixmap) { need_release = false; + // Determine texture target, copied from compiz + GLint tex_tgt = 0; + if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts + && ps->glx_has_texture_non_power_of_two) + tex_tgt = GLX_TEXTURE_2D_EXT; + else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts) + tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; + else if (!(GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts)) + tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; + else + tex_tgt = GLX_TEXTURE_2D_EXT; + #ifdef DEBUG_GLX - printf_dbgf("(): depth %d rgba %d\n", depth, (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt)); + printf_dbgf("(): depth %d, tgt %#x, rgba %d\n", depth, tex_tgt, + (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt)); #endif - int attrs[] = { + GLint attrs[] = { GLX_TEXTURE_FORMAT_EXT, pcfg->texture_fmt, - // GLX_TEXTURE_TARGET_EXT, - // , + GLX_TEXTURE_TARGET_EXT, + tex_tgt, 0, }; @@ -403,6 +461,51 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { } } +/** + * Set clipping region on the target window. + */ +void +glx_set_clip(session_t *ps, XserverRegion reg) { + glClear(GL_STENCIL_BUFFER_BIT); + + if (reg) { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glDepthMask(GL_FALSE); + glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); + + int nrects = 0; + XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects); + + glBegin(GL_QUADS); + + for (int i = 0; i < nrects; ++i) { + GLint rx = rects[i].x; + GLint ry = rects[i].y; + GLint rxe = rx + rects[i].width; + GLint rye = ry + rects[i].height; + GLint z = 0; + +#ifdef DEBUG_GLX + printf_dbgf("(): Rect %d: %f, %f, %f, %f\n", i, rx, ry, rxe, rye); +#endif + + glVertex3i(rx, ry, z); + glVertex3i(rxe, ry, z); + glVertex3i(rxe, rye, z); + glVertex3i(rx, rye, z); + } + + glEnd(); + + if (rects) + XFree(rects); + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + } +} + /** * @brief Render a region with texture data. */ @@ -440,11 +543,6 @@ glx_render(session_t *ps, const glx_texture_t *ptex, } } - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, ptex->texture); - - glBegin(GL_QUADS); - { XserverRegion reg_new = None; @@ -459,9 +557,10 @@ glx_render(session_t *ps, const glx_texture_t *ptex, int nrects = 1; #ifdef DEBUG_GLX - printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d \n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); + printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); #endif + /* if (reg_tgt) { reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1); XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt); @@ -469,6 +568,12 @@ glx_render(session_t *ps, const glx_texture_t *ptex, nrects = 0; rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); } + */ + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ptex->texture); + + glBegin(GL_QUADS); for (int i = 0; i < nrects; ++i) { GLfloat rx = (double) (rects[i].x - dx + x) / ptex->width; @@ -504,11 +609,12 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glVertex3i(rdx, rdye, z); } + glEnd(); + if (rects && rects != &rec_all) XFree(rects); free_region(ps, ®_new); } - glEnd(); // Cleanup glBindTexture(GL_TEXTURE_2D, 0); @@ -519,3 +625,85 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; } + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +GLuint +glx_create_shader(GLenum shader_type, const char *shader_str) { + bool success = false; + GLuint shader = glCreateShader(shader_type); + if (!shader) { + printf_errf("(): Failed to create shader with type %d.", shader_type); + goto glx_create_shader_end; + } + glShaderSource(shader, 1, &shader_str, NULL); + glCompileShader(shader); + + // Get shader status + { + GLint status = GL_FALSE; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (GL_FALSE == status) { + GLint log_len = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len); + if (log_len) { + char log[log_len + 1]; + glGetShaderInfoLog(shader, log_len, NULL, log); + printf_errf("(): Failed to compile shader with type %d: %s", + shader_type, log); + } + goto glx_create_shader_end; + } + } + + success = true; + +glx_create_shader_end: + if (shader && !success) { + glDeleteShader(shader); + shader = 0; + } + + return shader; +} + +GLuint +glx_create_program(GLenum shader_type, const GLuint * const shaders, + int nshaders) { + bool success = false; + GLuint program = glCreateProgram(); + if (!program) { + printf_errf("(): Failed to create program."); + goto glx_create_program_end; + } + + for (int i = 0; i < nshaders; ++i) + glAttachShader(program, shaders[i]); + glLinkProgram(program); + + // Get program status + { + GLint status = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (GL_FALSE == status) { + GLint log_len = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len); + if (log_len) { + char log[log_len + 1]; + glGetProgramInfoLog(program, log_len, NULL, log); + printf_errf("(): Failed to link program: %s", log); + } + goto glx_create_program_end; + } + } + success = true; + +glx_create_program_end: + if (program && !success) { + glDeleteProgram(program); + program = 0; + } + + return program; +} +#endif + diff --git a/opengl.h b/opengl.h index b48b441d0..7f6dae696 100644 --- a/opengl.h +++ b/opengl.h @@ -12,22 +12,62 @@ #include +/** + * Check if a word is in string. + */ +static inline bool +wd_is_in_str(const char *haystick, const char *needle) { + if (!haystick) + return false; + + assert(*needle); + + const char *pos = haystick - 1; + while ((pos = strstr(pos + 1, needle))) { + // Continue if it isn't a word boundary + if (((pos - haystick) && !isspace(*(pos - 1))) + || (strlen(pos) > strlen(needle) && !isspace(pos[strlen(needle)]))) + continue; + return true; + } + + return false; +} + /** * Check if a GLX extension exists. */ static inline bool -glx_hasext(session_t *ps, const char *ext) { +glx_hasglxext(session_t *ps, const char *ext) { const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr); - const char *pos = strstr(glx_exts, ext); - // Make sure the extension string is matched as a whole word - if (!pos - || ((pos - glx_exts) && !isspace(*(pos - 1))) - || (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) { - printf_errf("(): Missing OpenGL extension %s.", ext); + if (!glx_exts) { + printf_errf("(): Failed get GLX extension list."); return false; } - return true; + bool found = wd_is_in_str(glx_exts, ext); + if (!found) + printf_errf("(): Missing GLX extension %s.", ext); + + return found; +} + +/** + * Check if a GLX extension exists. + */ +static inline bool +glx_hasglext(session_t *ps, const char *ext) { + const char *gl_exts = (const char *) glGetString(GL_EXTENSIONS); + if (!gl_exts) { + printf_errf("(): Failed get GL extension list."); + return false; + } + + bool found = wd_is_in_str(gl_exts, ext); + if (!found) + printf_errf("(): Missing GL extension %s.", ext); + + return found; } static inline XVisualInfo * From 17f7d31a5282d55182aec8938d52e13379dcc2bb Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 17 Mar 2013 12:14:00 +0800 Subject: [PATCH 116/178] Bug fix: GLX backend incompatibility with VirtualBox & others - GLX backend: Fix a bug that window content does not get updated on VirtualBox, by rebinding texture when window content changes. This may have a negative effect on performance. - GLX backend: Add --glx-no-stencil to restore the old clipping method, just in case. - GLX backend: Apply stricter checks on texture-pixmap binding. - GLX backend: Fix a bug that glx_set_clip() behaves incorrectly when None is passed in. - GLX backend: Use glEnable()/glDisable() to toggle stencil tests, in hope to increase performance. - Move window pixmap/picture fetching to win_paint_win(), in hope to increase performance. - Intersect shadow painting region with its bounding rectangle, in hope to increase performance. --- common.h | 12 ++++++-- compton.c | 85 +++++++++++++++++++++++++++++++++++-------------------- compton.h | 8 ++++-- opengl.c | 30 +++++++++++++------- 4 files changed, 89 insertions(+), 46 deletions(-) diff --git a/common.h b/common.h index f36edf4ba..77aa582f6 100644 --- a/common.h +++ b/common.h @@ -339,6 +339,8 @@ typedef struct { char *display; /// The backend in use. enum backend backend; + /// Whether to avoid using stencil buffer under GLX backend. Might be unsafe. + bool glx_no_stencil; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -724,6 +726,8 @@ typedef struct _win { winmode_t mode; /// Whether the window has been damaged at least once. bool damaged; + /// Whether the window was damaged after last paint. + bool pixmap_damaged; /// Damage of the window. Damage damage; /// Paint info of the window. @@ -1574,9 +1578,13 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); +/** + * Check if a texture is binded, or is binded to the given pixmap. + */ static inline bool -glx_tex_binded(const glx_texture_t *ptex) { - return ptex && ptex->glpixmap && ptex->texture; +glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { + return ptex && ptex->glpixmap && ptex->texture + && (!pixmap || pixmap == ptex->pixmap); } void diff --git a/compton.c b/compton.c index df39a36f4..591fc7043 100644 --- a/compton.c +++ b/compton.c @@ -474,7 +474,7 @@ win_build_shadow(session_t *ps, win *w, double opacity) { w->shadow_paint.pixmap = shadow_pixmap_argb; w->shadow_paint.pict = shadow_picture_argb; - bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32); + bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32, true); XFreeGC(ps->dpy, gc); XDestroyImage(shadow_image); @@ -1281,36 +1281,6 @@ paint_preprocess(session_t *ps, win *list) { redir_start(ps); } - // Fetch pictures only if windows are redirected - if (ps->redirected) { - for (w = t; w; w = w->prev_trans) { - // Fetch Pixmap - if (!w->paint.pixmap && ps->has_name_pixmap) { - set_ignore_next(ps); - w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); - } - // XRender: Build picture - if (BKEND_XRENDER == ps->o.backend && !w->paint.pict) { - Drawable draw = w->paint.pixmap; - if (!draw) - draw = w->id; - { - XRenderPictureAttributes pa = { - .subwindow_mode = IncludeInferiors, - }; - - w->paint.pict = XRenderCreatePicture(ps->dpy, draw, w->pictfmt, - CPSubwindowMode, &pa); - } - } - // OpenGL: Build texture - if (!paint_bind_tex(ps, &w->paint, w->widthb, w->heightb, - w->pictfmt->depth)) { - printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id); - } - } - } - return t; } @@ -1447,6 +1417,32 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, */ static inline void win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { + // Fetch Pixmap + if (!w->paint.pixmap && ps->has_name_pixmap) { + set_ignore_next(ps); + w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); + } + // XRender: Build picture + if (BKEND_XRENDER == ps->o.backend && !w->paint.pict) { + Drawable draw = w->paint.pixmap; + if (!draw) + draw = w->id; + { + XRenderPictureAttributes pa = { + .subwindow_mode = IncludeInferiors, + }; + + w->paint.pict = XRenderCreatePicture(ps->dpy, draw, w->pictfmt, + CPSubwindowMode, &pa); + } + } + // GLX: Build texture + if (!paint_bind_tex(ps, &w->paint, w->widthb, w->heightb, + w->pictfmt->depth, w->pixmap_damaged)) { + printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id); + } + w->pixmap_damaged = false; + if (!paint_isvalid(ps, &w->paint)) { printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id); return; @@ -1664,6 +1660,21 @@ paint_all(session_t *ps, XserverRegion region, win *t) { reg_paint = reg_tmp; XFixesIntersectRegion(ps->dpy, reg_paint, region, w->extents); } + + // Might be worthwhile to crop the region to shadow border + { + XRectangle rec_shadow_border = { + .x = w->a.x + w->shadow_dx, + .y = w->a.y + w->shadow_dy, + .width = w->shadow_width, + .height = w->shadow_height + }; + XserverRegion reg_shadow = XFixesCreateRegion(ps->dpy, + &rec_shadow_border, 1); + XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, reg_shadow); + free_region(ps, ®_shadow); + } + // Clear the shadow here instead of in make_shadow() for saving GPU // power and handling shaped windows if (ps->o.clear_shadow && w->border_size) @@ -1837,6 +1848,7 @@ repair_win(session_t *ps, win *w) { add_damage(ps, parts); w->damaged = true; + w->pixmap_damaged = true; } static wintype_t @@ -2469,6 +2481,7 @@ add_win(session_t *ps, Window id, Window prev) { .mode = WMODE_TRANS, .damaged = false, .damage = None, + .pixmap_damaged = false, .paint = PAINT_INIT, .border_size = None, .extents = None, @@ -4104,6 +4117,10 @@ usage(void) { " inverted color. Resource-hogging, and is not well tested.\n" "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" + "--glx-no-stencil\n" + " Avoid using stencil buffer under GLX backend. Might cause issues\n" + " when rendering transparent content, may have a positive or\n" + " negative effect on performance.\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4560,6 +4577,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "invert-color-include", required_argument, NULL, 288 }, { "opengl", no_argument, NULL, 289 }, { "backend", required_argument, NULL, 290 }, + { "glx-no-stencil", no_argument, NULL, 291 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4822,6 +4840,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (!parse_backend(ps, optarg)) exit(1); break; + case 291: + // --glx-no-stencil + ps->o.glx_no_stencil = true; + break; default: usage(); break; @@ -5619,6 +5641,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .config_file = NULL, .display = NULL, .backend = BKEND_XRENDER, + .glx_no_stencil = false, .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, diff --git a/compton.h b/compton.h index 49ffcb88f..9c4bd0a9f 100644 --- a/compton.h +++ b/compton.h @@ -169,7 +169,7 @@ paint_isvalid(session_t *ps, const paint_t *ppaint) { return false; #ifdef CONFIG_VSYNC_OPENGL - if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex)) + if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, None)) return false; #endif @@ -179,10 +179,12 @@ paint_isvalid(session_t *ps, const paint_t *ppaint) { * Bind texture in paint_t if we are using GLX backend. */ static inline bool -paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth) { +paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth, + bool force) { #ifdef CONFIG_VSYNC_OPENGL // TODO: Make sure we have the same Pixmap binded? - if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex)) { + if (BKEND_GLX == ps->o.backend + && (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))) { return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); } #endif diff --git a/opengl.c b/opengl.c index 2ec0b3313..b32368e2f 100644 --- a/opengl.c +++ b/opengl.c @@ -71,7 +71,7 @@ glx_init(session_t *ps, bool need_render) { // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles // in regions don't overlap, so we must use stencil buffer to make sure // we don't paint a region for more than one time, I think? - if (need_render) { + if (need_render && !ps->o.glx_no_stencil) { GLint val = 0; glGetIntegerv(GL_STENCIL_BITS, &val); if (!val) { @@ -110,11 +110,13 @@ glx_init(session_t *ps, bool need_render) { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); - // Initialize stencil buffer - glClear(GL_STENCIL_BUFFER_BIT); - glEnable(GL_STENCIL_TEST); - glStencilMask(0x1); - glStencilFunc(GL_EQUAL, 0x1, 0x1); + if (!ps->o.glx_no_stencil) { + // Initialize stencil buffer + glClear(GL_STENCIL_BUFFER_BIT); + glDisable(GL_STENCIL_TEST); + glStencilMask(0x1); + glStencilFunc(GL_EQUAL, 0x1, 0x1); + } // Clear screen glClearColor(0.0f, 0.0f, 0.0f, 1.0f); @@ -466,9 +468,14 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { */ void glx_set_clip(session_t *ps, XserverRegion reg) { - glClear(GL_STENCIL_BUFFER_BIT); + // Quit if we aren't using stencils + if (ps->o.glx_no_stencil) + return; if (reg) { + glEnable(GL_STENCIL_TEST); + glClear(GL_STENCIL_BUFFER_BIT); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); @@ -504,6 +511,9 @@ glx_set_clip(session_t *ps, XserverRegion reg) { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); } + else { + glDisable(GL_STENCIL_TEST); + } } /** @@ -560,15 +570,15 @@ glx_render(session_t *ps, const glx_texture_t *ptex, printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); #endif - /* - if (reg_tgt) { + // On no-stencil mode, calculate painting region here instead of relying + // on stencil buffer + if (ps->o.glx_no_stencil && reg_tgt) { reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1); XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt); nrects = 0; rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); } - */ glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, ptex->texture); From 848103bc3447f814c51cb818836533e18e018bf5 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 18 Mar 2013 11:48:28 +0800 Subject: [PATCH 117/178] Bug fix: GLX: ARGB texture too dark & Jitter when resize & others - GLX backend: Fix a bug that ARGB windows / shadows are rendered too dark. Thanks to derhass in FreeNode/##opengl for help. - GLX backend: Fix a problem that during window resize the content looks jittering, by letting compton fetch pixmap sizes with XGetGeometry() instead of relying on window width/height, which could be inaccurate during window resize. Negative effect on performance. Thanks to M4he for reporting. (#7) - Add .desktop file. Thanks to quequotion for providing it. (#97) - Avoid checking presence of window pixmap, because they may not exist with very old X Composite implementations. - Add workaround for a strange window restack issue when compton receieves a ConfigureNotify with non-existent new above window. - Add debugging function hexdump(). Extra sanity checks on various places. --- common.h | 44 +++++++++++++++++++++++++++++++++++---- compton.c | 36 +++++++++++++++++++++++--------- compton.h | 18 +++++++++------- opengl.c | 61 ++++++++++++++++++++++++++++++++++++------------------- 4 files changed, 117 insertions(+), 42 deletions(-) diff --git a/common.h b/common.h index 77aa582f6..3b6620854 100644 --- a/common.h +++ b/common.h @@ -297,9 +297,9 @@ struct _glx_texture { GLuint texture; GLXPixmap glpixmap; Pixmap pixmap; - int width; - int height; - int depth; + unsigned width; + unsigned height; + unsigned depth; bool y_inverted; }; #endif @@ -1573,7 +1573,7 @@ glx_init_blur(session_t *ps); bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, - int width, int height, int depth); + unsigned width, unsigned height, unsigned depth); void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); @@ -1686,3 +1686,39 @@ c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, ///@} #endif + +/** + * @brief Dump raw bytes in HEX format. + * + * @param data pointer to raw data + * @param len length of data + */ +static inline void +hexdump(const char *data, int len) { + static const int BYTE_PER_LN = 16; + + if (len <= 0) + return; + + // Print header + printf("%10s:", "Offset"); + for (int i = 0; i < BYTE_PER_LN; ++i) + printf(" %2d", i); + putchar('\n'); + + // Dump content + for (int offset = 0; offset < len; ++offset) { + if (!(offset % BYTE_PER_LN)) + printf("0x%08x:", offset); + + printf(" %02hhx", data[offset]); + + if ((BYTE_PER_LN - 1) == offset % BYTE_PER_LN) + putchar('\n'); + } + if (len % BYTE_PER_LN) + putchar('\n'); + + fflush(stdout); +} + diff --git a/compton.c b/compton.c index 591fc7043..539f8d201 100644 --- a/compton.c +++ b/compton.c @@ -474,6 +474,7 @@ win_build_shadow(session_t *ps, win *w, double opacity) { w->shadow_paint.pixmap = shadow_pixmap_argb; w->shadow_paint.pict = shadow_picture_argb; + bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32, true); XFreeGC(ps->dpy, gc); @@ -863,8 +864,7 @@ get_root_tile(session_t *ps) { ps->root_tile_paint.pixmap = pixmap; #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend) - return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, ps->root_tile_paint.pixmap, - ps->root_width, ps->root_height, ps->depth); + return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex, ps->root_tile_paint.pixmap, 0, 0, 0); #endif return true; @@ -1437,8 +1437,10 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { } } // GLX: Build texture - if (!paint_bind_tex(ps, &w->paint, w->widthb, w->heightb, - w->pictfmt->depth, w->pixmap_damaged)) { + // Let glx_bind_pixmap() determine pixmap size, because if the user + // is resizing windows, the width and height we get may not be up-to-date, + // causing the jittering issue M4he reported in #7. + if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, w->pixmap_damaged)) { printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id); } w->pixmap_damaged = false; @@ -2633,21 +2635,33 @@ restack_win(session_t *ps, win *w, Window new_above) { } if (old_above != new_above) { - win **prev; + win **prev = NULL, **prev_old = NULL; - /* unhook */ + // unhook for (prev = &ps->list; *prev; prev = &(*prev)->next) { if ((*prev) == w) break; } - *prev = w->next; + prev_old = prev; + + bool found = false; - /* rehook */ + // rehook for (prev = &ps->list; *prev; prev = &(*prev)->next) { - if ((*prev)->id == new_above && !(*prev)->destroyed) + if ((*prev)->id == new_above && !(*prev)->destroyed) { + found = true; break; + } } + if (!found) { + printf_errf("(%#010lx, %#010lx): " + "Failed to found new above window.", w->id, new_above); + return; + } + + *prev_old = w->next; + w->next = *prev; *prev = w; @@ -2668,7 +2682,9 @@ restack_win(session_t *ps, win *w, Window new_above) { desc = ""; if (c->destroyed) desc = "(D) "; - printf("%#010lx \"%s\" %s-> ", c->id, window_name, desc); + printf("%#010lx \"%s\" %s", c->id, window_name, desc); + if (c->next) + printf("-> "); if (to_free) { XFree(window_name); diff --git a/compton.h b/compton.h index 9c4bd0a9f..c16c59612 100644 --- a/compton.h +++ b/compton.h @@ -162,7 +162,9 @@ free_wincondlst(c2_lptr_t **pcondlst) { */ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) { - if (!ppaint || !ppaint->pixmap) + // Don't check for presence of Pixmap here, because older X Composite doesn't + // provide it + if (!ppaint) return false; if (BKEND_XRENDER == ps->o.backend && !ppaint->pict) @@ -179,13 +181,15 @@ paint_isvalid(session_t *ps, const paint_t *ppaint) { * Bind texture in paint_t if we are using GLX backend. */ static inline bool -paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth, - bool force) { +paint_bind_tex(session_t *ps, paint_t *ppaint, + unsigned wid, unsigned hei, unsigned depth, bool force) { #ifdef CONFIG_VSYNC_OPENGL - // TODO: Make sure we have the same Pixmap binded? - if (BKEND_GLX == ps->o.backend - && (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))) { - return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); + if (BKEND_GLX == ps->o.backend) { + if (!ppaint->pixmap) + return false; + + if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) + return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); } #endif diff --git a/opengl.c b/opengl.c index b32368e2f..21b3c181f 100644 --- a/opengl.c +++ b/opengl.c @@ -107,7 +107,6 @@ glx_init(session_t *ps, bool need_render) { // glEnable(GL_DEPTH_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); if (!ps->o.glx_no_stencil) { @@ -322,15 +321,10 @@ glx_cmp_fbconfig(session_t *ps, */ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, - int width, int height, int depth) { - if (depth > OPENGL_MAX_DEPTH) { - printf_errf("(%d): Requested depth higher than %d.", depth, - OPENGL_MAX_DEPTH); - return false; - } - const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth]; - if (!pcfg) { - printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth); + unsigned width, unsigned height, unsigned depth) { + if (!pixmap) { + printf_errf("(%#010lx): Binding to an empty pixmap. This can't work.", + pixmap); return false; } @@ -359,8 +353,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, glEnable(target); // Release pixmap if parameters are inconsistent - if (ptex->texture && !(width == ptex->width && height == ptex->height - && ptex->pixmap == pixmap && depth == ptex->depth)) { + if (ptex->texture && ptex->pixmap != pixmap) { glx_release_pixmap(ps, ptex); } @@ -380,7 +373,6 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, glBindTexture(target, 0); ptex->texture = texture; - ptex->y_inverted = pcfg->y_inverted; } if (!ptex->texture) { printf_errf("(): Failed to allocate texture."); @@ -393,6 +385,29 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, if (!ptex->glpixmap) { need_release = false; + // Retrieve pixmap parameters, if they aren't provided + if (!(width && height && depth)) { + Window rroot = None; + int rx = 0, ry = 0; + unsigned rbdwid = 0; + if (!XGetGeometry(ps->dpy, pixmap, &rroot, &rx, &ry, + &width, &height, &rbdwid, &depth)) { + printf_errf("(%#010lx): Failed to query Pixmap info.", pixmap); + return false; + } + if (depth > OPENGL_MAX_DEPTH) { + printf_errf("(%d): Requested depth higher than %d.", depth, + OPENGL_MAX_DEPTH); + return false; + } + } + + const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth]; + if (!pcfg) { + printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth); + return false; + } + // Determine texture target, copied from compiz GLint tex_tgt = 0; if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts @@ -419,6 +434,11 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, }; ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs); + ptex->pixmap = pixmap; + ptex->width = width; + ptex->height = height; + ptex->depth = depth; + ptex->y_inverted = pcfg->y_inverted; } if (!ptex->glpixmap) { printf_errf("(): Failed to allocate GLX pixmap."); @@ -436,11 +456,6 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, glBindTexture(target, 0); glDisable(target); - ptex->width = width; - ptex->height = height; - ptex->depth = depth; - ptex->pixmap = pixmap; - return true; } @@ -493,7 +508,7 @@ glx_set_clip(session_t *ps, XserverRegion reg) { GLint z = 0; #ifdef DEBUG_GLX - printf_dbgf("(): Rect %d: %f, %f, %f, %f\n", i, rx, ry, rxe, rye); + printf_dbgf("(): Rect %d: %d, %d, %d, %d\n", i, rx, ry, rxe, rye); #endif glVertex3i(rx, ry, z); @@ -532,10 +547,14 @@ glx_render(session_t *ps, const glx_texture_t *ptex, if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == ps->glx_fbconfigs[ptex->depth]->texture_fmt) { glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // Needed for handling opacity of ARGB texture glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glColor4f(1.0f, 1.0f, 1.0f, opacity); + + // This is all weird, but X Render is using a strange ARGB format, and + // we need to use those things to correct it. Thanks to derhass for help. + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(opacity, opacity, opacity, opacity); } // Color negation From 69c3579a24635e9504bceabc1c76dfe96342c3b0 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 18 Mar 2013 19:01:18 +0800 Subject: [PATCH 118/178] Improvement: GLX: Use SCISSOR_TEST instead of STENCIL_TEST when possible - GLX backend: Use GL_SCISSOR_TEST instead of STENCIL_TEST if there's only one rectangle in glx_set_clip(). Profiling with gDebugger shows a 10% performance improvement. - Add .desktop installation rules. (#97) --- compton.c | 2 +- opengl.c | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/compton.c b/compton.c index 539f8d201..a2fbee265 100644 --- a/compton.c +++ b/compton.c @@ -4136,7 +4136,7 @@ usage(void) { "--glx-no-stencil\n" " Avoid using stencil buffer under GLX backend. Might cause issues\n" " when rendering transparent content, may have a positive or\n" - " negative effect on performance.\n" + " negative effect on performance. (My test shows a 10% slowdown.)\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED diff --git a/opengl.c b/opengl.c index 21b3c181f..92b0292b0 100644 --- a/opengl.c +++ b/opengl.c @@ -487,7 +487,31 @@ glx_set_clip(session_t *ps, XserverRegion reg) { if (ps->o.glx_no_stencil) return; - if (reg) { + static XRectangle rect_blank = { + .x = 0, .y = 0, .width = 0, .height = 0 + }; + + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + + if (!reg) + return; + + int nrects = 0; + XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects); + if (!nrects) { + if (rects) XFree(rects); + nrects = 1; + rects = &rect_blank; + } + + assert(nrects); + if (1 == nrects) { + glEnable(GL_SCISSOR_TEST); + glScissor(rects[0].x, ps->root_height - rects[0].y - rects[0].height, + rects[0].width, rects[0].height); + } + else { glEnable(GL_STENCIL_TEST); glClear(GL_STENCIL_BUFFER_BIT); @@ -495,8 +519,6 @@ glx_set_clip(session_t *ps, XserverRegion reg) { glDepthMask(GL_FALSE); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); - int nrects = 0; - XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects); glBegin(GL_QUADS); @@ -519,16 +541,13 @@ glx_set_clip(session_t *ps, XserverRegion reg) { glEnd(); - if (rects) - XFree(rects); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); } - else { - glDisable(GL_STENCIL_TEST); - } + + if (rects && &rect_blank != rects) + XFree(rects); } /** From cdd6a73836cc2c1943cdbceab4328dec96949d51 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 19 Mar 2013 20:58:55 +0800 Subject: [PATCH 119/178] Improvement: --glx-copy-from-front & benchmark mode - GLX backend: Add --glx-copy-front, which copies all unmodified regions from front buffer to back buffer instead of redrawing the whole screen. Unfortunately, probably because glCopyPixels() isn't usually well-optimized on graphic cards, this option appears very unstable in its effect: Over 20% boost in performance when only 1/4 of the screen is modified, but 10% decrease when the whole screen is. Thus, not enabled by default. - GLX backend: Add glx_blur_dst(), to prepare for the background blur feature. It currently is capable to modify background in the desired way, but the core blur shader is absent. glConvolution2D() seemingly relies entirely on CPU and has horrifying performance. I've hesitating about whether I should use ARB assembly language or GLSL for the shader. - GLX backend: Invert y-axis GL matrix. It's upside-down previously because that's what dcompmgr uses. Seemingly a "normal" y-axis is required for glCopyPixels() to operate correctly. - GLX backend: Fix some GLX_TEXTURE_RECTANGLE compatibility issues. Still, not actually tested. - Add benchmark mode (--benchmark & --benchmark-wid). - Misc changes. --- common.h | 10 +++ compton.c | 60 +++++++++++++--- opengl.c | 199 +++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 219 insertions(+), 50 deletions(-) diff --git a/common.h b/common.h index 3b6620854..978a9ecb8 100644 --- a/common.h +++ b/common.h @@ -297,6 +297,7 @@ struct _glx_texture { GLuint texture; GLXPixmap glpixmap; Pixmap pixmap; + GLenum target; unsigned width; unsigned height; unsigned depth; @@ -341,6 +342,8 @@ typedef struct { enum backend backend; /// Whether to avoid using stencil buffer under GLX backend. Might be unsafe. bool glx_no_stencil; + /// Whether to copy unmodified regions from front buffer. + bool glx_copy_from_front; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -359,6 +362,10 @@ typedef struct { bool dbus; /// Path to log file. char *logpath; + /// Number of cycles to paint in benchmark mode. 0 for disabled. + int benchmark; + /// Window to constantly repaint in benchmark mode. 0 for full-screen. + Window benchmark_wid; /// Whether to work under synchronized mode for debugging. bool synchronize; @@ -1578,6 +1585,9 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); +void +glx_paint_pre(session_t *ps, XserverRegion *preg); + /** * Check if a texture is binded, or is binded to the given pixmap. */ diff --git a/compton.c b/compton.c index a2fbee265..2b1ca9204 100644 --- a/compton.c +++ b/compton.c @@ -1569,12 +1569,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; #ifdef CONFIG_VSYNC_OPENGL - // GLX backend: OpenGL doesn't support partial repaint without - // GLX_MESA_copy_sub_buffer if (BKEND_GLX == ps->o.backend) { - free_region(ps, ®ion); - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - ps->glx_z = 0.0; + glx_paint_pre(ps, ®ion); } #endif @@ -4134,9 +4130,14 @@ usage(void) { "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" - " Avoid using stencil buffer under GLX backend. Might cause issues\n" - " when rendering transparent content, may have a positive or\n" + " GLX backend: Avoid using stencil buffer. Might cause issues\n" + " when rendering transparent content. May have a positive or\n" " negative effect on performance. (My test shows a 10% slowdown.)\n" + "--glx-copy-from-front\n" + " GLX backend: Copy unmodified regions from front buffer instead of\n" + " redrawing them all. My tests show a 10% decrease in performance\n" + " when the whole screen is modified, but a 20% increase when only 1/4\n" + " is, so this optimization is not enabled by default.\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4145,7 +4146,13 @@ usage(void) { #endif "--dbus\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n" - " man page for more details." WARNING "\n"; + " man page for more details." WARNING "\n" + "--benchmark cycles\n" + " Benchmark mode. Repeatedly paint until reaching the specified cycles.\n" + "--benchmark-wid window-id\n" + " Specify window ID to repaint in benchmark mode. If omitted or is 0,\n" + " the whole screen is repainted.\n" + ; fputs(usage_text , stderr); #undef WARNING #undef WARNING_DISABLED @@ -4594,6 +4601,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "opengl", no_argument, NULL, 289 }, { "backend", required_argument, NULL, 290 }, { "glx-no-stencil", no_argument, NULL, 291 }, + { "glx-copy-from-front", no_argument, NULL, 292 }, + { "benchmark", required_argument, NULL, 293 }, + { "benchmark-wid", required_argument, NULL, 294 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4860,6 +4870,18 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --glx-no-stencil ps->o.glx_no_stencil = true; break; + case 292: + // --glx-copy-from-front + ps->o.glx_copy_from_front = true; + break; + case 293: + // --benchmark + ps->o.benchmark = atoi(optarg); + break; + case 294: + // --benchmark-wid + ps->o.benchmark_wid = strtol(optarg, NULL, 0); + break; default: usage(); break; @@ -5575,7 +5597,7 @@ mainloop(session_t *ps) { struct timeval *ptv = NULL; { // Consider ev_received firstly - if (ps->ev_received) { + if (ps->ev_received || ps->o.benchmark) { ptv = malloc(sizeof(struct timeval)); ptv->tv_sec = 0L; ptv->tv_usec = 0L; @@ -5658,6 +5680,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .display = NULL, .backend = BKEND_XRENDER, .glx_no_stencil = false, + .glx_copy_from_front = false, .mark_wmwin_focused = false, .mark_ovredir_focused = false, .fork_after_register = false, @@ -5666,6 +5689,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .paint_on_overlay = false, .unredir_if_possible = false, .dbus = false, + .benchmark = 0, + .benchmark_wid = None, .logpath = NULL, .refresh_rate = 0, @@ -6249,6 +6274,21 @@ session_run(session_t *ps) { while (mainloop(ps)) continue; + if (ps->o.benchmark) { + if (ps->o.benchmark_wid) { + win *w = find_win(ps, ps->o.benchmark_wid); + if (!w) { + printf_errf("(): Couldn't find specified benchmark window."); + session_destroy(ps); + exit(1); + } + add_damage_win(ps, w); + } + else { + force_repaint(ps); + } + } + // idling will be turned off during paint_preprocess() if needed ps->idling = true; @@ -6263,6 +6303,8 @@ session_run(session_t *ps) { paint_all(ps, ps->all_damage, t); ps->reg_ignore_expire = false; paint++; + if (ps->o.benchmark && paint >= ps->o.benchmark) + exit(0); XSync(ps->dpy, False); ps->all_damage = None; } diff --git a/opengl.c b/opengl.c index 92b0292b0..eca91667b 100644 --- a/opengl.c +++ b/opengl.c @@ -163,7 +163,7 @@ glx_on_root_change(session_t *ps) { // Initialize matrix, copied from dcompmgr glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0); + glOrtho(0, ps->root_width, 0, ps->root_height, -1000.0, 1000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } @@ -328,7 +328,6 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, return false; } - const GLenum target = GL_TEXTURE_2D; glx_texture_t *ptex = *pptex; bool need_release = true; @@ -338,6 +337,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, .texture = 0, .glpixmap = 0, .pixmap = 0, + .target = 0, .width = 0, .height = 0, .depth = 0, @@ -350,37 +350,11 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, *pptex = ptex; } - glEnable(target); - // Release pixmap if parameters are inconsistent if (ptex->texture && ptex->pixmap != pixmap) { glx_release_pixmap(ps, ptex); } - // Create texture - if (!ptex->texture) { - need_release = false; - - GLuint texture = 0; - glGenTextures(1, &texture); - glBindTexture(target, texture); - - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindTexture(target, 0); - - ptex->texture = texture; - } - if (!ptex->texture) { - printf_errf("(): Failed to allocate texture."); - return false; - } - - glBindTexture(target, ptex->texture); - // Create GLX pixmap if (!ptex->glpixmap) { need_release = false; @@ -409,7 +383,9 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, } // Determine texture target, copied from compiz - GLint tex_tgt = 0; + // The assumption we made here is the target never changes based on any + // pixmap-specific parameters, and this may change in the future + GLenum tex_tgt = 0; if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts && ps->glx_has_texture_non_power_of_two) tex_tgt = GLX_TEXTURE_2D_EXT; @@ -435,6 +411,8 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs); ptex->pixmap = pixmap; + ptex->target = (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D: + GL_TEXTURE_RECTANGLE); ptex->width = width; ptex->height = height; ptex->depth = depth; @@ -445,6 +423,32 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, return false; } + glEnable(ptex->target); + + // Create texture + if (!ptex->texture) { + need_release = false; + + GLuint texture = 0; + glGenTextures(1, &texture); + glBindTexture(ptex->target, texture); + + glTexParameteri(ptex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(ptex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(ptex->target, 0); + + ptex->texture = texture; + } + if (!ptex->texture) { + printf_errf("(): Failed to allocate texture."); + return false; + } + + glBindTexture(ptex->target, ptex->texture); + // The specification requires rebinding whenever the content changes... // We can't follow this, too slow. if (need_release) @@ -453,8 +457,8 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); // Cleanup - glBindTexture(target, 0); - glDisable(target); + glBindTexture(ptex->target, 0); + glDisable(ptex->target); return true; } @@ -466,9 +470,9 @@ void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { // Release binding if (ptex->glpixmap && ptex->texture) { - glBindTexture(GL_TEXTURE_2D, ptex->texture); + glBindTexture(ptex->target, ptex->texture); ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); - glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(ptex->target, 0); } // Free GLX Pixmap @@ -478,6 +482,41 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { } } +/** + * Preprocess function before start painting. + */ +void +glx_paint_pre(session_t *ps, XserverRegion *preg) { + ps->glx_z = 0.0; + // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, + // we currently redraw the whole screen or copy unmodified pixels from + // front buffer with --glx-copy-from-front. + if (!ps->o.glx_copy_from_front || !*preg) { + free_region(ps, preg); + } + else { + { + XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); + XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); + glx_set_clip(ps, reg_copy); + free_region(ps, ®_copy); + } + + { + GLfloat raster_pos[4]; + glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos); + glReadBuffer(GL_FRONT); + glRasterPos2f(0.0, 0.0); + glCopyPixels(0, 0, ps->root_width, ps->root_height, GL_COLOR); + glReadBuffer(GL_BACK); + glRasterPos4fv(raster_pos); + } + } + + glx_set_clip(ps, *preg); +} + /** * Set clipping region on the target window. */ @@ -524,9 +563,9 @@ glx_set_clip(session_t *ps, XserverRegion reg) { for (int i = 0; i < nrects; ++i) { GLint rx = rects[i].x; - GLint ry = rects[i].y; + GLint ry = ps->root_height - rects[i].y; GLint rxe = rx + rects[i].width; - GLint rye = ry + rects[i].height; + GLint rye = ry - rects[i].height; GLint z = 0; #ifdef DEBUG_GLX @@ -550,6 +589,78 @@ glx_set_clip(session_t *ps, XserverRegion reg) { XFree(rects); } +bool +glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { + // Read destination pixels into a texture + GLuint tex_scr = 0; + glGenTextures(1, &tex_scr); + if (!tex_scr) { + printf_errf("(): Failed to allocate texture."); + return false; + } + + GLenum tex_tgt = GL_TEXTURE_RECTANGLE; + if (ps->glx_has_texture_non_power_of_two) + tex_tgt = GL_TEXTURE_2D; + + glEnable(tex_tgt); + glBindTexture(tex_tgt, tex_scr); + glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + glCopyTexSubImage2D(tex_tgt, 0, 0, 0, dx, ps->root_height - dy - height, width, height); + +#ifdef DEBUG_GLX + printf_dbgf("(): %d, %d, %d, %d\n", dx, ps->root_height - dy - height, width, height); +#endif + + // Paint it back + // TODO: Blur function. We are using color negation for testing now. + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + + glBegin(GL_QUADS); + + { + const GLfloat rx = 0.0; + const GLfloat ry = 1.0; + const GLfloat rxe = 1.0; + const GLfloat rye = 0.0; + const GLint rdx = dx; + const GLint rdy = ps->root_height - dy; + const GLint rdxe = rdx + width; + const GLint rdye = rdy - height; + +#ifdef DEBUG_GLX + printf_dbgf("(): %f, %f, %f, %f -> %d, %d, %d, %d\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); +#endif + + glTexCoord2f(rx, ry); + glVertex3f(rdx, rdy, z); + + glTexCoord2f(rxe, ry); + glVertex3f(rdxe, rdy, z); + + glTexCoord2f(rxe, rye); + glVertex3f(rdxe, rdye, z); + + glTexCoord2f(rx, rye); + glVertex3f(rdx, rdye, z); + } + + glEnd(); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glBindTexture(tex_tgt, 0); + glDeleteTextures(1, &tex_scr); + glDisable(tex_tgt); + + return true; +} + /** * @brief Render a region with texture data. */ @@ -557,6 +668,8 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool neg, XserverRegion reg_tgt) { + bool blur_background = false; + if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -565,12 +678,15 @@ glx_render(session_t *ps, const glx_texture_t *ptex, // Enable blending if needed if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == ps->glx_fbconfigs[ptex->depth]->texture_fmt) { + if (!ps->o.glx_no_stencil && blur_background) + glx_blur_dst(ps, dx, dy, width, height, z - 0.5); + glEnable(GL_BLEND); // Needed for handling opacity of ARGB texture glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - // This is all weird, but X Render is using a strange ARGB format, and + // This is all weird, but X Render is using premulitplied ARGB format, and // we need to use those things to correct it. Thanks to derhass for help. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColor4f(opacity, opacity, opacity, opacity); @@ -618,8 +734,8 @@ glx_render(session_t *ps, const glx_texture_t *ptex, rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); } - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, ptex->texture); + glEnable(ptex->target); + glBindTexture(ptex->target, ptex->texture); glBegin(GL_QUADS); @@ -629,9 +745,9 @@ glx_render(session_t *ps, const glx_texture_t *ptex, GLfloat rxe = rx + (double) rects[i].width / ptex->width; GLfloat rye = ry + (double) rects[i].height / ptex->height; GLint rdx = rects[i].x; - GLint rdy = rects[i].y; + GLint rdy = ps->root_height - rects[i].y; GLint rdxe = rdx + rects[i].width; - GLint rdye = rdy + rects[i].height; + GLint rdye = rdy - rects[i].height; // Invert Y if needed, this may not work as expected, though. I don't // have such a FBConfig to test with. @@ -665,11 +781,12 @@ glx_render(session_t *ps, const glx_texture_t *ptex, } // Cleanup - glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(ptex->target, 0); glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); glDisable(GL_COLOR_LOGIC_OP); + glDisable(ptex->target); return true; } From da85de48a9d078eeb5a437482c41c85884496318 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 20 Mar 2013 17:29:45 +0800 Subject: [PATCH 120/178] Feature #69: GLX: Blur background - GLX backend: Add blur background support using a GLSL shader. Only tested with nvidia-drivers-313.26. Known to cause quite some decrease in performance (~10%?). - Detach shaders in glx_create_program(). Misc changes. --- common.h | 24 ++++++++++++ compton.c | 98 ++++++++++++++++++++++++++-------------------- opengl.c | 113 ++++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 170 insertions(+), 65 deletions(-) diff --git a/common.h b/common.h index 978a9ecb8..977643b60 100644 --- a/common.h +++ b/common.h @@ -626,6 +626,18 @@ typedef struct { f_ReleaseTexImageEXT glXReleaseTexImageProc; /// FBConfig-s for GLX pixmap of different depths. glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Fragment shader for blur. + GLuint glx_frag_shader_blur; + /// GLSL program for blur. + GLuint glx_prog_blur; + /// Location of uniform "offset_x" in blur GLSL program. + GLint glx_prog_blur_unifm_offset_x; + /// Location of uniform "offset_y" in blur GLSL program. + GLint glx_prog_blur_unifm_offset_y; + /// Location of uniform "factor_center" in blur GLSL program. + GLint glx_prog_blur_unifm_factor_center; +#endif #endif // === X extension related === @@ -1600,10 +1612,22 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { void glx_set_clip(session_t *ps, XserverRegion reg); +bool +glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor_center); + bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool neg, XserverRegion reg_tgt); + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +GLuint +glx_create_shader(GLenum shader_type, const char *shader_str); + +GLuint +glx_create_program(const GLuint * const shaders, int nshaders); +#endif #endif static inline void diff --git a/compton.c b/compton.c index 2b1ca9204..9e8623ea8 100644 --- a/compton.c +++ b/compton.c @@ -1329,60 +1329,76 @@ win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { static inline void win_blur_background(session_t *ps, win *w, Picture tgt_buffer, XserverRegion reg_paint) { - const static int convolution_blur_size = 3; - // Convolution filter parameter (box blur) - // gaussian or binomial filters are definitely superior, yet looks - // like they aren't supported as of xorg-server-1.13.0 - XFixed convolution_blur[] = { - // Must convert to XFixed with XDoubleToFixed() - // Matrix size - XDoubleToFixed(convolution_blur_size), - XDoubleToFixed(convolution_blur_size), - // Matrix - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - }; - const int x = w->a.x; const int y = w->a.y; const int wid = w->widthb; const int hei = w->heightb; - // Directly copying from tgt_buffer does not work, so we create a - // Picture in the middle. - Picture tmp_picture = win_build_picture(ps, w, NULL); - - if (!tmp_picture) - return; - + double factor_center = 1.0; // Adjust blur strength according to window opacity, to make it appear // better during fading if (!ps->o.blur_background_fixed) { double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0); - convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(pct * 8.0 / (1.1 - pct)); + factor_center = pct * 8.0 / (1.1 - pct); } - // Minimize the region we try to blur, if the window itself is not - // opaque, only the frame is. - if (WMODE_SOLID == w->mode && w->frame_opacity) { - XserverRegion reg_all = border_size(ps, w, false); - XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); - XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); - XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_noframe, 0, 0); - free_region(ps, ®_all); - free_region(ps, ®_noframe); - } + switch (ps->o.backend) { + case BKEND_XRENDER: + { + const static int convolution_blur_size = 3; + // Convolution filter parameter (box blur) + // gaussian or binomial filters are definitely superior, yet looks + // like they aren't supported as of xorg-server-1.13.0 + XFixed convolution_blur[] = { + // Must convert to XFixed with XDoubleToFixed() + // Matrix size + XDoubleToFixed(convolution_blur_size), + XDoubleToFixed(convolution_blur_size), + // Matrix + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + }; + + // Directly copying from tgt_buffer does not work, so we create a + // Picture in the middle. + Picture tmp_picture = win_build_picture(ps, w, NULL); + + if (!tmp_picture) + return; - // Copy the content to tmp_picture, then copy back. The filter must - // be applied on tgt_buffer, to get the nearby pixels outside the - // window. - XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); - XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei); - xrfilter_reset(ps, tgt_buffer); - XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(factor_center); - free_picture(ps, &tmp_picture); + // Minimize the region we try to blur, if the window itself is not + // opaque, only the frame is. + if (WMODE_SOLID == w->mode && w->frame_opacity) { + XserverRegion reg_all = border_size(ps, w, false); + XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); + XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); + XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_noframe, 0, 0); + free_region(ps, ®_all); + free_region(ps, ®_noframe); + } + + // Copy the content to tmp_picture, then copy back. The filter must + // be applied on tgt_buffer, to get the nearby pixels outside the + // window. + XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); + XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei); + xrfilter_reset(ps, tgt_buffer); + XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + + free_picture(ps, &tmp_picture); + } + break; +#ifdef CONFIG_VSYNC_OPENGL + case BKEND_GLX: + glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center); + break; +#endif + default: + assert(0); + } } static void diff --git a/opengl.c b/opengl.c index eca91667b..a0bed63bb 100644 --- a/opengl.c +++ b/opengl.c @@ -140,6 +140,14 @@ glx_init_end: */ void glx_destroy(session_t *ps) { +#ifdef CONFIG_VSYNC_OPENGL_GLSL + // Free GLSL shaders/programs + if (ps->glx_frag_shader_blur) + glDeleteShader(ps->glx_frag_shader_blur); + if (ps->glx_prog_blur) + glDeleteProgram(ps->glx_prog_blur); +#endif + // Free FBConfigs for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) { free(ps->glx_fbconfigs[i]); @@ -173,20 +181,66 @@ glx_on_root_change(session_t *ps) { */ bool glx_init_blur(session_t *ps) { - printf_errf("(): Blur on GLX backend isn't implemented yet, sorry."); - return false; +#ifdef CONFIG_VSYNC_OPENGL_GLSL + if (ps->o.glx_no_stencil) { + printf_errf("(): I'm afraid blur background won't work so well without " + "stencil buffer support."); + return false; + } + + // Build shader + static const char *FRAG_SHADER_BLUR = + "#version 110\n" + "uniform float offset_x;\n" + "uniform float offset_y;\n" + "uniform float factor_center;\n" + "uniform sampler2D tex_scr;\n" + "\n" + "void main() {\n" + " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y - offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y + offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y - offset_y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y));\n" + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y + offset_y));\n" + " gl_FragColor = sum / (factor_center + 8.0);\n" + "}\n" + ; + ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR); + if (!ps->glx_frag_shader_blur) { + printf_errf("(): Failed to create fragment shader."); + return false; + } + + ps->glx_prog_blur = glx_create_program(&ps->glx_frag_shader_blur, 1); + if (!ps->glx_prog_blur) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + +#define P_GET_UNIFM_LOC(name, target) { \ + ps->target = glGetUniformLocation(ps->glx_prog_blur, name); \ + if (ps->target < 0) { \ + printf_errf("(): Failed to get location of uniform '" name "'."); \ + return false; \ + } \ +} + + P_GET_UNIFM_LOC("factor_center", glx_prog_blur_unifm_factor_center); + P_GET_UNIFM_LOC("offset_x", glx_prog_blur_unifm_offset_x); + P_GET_UNIFM_LOC("offset_y", glx_prog_blur_unifm_offset_y); -// #ifdef CONFIG_VSYNC_OPENGL_GLSL -// static const char *FRAG_SHADER_BLUR = ""; -// GLuint frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR); -// if (!frag_shader) { -// printf_errf("(): Failed to create fragment shader for blurring."); -// return false; -// } -// #else -// printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); -// return false; -// #endif +#undef P_GET_UNIFM_LOC + + return true; +#else + printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); + return false; +#endif } /** @@ -590,7 +644,8 @@ glx_set_clip(session_t *ps, XserverRegion reg) { } bool -glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { +glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor_center) { // Read destination pixels into a texture GLuint tex_scr = 0; glGenTextures(1, &tex_scr); @@ -617,10 +672,18 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { #endif // Paint it back - // TODO: Blur function. We are using color negation for testing now. - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + // Color negation for testing... + // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + // glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + // glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + glUseProgram(ps->glx_prog_blur); + glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width); + glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height); + glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); +#endif glBegin(GL_QUADS); @@ -653,6 +716,9 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z) { glEnd(); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + glUseProgram(0); +#endif glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(tex_tgt, 0); glDeleteTextures(1, &tex_scr); @@ -668,8 +734,6 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool neg, XserverRegion reg_tgt) { - bool blur_background = false; - if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -678,8 +742,6 @@ glx_render(session_t *ps, const glx_texture_t *ptex, // Enable blending if needed if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == ps->glx_fbconfigs[ptex->depth]->texture_fmt) { - if (!ps->o.glx_no_stencil && blur_background) - glx_blur_dst(ps, dx, dy, width, height, z - 0.5); glEnable(GL_BLEND); @@ -832,8 +894,7 @@ glx_create_shader_end: } GLuint -glx_create_program(GLenum shader_type, const GLuint * const shaders, - int nshaders) { +glx_create_program(const GLuint * const shaders, int nshaders) { bool success = false; GLuint program = glCreateProgram(); if (!program) { @@ -863,6 +924,10 @@ glx_create_program(GLenum shader_type, const GLuint * const shaders, success = true; glx_create_program_end: + if (program) { + for (int i = 0; i < nshaders; ++i) + glDetachShader(program, shaders[i]); + } if (program && !success) { glDeleteProgram(program); program = 0; From 2dfe9d52ed6d83fd0f2cc6cc0d921701dcb8b017 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 21 Mar 2013 13:05:56 +0800 Subject: [PATCH 121/178] Improvement: --glx-use-copysubbuffermesa - GLX backend: Add --glx-use-copysubbuffermesa, to use MESA_copy_sub_buffer to do partial screen update. Huge performance boost on mesa drivers for partial screen updates, but does not work for nvidia-drivers and may break VSync. Automagically overrides --glx-copy-from-front. - Add rect_is_fullscreen() to reuse code. Misc changes. --- common.h | 18 ++++++++++++++++++ compton.c | 24 +++++++++++++++++++----- compton.h | 6 ++---- opengl.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/common.h b/common.h index 977643b60..2f8b6e20d 100644 --- a/common.h +++ b/common.h @@ -284,6 +284,8 @@ typedef int (*f_SwapIntervalSGI) (int interval); typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer); +typedef void (*f_CopySubBuffer) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); + /// @brief Wrapper of a GLX FBConfig. typedef struct { GLXFBConfig cfg; @@ -344,6 +346,8 @@ typedef struct { bool glx_no_stencil; /// Whether to copy unmodified regions from front buffer. bool glx_copy_from_front; + /// Whether to use glXCopySubBufferMESA() to update screen. + bool glx_use_copysubbuffermesa; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -624,6 +628,8 @@ typedef struct { f_BindTexImageEXT glXBindTexImageProc; /// Pointer to glXReleaseTexImageEXT function. f_ReleaseTexImageEXT glXReleaseTexImageProc; + /// Pointer to glXCopySubBufferMESA function. + f_CopySubBuffer glXCopySubBufferProc; /// FBConfig-s for GLX pixmap of different depths. glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; #ifdef CONFIG_VSYNC_OPENGL_GLSL @@ -1490,6 +1496,15 @@ free_region(session_t *ps, XserverRegion *p) { } } +/** + * Check if a rectangle includes the whole screen. + */ +static inline bool +rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { + return (x <= 0 && y <= 0 + && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height); +} + /** * Determine if a window has a specific property. * @@ -1621,6 +1636,9 @@ glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, double opacity, bool neg, XserverRegion reg_tgt); +void +glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str); diff --git a/compton.c b/compton.c index 9e8623ea8..811bbf17e 100644 --- a/compton.c +++ b/compton.c @@ -1736,7 +1736,6 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } // Free up all temporary regions - XFixesDestroyRegion(ps->dpy, region); XFixesDestroyRegion(ps->dpy, reg_tmp); XFixesDestroyRegion(ps->dpy, reg_tmp2); @@ -1783,7 +1782,10 @@ paint_all(session_t *ps, XserverRegion region, win *t) { break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + if (ps->o.glx_use_copysubbuffermesa) + glx_swap_copysubbuffermesa(ps, region); + else + glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; #endif default: @@ -1802,6 +1804,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } #endif + XFixesDestroyRegion(ps->dpy, region); + #ifdef DEBUG_REPAINT print_timestamp(ps); struct timespec now = get_time_timespec(); @@ -4151,9 +4155,14 @@ usage(void) { " negative effect on performance. (My test shows a 10% slowdown.)\n" "--glx-copy-from-front\n" " GLX backend: Copy unmodified regions from front buffer instead of\n" - " redrawing them all. My tests show a 10% decrease in performance\n" - " when the whole screen is modified, but a 20% increase when only 1/4\n" - " is, so this optimization is not enabled by default.\n" + " redrawing them all. My tests with nvidia-drivers show a 10% decrease\n" + " in performance when the whole screen is modified, but a 20% increase\n" + " when only 1/4 is. My tests on nouveau show terrible slowdown.\n" + "--glx-use-copysubbuffermesa\n" + " GLX backend: Use MESA_copy_sub_buffer to do partial screen update.\n" + " My tests on nouveau shows a 200% performance boost when only 1/4 of\n" + " the screen is updated. May break VSync and is not available on some\n" + " drivers. Overrides --glx-copy-from-front.\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4620,6 +4629,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-copy-from-front", no_argument, NULL, 292 }, { "benchmark", required_argument, NULL, 293 }, { "benchmark-wid", required_argument, NULL, 294 }, + { "glx-use-copysubbuffermesa", no_argument, NULL, 295 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4898,6 +4908,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --benchmark-wid ps->o.benchmark_wid = strtol(optarg, NULL, 0); break; + case 295: + // --glx-use-copysubbuffermesa + ps->o.glx_use_copysubbuffermesa = true; + break; default: usage(); break; diff --git a/compton.h b/compton.h index c16c59612..b590b0ead 100644 --- a/compton.h +++ b/compton.h @@ -427,10 +427,8 @@ dump_drawable(session_t *ps, Drawable drawable) { */ static inline bool win_is_fullscreen(session_t *ps, const win *w) { - return (w->a.x <= 0 && w->a.y <= 0 - && (w->a.x + w->widthb) >= ps->root_width - && (w->a.y + w->heightb) >= ps->root_height - && !w->bounding_shaped); + return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) + && !w->bounding_shaped; } static void diff --git a/opengl.c b/opengl.c index a0bed63bb..3f1f419b7 100644 --- a/opengl.c +++ b/opengl.c @@ -96,6 +96,15 @@ glx_init(session_t *ps, bool need_render) { printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT()."); goto glx_init_end; } + + if (ps->o.glx_use_copysubbuffermesa) { + ps->glXCopySubBufferProc = (f_CopySubBuffer) + glXGetProcAddress((const GLubyte *) "glXCopySubBufferMESA"); + if (!ps->glXCopySubBufferProc) { + printf_errf("(): Failed to acquire glXCopySubBufferMESA()."); + goto glx_init_end; + } + } } // Acquire FBConfigs @@ -543,10 +552,13 @@ void glx_paint_pre(session_t *ps, XserverRegion *preg) { ps->glx_z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, - // we currently redraw the whole screen or copy unmodified pixels from + // we could redraw the whole screen or copy unmodified pixels from // front buffer with --glx-copy-from-front. - if (!ps->o.glx_copy_from_front || !*preg) { + if (ps->o.glx_use_copysubbuffermesa || !*preg) { + } + else if (!ps->o.glx_copy_from_front) { free_region(ps, preg); } else { @@ -612,7 +624,6 @@ glx_set_clip(session_t *ps, XserverRegion reg) { glDepthMask(GL_FALSE); glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP); - glBegin(GL_QUADS); for (int i = 0; i < nrects; ++i) { @@ -853,6 +864,37 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; } +/** + * Swap buffer with glXCopySubBufferMESA(). + */ +void +glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { + int nrects = 0; + XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects); + + if (1 == nrects && rect_is_fullscreen(ps, rects[0].x, rects[0].y, + rects[0].width, rects[0].height)) { + glXSwapBuffers(ps->dpy, get_tgt_window(ps)); + } + else { + glx_set_clip(ps, None); + for (int i = 0; i < nrects; ++i) { + const int x = rects[i].x; + const int y = ps->root_height - rects[i].y - rects[i].height; + const int wid = rects[i].width; + const int hei = rects[i].height; + +#ifdef DEBUG_GLX + printf_dbgf("(): %d, %d, %d, %d\n", x, y, wid, hei); +#endif + ps->glXCopySubBufferProc(ps->dpy, get_tgt_window(ps), x, y, wid, hei); + } + } + + if (rects) + XFree(rects); +} + #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str) { From 8fdb6e49c387e729cc2ea7bb0ce55a3a53c36cd2 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 22 Mar 2013 16:44:03 +0800 Subject: [PATCH 122/178] Bug fix: Incorrect handling when a window is placed on bottom - Fix a bug that ConfigureNotify placing a window in bottom (ConfigureNotify with .new_above == 0) is not correctly handled, introduced in 1a88e3d0c5. --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 811bbf17e..f37746aa1 100644 --- a/compton.c +++ b/compton.c @@ -2670,7 +2670,7 @@ restack_win(session_t *ps, win *w, Window new_above) { } } - if (!found) { + if (new_above && !found) { printf_errf("(%#010lx, %#010lx): " "Failed to found new above window.", w->id, new_above); return; From e9ea554fa6f246cc17e578bb3af4c1f9847e57ad Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 23 Mar 2013 22:06:41 +0800 Subject: [PATCH 123/178] Improvement: --blur-background-exclude #98 & MESA_swap_control & others - Add --blur-background-exclude. (#98) - Add `opengl-mswc` VSync, which uses MESA_swap_control instead of SGI_swap_control. I don't expect it to perform better than SGI_swap_control, though. - Update CMakeLists.txt . - Add a few targets for D-Bus `win_get`. Misc changes. - Known issue: Apparently I've forgotten implementing --inactive-dim on GLX backend... Silly me. --- common.h | 10 ++++++ compton.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++------- compton.h | 9 ++++++ dbus.c | 5 +++ 4 files changed, 106 insertions(+), 12 deletions(-) diff --git a/common.h b/common.h index 2f8b6e20d..249ef92e8 100644 --- a/common.h +++ b/common.h @@ -260,6 +260,7 @@ typedef enum { VSYNC_OPENGL, VSYNC_OPENGL_OML, VSYNC_OPENGL_SWC, + VSYNC_OPENGL_MSWC, NUM_VSYNC, } vsync_t; @@ -280,6 +281,7 @@ typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc); typedef int (*f_SwapIntervalSGI) (int interval); +typedef int (*f_SwapIntervalMESA) (unsigned int interval); typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list); typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer); @@ -443,6 +445,8 @@ typedef struct { /// Whether to use fixed blur strength instead of adjusting according /// to window opacity. bool blur_background_fixed; + /// Background blur blacklist. A linked list of conditions. + c2_lptr_t *blur_background_blacklist; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; /// Whether to use fixed inactive dim opacity, instead of deciding @@ -624,6 +628,8 @@ typedef struct { f_WaitForMscOML glXWaitForMscOML; /// Pointer to glXSwapIntervalSGI function. f_SwapIntervalSGI glXSwapIntervalProc; + /// Pointer to glXSwapIntervalMESA function. + f_SwapIntervalMESA glXSwapIntervalMESAProc; /// Pointer to glXBindTexImageEXT function. f_BindTexImageEXT glXBindTexImageProc; /// Pointer to glXReleaseTexImageEXT function. @@ -821,6 +827,7 @@ typedef struct _win { const c2_lptr_t *cache_fblst; const c2_lptr_t *cache_fcblst; const c2_lptr_t *cache_ivclst; + const c2_lptr_t *cache_bbblst; // Opacity-related members /// Current window opacity. @@ -880,6 +887,9 @@ typedef struct _win { /// Override value of window color inversion state. Set by D-Bus method /// calls. switch_t invert_color_force; + + /// Whether to blur window background. + bool blur_background; } win; /// Temporary structure used for communication between diff --git a/compton.c b/compton.c index f37746aa1..ccdd23d30 100644 --- a/compton.c +++ b/compton.c @@ -33,11 +33,12 @@ const char * const WINTYPES[NUM_WINTYPES] = { /// Names of VSync modes. const char * const VSYNC_STRS[NUM_VSYNC] = { - "none", // VSYNC_NONE - "drm", // VSYNC_DRM - "opengl", // VSYNC_OPENGL - "opengl-oml", // VSYNC_OPENGL_OML - "opengl-swc", // VSYNC_OPENGL_SWC + "none", // VSYNC_NONE + "drm", // VSYNC_DRM + "opengl", // VSYNC_OPENGL + "opengl-oml", // VSYNC_OPENGL_OML + "opengl-swc", // VSYNC_OPENGL_SWC + "opengl-mswc", // VSYNC_OPENGL_MSWC }; /// Names of backends. @@ -48,10 +49,11 @@ const char * const BACKEND_STRS[NUM_BKEND] = { /// Function pointers to init VSync modes. static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = { - [VSYNC_DRM ] = vsync_drm_init, - [VSYNC_OPENGL ] = vsync_opengl_init, - [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init, - [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init, + [VSYNC_DRM ] = vsync_drm_init, + [VSYNC_OPENGL ] = vsync_opengl_init, + [VSYNC_OPENGL_OML ] = vsync_opengl_oml_init, + [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init, + [VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_init, }; /// Function pointers to wait for VSync. @@ -1560,7 +1562,8 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { free_picture(ps, &pict); // Dimming the window if needed - if (w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) { + if (BKEND_XRENDER == ps->o.backend + && w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) { XRenderComposite(ps->dpy, PictOpOver, ps->black_picture, w->dim_alpha_pict, ps->tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); } @@ -1725,8 +1728,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { if (!is_region_empty(ps, reg_paint)) { set_tgt_clip(ps, reg_paint); // Blur window background - if ((ps->o.blur_background && WMODE_SOLID != w->mode) - || (ps->o.blur_background_frame && w->frame_opacity)) { + if (w->blur_background && (WMODE_SOLID != w->mode + || (ps->o.blur_background_frame && w->frame_opacity))) { win_blur_background(ps, w, ps->tgt_buffer, reg_paint); } @@ -1977,6 +1980,8 @@ map_win(session_t *ps, Window id) { } win_determine_fade(ps, w); + win_determine_blur_background(ps, w); + w->damaged = false; /* if any configure events happened while @@ -2292,6 +2297,23 @@ win_determine_invert_color(session_t *ps, win *w) { add_damage_win(ps, w); } +/** + * Determine if a window should have background blurred. + */ +static void +win_determine_blur_background(session_t *ps, win *w) { + bool blur_background_old = w->blur_background; + + w->blur_background = ps->o.blur_background + && !win_match(ps, w, ps->o.blur_background_blacklist, &w->cache_bbblst); + + // Only consider window damaged if it's previously painted with background + // blurred + if (w->blur_background != blur_background_old && (WMODE_SOLID != w->mode + || (ps->o.blur_background_frame && w->frame_opacity))) + add_damage_win(ps, w); +} + /** * Function to be called on window type changes. */ @@ -2317,6 +2339,8 @@ win_on_factor_change(session_t *ps, win *w) { win_determine_invert_color(ps, w); if (ps->o.focus_blacklist) win_update_focused(ps, w); + if (ps->o.blur_background_blacklist) + win_determine_blur_background(ps, w); } /** @@ -2533,6 +2557,7 @@ add_win(session_t *ps, Window id, Window prev) { .cache_fblst = NULL, .cache_fcblst = NULL, .cache_ivclst = NULL, + .cache_bbblst = NULL, .opacity = 0, .opacity_tgt = 0, @@ -2563,6 +2588,8 @@ add_win(session_t *ps, Window id, Window prev) { .invert_color = false, .invert_color_force = UNSET, + + .blur_background = false, }; // Reject overlay window and already added windows @@ -4097,6 +4124,9 @@ usage(void) { " Does not actually control paint timing, only buffer swap is\n" " affected, so it doesn't have the effect of --sw-opti unlike\n" " other methods. Experimental." WARNING "\n" + " opengl-mswc = Try to VSync with MESA_swap_control OpenGL\n" + " extension. Basically the same as opengl-swc above, except the\n" + " extension we use." WARNING "\n" "--alpha-step val\n" " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" " 0.03.\n" @@ -4549,6 +4579,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { parse_cfg_condlst(ps, &cfg, &ps->o.focus_blacklist, "focus-exclude"); // --invert-color-include parse_cfg_condlst(ps, &cfg, &ps->o.invert_color_list, "invert-color-include"); + // --blur-background-exclude + parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude"); // --blur-background lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background); // --blur-background-frame @@ -4630,6 +4662,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "benchmark", required_argument, NULL, 293 }, { "benchmark-wid", required_argument, NULL, 294 }, { "glx-use-copysubbuffermesa", no_argument, NULL, 295 }, + { "blur-background-exclude", required_argument, NULL, 296 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4912,6 +4945,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --glx-use-copysubbuffermesa ps->o.glx_use_copysubbuffermesa = true; break; + case 296: + // --blur-background-exclude + condlst_add(ps, &ps->o.blur_background_blacklist, optarg); + break; default: usage(); break; @@ -4955,6 +4992,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (ps->o.blur_background_frame) ps->o.blur_background = true; + // Free background blur blacklist if background blur is not actually enabled + if (!ps->o.blur_background) + free_wincondlst(&ps->o.blur_background_blacklist); + // Other variables determined by options // Determine whether we need to track focus changes @@ -5238,6 +5279,34 @@ vsync_opengl_swc_init(session_t *ps) { #endif } +static bool +vsync_opengl_mswc_init(session_t *ps) { +#ifdef CONFIG_VSYNC_OPENGL + if (!ensure_glx_context(ps)) + return false; + + if (BKEND_GLX != ps->o.backend) { + printf_errf("(): I'm afraid glXSwapIntervalMESA wouldn't help if you are " + "not using GLX backend. You could try, nonetheless."); + } + + // Get video sync functions + if (!ps->glXSwapIntervalMESAProc) + ps->glXSwapIntervalMESAProc = (f_SwapIntervalMESA) + glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA"); + if (!ps->glXSwapIntervalMESAProc) { + printf_errf("(): Failed to get MESA_swap_control function."); + return false; + } + ps->glXSwapIntervalMESAProc(1); + + return true; +#else + printf_errf("(): Program not compiled with OpenGL VSync support."); + return false; +#endif +} + #ifdef CONFIG_VSYNC_OPENGL /** * Wait for next VSync, OpenGL method. @@ -5759,6 +5828,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .blur_background = false, .blur_background_frame = false, .blur_background_fixed = false, + .blur_background_blacklist = NULL, .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, diff --git a/compton.h b/compton.h index b590b0ead..77d3b6bc3 100644 --- a/compton.h +++ b/compton.h @@ -667,6 +667,12 @@ win_update_prop_shadow(session_t *ps, win *w); static void win_determine_shadow(session_t *ps, win *w); +static void +win_determine_invert_color(session_t *ps, win *w); + +static void +win_determine_blur_background(session_t *ps, win *w); + static void win_on_wtype_change(session_t *ps, win *w); @@ -988,6 +994,9 @@ vsync_opengl_oml_init(session_t *ps); static bool vsync_opengl_swc_init(session_t *ps); +static bool +vsync_opengl_mswc_init(session_t *ps); + #ifdef CONFIG_VSYNC_OPENGL static int vsync_opengl_wait(session_t *ps); diff --git a/dbus.c b/dbus.c index 25b37fdf3..48f847caf 100644 --- a/dbus.c +++ b/dbus.c @@ -716,6 +716,11 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(right_width, cdbus_reply_uint32); cdbus_m_win_get_do(top_width, cdbus_reply_uint32); cdbus_m_win_get_do(bottom_width, cdbus_reply_uint32); + + cdbus_m_win_get_do(shadow, cdbus_reply_bool); + cdbus_m_win_get_do(fade, cdbus_reply_bool); + cdbus_m_win_get_do(invert_color, cdbus_reply_bool); + cdbus_m_win_get_do(blur_background, cdbus_reply_bool); #undef cdbus_m_win_get_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); From 47f7b4072200463557ff692d41bf621f6954604f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 25 Mar 2013 11:36:39 +0800 Subject: [PATCH 124/178] Bug fix: GLX: Fix --inactive-dim & fix color inversion - GLX backend: Fix broken --inactive-dim. - GLX backend: Fix bugs when inverting colors of windows. Thanks to madsy and neure for help. - GLX backend: Lift `glx_no_stencil` restriction from glx_init_blur(). It still probably won't work, but the user can try. - XRender backend: Use XRenderFillRectangles() instead of XRenderComposite() to do dimming. --- common.h | 7 +-- compton.c | 49 ++++++++++++++------ opengl.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 162 insertions(+), 30 deletions(-) diff --git a/common.h b/common.h index 249ef92e8..e2af50f08 100644 --- a/common.h +++ b/common.h @@ -878,9 +878,6 @@ typedef struct _win { // Dim-related members /// Whether the window is to be dimmed. bool dim; - /// Picture for dimming. Affected by user-specified inactive dim - /// opacity and window opacity. - Picture dim_alpha_pict; /// Whether to invert window color. bool invert_color; @@ -1641,6 +1638,10 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, GLfloat factor_center); +bool +glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor); + bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, diff --git a/compton.c b/compton.c index ccdd23d30..8fae58ce5 100644 --- a/compton.c +++ b/compton.c @@ -1192,16 +1192,6 @@ paint_preprocess(session_t *ps, win *list) { if (w->shadow && !paint_isvalid(ps, &w->shadow_paint)) win_build_shadow(ps, w, 1); - - // Dimming preprocessing - if (w->dim) { - double dim_opacity = ps->o.inactive_dim; - if (!ps->o.inactive_dim_fixed) - dim_opacity *= get_opacity_percent(w); - - w->dim_alpha_pict = get_alpha_pict_d(ps, dim_opacity); - } - } if ((to_paint && WMODE_SOLID == w->mode) @@ -1562,10 +1552,38 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { free_picture(ps, &pict); // Dimming the window if needed - if (BKEND_XRENDER == ps->o.backend - && w->dim && w->dim_alpha_pict != ps->alpha_picts[0]) { - XRenderComposite(ps->dpy, PictOpOver, ps->black_picture, - w->dim_alpha_pict, ps->tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); + if (w->dim) { + double dim_opacity = ps->o.inactive_dim; + if (!ps->o.inactive_dim_fixed) + dim_opacity *= get_opacity_percent(w); + + switch (ps->o.backend) { + case BKEND_XRENDER: + { + unsigned short cval = 0xffff * dim_opacity; + + // Premultiply color + XRenderColor color = { + .red = 0, .green = 0, .blue = 0, .alpha = cval, + }; + + XRectangle rect = { + .x = x, + .y = y, + .width = wid, + .height = hei, + }; + + XRenderFillRectangles(ps->dpy, PictOpOver, ps->tgt_buffer, &color, + &rect, 1); + } + break; +#ifdef CONFIG_VSYNC_OPENGL + case BKEND_GLX: + glx_dim_dst(ps, x, y, wid, hei, ps->glx_z - 0.7, dim_opacity); + break; +#endif + } } } @@ -2584,7 +2602,6 @@ add_win(session_t *ps, Window id, Window prev) { .prop_shadow = -1, .dim = false, - .dim_alpha_pict = None, .invert_color = false, .invert_color_force = UNSET, @@ -4174,6 +4191,8 @@ usage(void) { "--blur-background-fixed\n" " Use fixed blur strength instead of adjusting according to window\n" " opacity.\n" + "--blur-background-exclude condition\n" + " Exclude conditions for background blur.\n" "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" diff --git a/opengl.c b/opengl.c index 3f1f419b7..45f7b5f32 100644 --- a/opengl.c +++ b/opengl.c @@ -194,7 +194,6 @@ glx_init_blur(session_t *ps) { if (ps->o.glx_no_stencil) { printf_errf("(): I'm afraid blur background won't work so well without " "stencil buffer support."); - return false; } // Build shader @@ -738,6 +737,37 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, return true; } +bool +glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, + GLfloat factor) { + // It's possible to dim in glx_render(), but it would be over-complicated + // considering all those mess in color negation and modulation + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(0.0f, 0.0f, 0.0f, factor); + + glBegin(GL_QUADS); + + { + GLint rdx = dx; + GLint rdy = ps->root_height - dy; + GLint rdxe = rdx + width; + GLint rdye = rdy - height; + + glVertex3i(rdx, rdy, z); + glVertex3i(rdxe, rdy, z); + glVertex3i(rdxe, rdye, z); + glVertex3i(rdx, rdye, z); + } + + glEnd(); + + glColor4f(0.0f, 0.0f, 0.0f, 0.0f); + glDisable(GL_BLEND); + + return true; +} + /** * @brief Render a region with texture data. */ @@ -750,16 +780,23 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return false; } + const bool argb = (GLX_TEXTURE_FORMAT_RGBA_EXT == + ps->glx_fbconfigs[ptex->depth]->texture_fmt); + bool dual_texture = false; + + // It's required by legacy versions of OpenGL to enable texture target + // before specifying environment. Thanks to madsy for telling me. + glEnable(ptex->target); + // Enable blending if needed - if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT == - ps->glx_fbconfigs[ptex->depth]->texture_fmt) { + if (opacity < 1.0 || argb) { glEnable(GL_BLEND); // Needed for handling opacity of ARGB texture glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - // This is all weird, but X Render is using premulitplied ARGB format, and + // This is all weird, but X Render is using premultiplied ARGB format, and // we need to use those things to correct it. Thanks to derhass for help. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColor4f(opacity, opacity, opacity, opacity); @@ -772,11 +809,68 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glEnable(GL_COLOR_LOGIC_OP); glLogicOp(GL_COPY_INVERTED); } - // Blending color negation + // ARGB texture color negation + else if (argb) { + dual_texture = true; + + // Use two texture stages because the calculation is too complicated, + // thanks to madsy for providing code + // Texture stage 0 + glActiveTexture(GL_TEXTURE0); + + // Negation for premultiplied color: color = A - C + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + // Pass texture alpha through + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + // Texture stage 1 + glActiveTexture(GL_TEXTURE1); + glEnable(ptex->target); + glBindTexture(ptex->target, ptex->texture); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + } + // RGB blend color negation else { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + // Modulation with constant factor + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); } } @@ -807,8 +901,12 @@ glx_render(session_t *ps, const glx_texture_t *ptex, rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); } - glEnable(ptex->target); glBindTexture(ptex->target, ptex->texture); + if (dual_texture) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(ptex->target, ptex->texture); + glActiveTexture(GL_TEXTURE0); + } glBegin(GL_QUADS); @@ -833,16 +931,23 @@ glx_render(session_t *ps, const glx_texture_t *ptex, printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif - glTexCoord2f(rx, ry); +#define P_TEXCOORD(cx, cy) { \ + if (dual_texture) { \ + glMultiTexCoord2f(GL_TEXTURE0, cx, cy); \ + glMultiTexCoord2f(GL_TEXTURE1, cx, cy); \ + } \ + else glTexCoord2f(cx, cy); \ +} + P_TEXCOORD(rx, ry); glVertex3i(rdx, rdy, z); - glTexCoord2f(rxe, ry); + P_TEXCOORD(rxe, ry); glVertex3i(rdxe, rdy, z); - glTexCoord2f(rxe, rye); + P_TEXCOORD(rxe, rye); glVertex3i(rdxe, rdye, z); - glTexCoord2f(rx, rye); + P_TEXCOORD(rx, rye); glVertex3i(rdx, rdye, z); } @@ -861,6 +966,13 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glDisable(GL_COLOR_LOGIC_OP); glDisable(ptex->target); + if (dual_texture) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(ptex->target, 0); + glDisable(ptex->target); + glActiveTexture(GL_TEXTURE0); + } + return true; } From 3ad3ebae953c22ce4c3145431351a3192b2c70a4 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 30 Mar 2013 11:46:32 +0800 Subject: [PATCH 125/178] Feature #4: Default active window opacity - Add default active window opacity (--active-opacity). (#4) - Add win_focusin and win_focusout D-Bus signals. --- common.h | 8 ++++++++ compton.c | 30 +++++++++++++++++++++++++++++- dbus.c | 18 ++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/common.h b/common.h index e2af50f08..a3435d430 100644 --- a/common.h +++ b/common.h @@ -424,6 +424,8 @@ typedef struct { /// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for /// not enabled, default. opacity_t inactive_opacity; + /// Default opacity for inactive windows. + opacity_t active_opacity; /// Whether inactive_opacity overrides the opacity set by window /// attributes. bool inactive_opacity_override; @@ -1711,6 +1713,12 @@ cdbus_ev_win_mapped(session_t *ps, win *w); void cdbus_ev_win_unmapped(session_t *ps, win *w); + +void +cdbus_ev_win_focusout(session_t *ps, win *w); + +void +cdbus_ev_win_focusin(session_t *ps, win *w); //!@} /** @name DBus hooks diff --git a/compton.c b/compton.c index 8fae58ce5..981ee0c68 100644 --- a/compton.c +++ b/compton.c @@ -2153,6 +2153,10 @@ calc_opacity(session_t *ps, win *w) { && (OPAQUE == opacity || ps->o.inactive_opacity_override)) { opacity = ps->o.inactive_opacity; } + + // Respect active_opacity only when the window is physically focused + if (OPAQUE == opacity && ps->o.active_opacity && w->focused_real) + opacity = ps->o.active_opacity; } w->opacity_tgt = opacity; @@ -3136,6 +3140,16 @@ win_set_focused(session_t *ps, win *w, bool focused) { // Update everything related to conditions win_on_factor_change(ps, w); + +#ifdef CONFIG_DBUS + // Send D-Bus signal + if (ps->o.dbus) { + if (w->focused_real) + cdbus_ev_win_focusin(ps, w); + else + cdbus_ev_win_focusout(ps, w); + } +#endif } } /** @@ -4095,6 +4109,8 @@ usage(void) { " Inactive opacity set by -i overrides value of _NET_WM_OPACITY.\n" "--inactive-dim value\n" " Dim inactive windows. (0.0 - 1.0, defaults to 0)\n" + "--active-opacity opacity\n" + " Default opacity for active windows. (0.0 - 1.0)\n" "--mark-wmwin-focused\n" " Try to detect WM windows and mark them as active.\n" "--shadow-exclude condition\n" @@ -4520,6 +4536,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // -i (inactive_opacity) if (config_lookup_float(&cfg, "inactive-opacity", &dval)) ps->o.inactive_opacity = normalize_d(dval) * OPAQUE; + // --active_opacity + if (config_lookup_float(&cfg, "active-opacity", &dval)) + ps->o.active_opacity = normalize_d(dval) * OPAQUE; // -e (frame_opacity) config_lookup_float(&cfg, "frame-opacity", &ps->o.frame_opacity); // -z (clear_shadow) @@ -4682,6 +4701,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "benchmark-wid", required_argument, NULL, 294 }, { "glx-use-copysubbuffermesa", no_argument, NULL, 295 }, { "blur-background-exclude", required_argument, NULL, 296 }, + { "active-opacity", required_argument, NULL, 297 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4968,6 +4988,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --blur-background-exclude condlst_add(ps, &ps->o.blur_background_blacklist, optarg); break; + case 297: + // --active-opacity + ps->o.active_opacity = (normalize_d(atof(optarg)) * OPAQUE); + break; default: usage(); break; @@ -4993,6 +5017,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (OPAQUE == ps->o.inactive_opacity) { ps->o.inactive_opacity = 0; } + if (OPAQUE == ps->o.active_opacity) { + ps->o.active_opacity = 0; + } if (shadow_enable) wintype_arr_enable(ps->o.wintype_shadow); ps->o.wintype_shadow[WINTYPE_DESKTOP] = false; @@ -5018,7 +5045,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // Other variables determined by options // Determine whether we need to track focus changes - if (ps->o.inactive_opacity || ps->o.inactive_dim) { + if (ps->o.inactive_opacity || ps->o.active_opacity || ps->o.inactive_dim) { ps->o.track_focus = true; } @@ -5840,6 +5867,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .wintype_opacity = { 0.0 }, .inactive_opacity = 0, .inactive_opacity_override = false, + .active_opacity = 0, .frame_opacity = 0.0, .detect_client_opacity = false, .alpha_step = 0.03, diff --git a/dbus.c b/dbus.c index 48f847caf..5a1a3e344 100644 --- a/dbus.c +++ b/dbus.c @@ -1033,6 +1033,12 @@ cdbus_process_introspect(session_t *ps, DBusMessage *msg) { " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" " \n" "\n"; @@ -1069,4 +1075,16 @@ cdbus_ev_win_unmapped(session_t *ps, win *w) { if (ps->dbus_conn) cdbus_signal_wid(ps, "win_unmapped", w->id); } + +void +cdbus_ev_win_focusout(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_focusout", w->id); +} + +void +cdbus_ev_win_focusin(session_t *ps, win *w) { + if (ps->dbus_conn) + cdbus_signal_wid(ps, "win_focusin", w->id); +} //!@} From ab53e73ce77aa44458de55f62abf6273ac44d540 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 3 Apr 2013 10:51:37 +0800 Subject: [PATCH 126/178] Misc: Workaround for some missing definitions - Add workarounds for missing GL_TEXTURE_RECTANGLE and PictOpDifference definitions in broken GL headers / old X Composite headers. --- common.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/common.h b/common.h index a3435d430..2f5b831d1 100644 --- a/common.h +++ b/common.h @@ -76,6 +76,12 @@ #include #include +// Workarounds for missing definitions in very old versions of X headers, +// thanks to consolers for reporting +#ifndef PictOpDifference +#define PictOpDifference 0x39 +#endif + // libconfig #ifdef CONFIG_LIBCONFIG #include @@ -94,6 +100,13 @@ #endif #include + +// Workarounds for missing definitions in some broken GL drivers, thanks to +// douglasp and consolers for reporting +#ifndef GL_TEXTURE_RECTANGLE +#define GL_TEXTURE_RECTANGLE 0x84F5 +#endif + #endif // === Macros === From 53870fb7fe83f5ab54a0e37a553f3dd769e2fbbf Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 5 Apr 2013 21:05:19 +0800 Subject: [PATCH 127/178] Improvement: GLX: Cache region contents & --glx-no-rebind-pixmap - Cache region contents in is_region_empty(), mostly useful only for GLX backend to save one roundtrip to X. - GLX backend: Add --glx-no-rebind-pixmap, which prevents rebinding of GLX texture to pixmap on content change. It doesn't work on some drivers, but it saves some CPU on those where it does. - Wrap XFree() with a new function cxfree() since its man page claims NULL pointers are not acceptable (although in fact it does...). - Use macro to save some code in get_cfg(). Code clean-up. --- c2.c | 2 +- common.h | 23 +++++- compton.c | 213 ++++++++++++++++++++---------------------------------- compton.h | 23 ++++-- opengl.c | 35 +++++---- 5 files changed, 132 insertions(+), 164 deletions(-) diff --git a/c2.c b/c2.c index 4e2b0dfb3..13c194050 100644 --- a/c2.c +++ b/c2.c @@ -1183,7 +1183,7 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, // Free the string after usage, if necessary if (tgt_free) { if (C2_L_TATOM == pleaf->type) - XFree(tgt_free); + cxfree(tgt_free); else free(tgt_free); } diff --git a/common.h b/common.h index 2f5b831d1..eb57250b0 100644 --- a/common.h +++ b/common.h @@ -357,12 +357,15 @@ typedef struct { char *display; /// The backend in use. enum backend backend; - /// Whether to avoid using stencil buffer under GLX backend. Might be unsafe. + /// Whether to avoid using stencil buffer under GLX backend. Might be + /// unsafe. bool glx_no_stencil; /// Whether to copy unmodified regions from front buffer. bool glx_copy_from_front; /// Whether to use glXCopySubBufferMESA() to update screen. bool glx_use_copysubbuffermesa; + /// Whether to avoid rebinding pixmap on window damage. + bool glx_no_rebind_pixmap; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -1424,6 +1427,17 @@ fds_poll(session_t *ps, struct timeval *ptv) { } #undef CPY_FDS +/** + * Wrapper of XFree() for convenience. + * + * Because a NULL pointer cannot be passed to XFree(), its man page says. + */ +static inline void +cxfree(void *data) { + if (data) + XFree(data); +} + /** * Wrapper of XInternAtom() for convenience. */ @@ -1544,7 +1558,7 @@ wid_has_prop(const session_t *ps, Window w, Atom atom) { if (Success == XGetWindowProperty(ps->dpy, w, atom, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &data)) { - XFree(data); + cxfree(data); if (type) return true; } @@ -1598,7 +1612,7 @@ static inline void free_winprop(winprop_t *pprop) { // Empty the whole structure to avoid possible issues if (pprop->data.p8) { - XFree(pprop->data.p8); + cxfree(pprop->data.p8); pprop->data.p8 = NULL; } pprop->nitems = 0; @@ -1647,7 +1661,8 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { } void -glx_set_clip(session_t *ps, XserverRegion reg); +glx_set_clip(session_t *ps, XserverRegion reg, + const XRectangle * const cache_rects, const int cache_nrects); bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, diff --git a/compton.c b/compton.c index 981ee0c68..e86d74951 100644 --- a/compton.c +++ b/compton.c @@ -614,7 +614,7 @@ wid_get_prop_adv(const session_t *ps, Window w, Atom atom, long offset, }; } - XFree(data); + cxfree(data); return (winprop_t) { .data.p8 = NULL, @@ -658,12 +658,12 @@ win_rounded_corners(session_t *ps, win *w) { for (i = 0; i < nrects; ++i) if (rects[i].width >= minwidth && rects[i].height >= minheight) { w->rounded_corners = true; - XFree(rects); + cxfree(rects); return; } w->rounded_corners = false; - XFree(rects); + cxfree(rects); } /** @@ -762,7 +762,7 @@ find_toplevel2(session_t *ps, Window wid) { break; } - if (tchildren) XFree(tchildren); + cxfree(tchildren); wid = parent; } @@ -1026,7 +1026,7 @@ find_client_win(session_t *ps, Window w) { break; } - XFree(children); + cxfree(children); return ret; } @@ -1448,7 +1448,8 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { // Let glx_bind_pixmap() determine pixmap size, because if the user // is resizing windows, the width and height we get may not be up-to-date, // causing the jittering issue M4he reported in #7. - if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, w->pixmap_damaged)) { + if (!paint_bind_tex(ps, &w->paint, 0, 0, 0, + (!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) { printf_errf("(%#010lx): Failed to bind texture. Expect troubles.", w->id); } w->pixmap_damaged = false; @@ -1665,7 +1666,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { reg_paint = region; } - set_tgt_clip(ps, reg_paint); + set_tgt_clip(ps, reg_paint, NULL, 0); paint_root(ps, reg_paint); // Create temporary regions for use during painting @@ -1716,10 +1717,16 @@ paint_all(session_t *ps, XserverRegion region, win *t) { XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, w->border_size); // Detect if the region is empty before painting - if (region == reg_paint || !is_region_empty(ps, reg_paint)) { - set_tgt_clip(ps, reg_paint); + { + int nrects = 0; + XRectangle *rects = NULL; + if (region == reg_paint + || !is_region_empty(ps, reg_paint, &rects, &nrects)) { + set_tgt_clip(ps, reg_paint, rects, nrects); - win_paint_shadow(ps, w, reg_paint); + win_paint_shadow(ps, w, reg_paint); + } + cxfree(rects); } } @@ -1743,16 +1750,21 @@ paint_all(session_t *ps, XserverRegion region, win *t) { reg_paint = region; } - if (!is_region_empty(ps, reg_paint)) { - set_tgt_clip(ps, reg_paint); - // Blur window background - if (w->blur_background && (WMODE_SOLID != w->mode - || (ps->o.blur_background_frame && w->frame_opacity))) { - win_blur_background(ps, w, ps->tgt_buffer, reg_paint); - } + { + int nrects = 0; + XRectangle *rects = NULL; + if (!is_region_empty(ps, reg_paint, &rects, &nrects)) { + set_tgt_clip(ps, reg_paint, rects, nrects); + // Blur window background + if (w->blur_background && (WMODE_SOLID != w->mode + || (ps->o.blur_background_frame && w->frame_opacity))) { + win_blur_background(ps, w, ps->tgt_buffer, reg_paint); + } - // Painting the window - win_paint_win(ps, w, reg_paint); + // Painting the window + win_paint_win(ps, w, reg_paint); + } + cxfree(rects); } } @@ -1762,7 +1774,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { // Do this as early as possible if (!ps->o.dbe) - set_tgt_clip(ps, None); + set_tgt_clip(ps, None, NULL, 0); if (ps->o.vsync) { // Make sure all previous requests are processed to achieve best @@ -2751,7 +2763,7 @@ restack_win(session_t *ps, win *w, Window new_above) { printf("-> "); if (to_free) { - XFree(window_name); + cxfree(window_name); window_name = NULL; } } @@ -3251,11 +3263,11 @@ wid_get_text_prop(session_t *ps, Window wid, Atom prop, *pnstr = 0; if (*pstrlst) XFreeStringList(*pstrlst); - XFree(text_prop.value); + cxfree(text_prop.value); return false; } - XFree(text_prop.value); + cxfree(text_prop.value); return true; } @@ -3281,10 +3293,10 @@ wid_get_name(session_t *ps, Window wid, char **name) { || !nstr || !strlst) { if (strlst) XFreeStringList(strlst); - XFree(text_prop.value); + cxfree(text_prop.value); return false; } - XFree(text_prop.value); + cxfree(text_prop.value); } *name = mstrcpy(strlst[0]); @@ -3766,8 +3778,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { // Print out changed atom char *name = XGetAtomName(ps->dpy, ev->atom); printf_dbg(" { atom = %s }\n", name); - if (name) - XFree(name); + cxfree(name); } #endif @@ -3987,7 +3998,7 @@ ev_handle(session_t *ps, XEvent *ev) { ev_name(ps, ev), ev_serial(ev), wid, window_name); if (to_free) { - XFree(window_name); + cxfree(window_name); window_name = NULL; } } @@ -4228,6 +4239,10 @@ usage(void) { " My tests on nouveau shows a 200% performance boost when only 1/4 of\n" " the screen is updated. May break VSync and is not available on some\n" " drivers. Overrides --glx-copy-from-front.\n" + "--glx-no-rebind-pixmap\n" + " GLX backend: Avoid rebinding pixmap on window damage. Probably\n" + " could improve performance on rapid window content changes, but is\n" + " known to break things on some drivers.\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4702,6 +4717,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-use-copysubbuffermesa", no_argument, NULL, 295 }, { "blur-background-exclude", required_argument, NULL, 296 }, { "active-opacity", required_argument, NULL, 297 }, + { "glx-no-rebind-pixmap", no_argument, NULL, 298 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4759,6 +4775,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { switch (o) { +#define P_CASEBOOL(idx, option) case idx: ps->o.option = true; break // Short options case 'h': usage(); @@ -4790,9 +4807,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'F': fading_enable = true; break; - case 'S': - ps->o.synchronize = true; - break; + P_CASEBOOL('S', synchronize); case 'r': ps->o.shadow_radius = atoi(optarg); break; @@ -4811,17 +4826,13 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'e': ps->o.frame_opacity = atof(optarg); break; - case 'z': - ps->o.clear_shadow = true; - break; + P_CASEBOOL('z', clear_shadow); case 'n': case 'a': case 's': printf_errfq(1, "(): -n, -a, and -s have been removed."); break; - case 'b': - ps->o.fork_after_register = true; - break; + P_CASEBOOL('b', fork_after_register); // Long options case 256: // --config @@ -4838,42 +4849,21 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --shadow-blue ps->o.shadow_blue = atof(optarg); break; - case 260: - // --inactive-opacity-override - ps->o.inactive_opacity_override = true; - break; + P_CASEBOOL(260, inactive_opacity_override); case 261: // --inactive-dim ps->o.inactive_dim = atof(optarg); break; - case 262: - // --mark-wmwin-focused - ps->o.mark_wmwin_focused = true; - break; + P_CASEBOOL(262, mark_wmwin_focused); case 263: // --shadow-exclude condlst_add(ps, &ps->o.shadow_blacklist, optarg); break; - case 264: - // --mark-ovredir-focused - ps->o.mark_ovredir_focused = true; - break; - case 265: - // --no-fading-openclose - ps->o.no_fading_openclose = true; - break; - case 266: - // --shadow-ignore-shaped - ps->o.shadow_ignore_shaped = true; - break; - case 267: - // --detect-rounded-corners - ps->o.detect_rounded_corners = true; - break; - case 268: - // --detect-client-opacity - ps->o.detect_client_opacity = true; - break; + P_CASEBOOL(264, mark_ovredir_focused); + P_CASEBOOL(265, no_fading_openclose); + P_CASEBOOL(266, shadow_ignore_shaped); + P_CASEBOOL(267, detect_rounded_corners); + P_CASEBOOL(268, detect_client_opacity); case 269: // --refresh-rate ps->o.refresh_rate = atoi(optarg); @@ -4887,66 +4877,24 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --alpha-step ps->o.alpha_step = atof(optarg); break; - case 272: - // --dbe - ps->o.dbe = true; - break; - case 273: - // --paint-on-overlay - ps->o.paint_on_overlay = true; - break; - case 274: - // --sw-opti - ps->o.sw_opti = true; - break; - case 275: - // --vsync-aggressive - ps->o.vsync_aggressive = true; - break; - case 276: - // --use-ewmh-active-win - ps->o.use_ewmh_active_win = true; - break; - case 277: - // --respect-prop-shadow - ps->o.respect_prop_shadow = true; - break; - case 278: - // --unredir-if-possible - ps->o.unredir_if_possible = true; - break; + P_CASEBOOL(272, dbe); + P_CASEBOOL(273, paint_on_overlay); + P_CASEBOOL(274, sw_opti); + P_CASEBOOL(275, vsync_aggressive); + P_CASEBOOL(276, use_ewmh_active_win); + P_CASEBOOL(277, respect_prop_shadow); + P_CASEBOOL(278, unredir_if_possible); case 279: // --focus-exclude condlst_add(ps, &ps->o.focus_blacklist, optarg); break; - case 280: - // --inactive-dim-fixed - ps->o.inactive_dim_fixed = true; - break; - case 281: - // --detect-transient - ps->o.detect_transient = true; - break; - case 282: - // --detect-client-leader - ps->o.detect_client_leader = true; - break; - case 283: - // --blur-background - ps->o.blur_background = true; - break; - case 284: - // --blur-background-frame - ps->o.blur_background_frame = true; - break; - case 285: - // --blur-background-fixed - ps->o.blur_background_fixed = true; - break; - case 286: - // --dbus - ps->o.dbus = true; - break; + P_CASEBOOL(280, inactive_dim_fixed); + P_CASEBOOL(281, detect_transient); + P_CASEBOOL(282, detect_client_leader); + P_CASEBOOL(283, blur_background); + P_CASEBOOL(284, blur_background_frame); + P_CASEBOOL(285, blur_background_fixed); + P_CASEBOOL(286, dbus); case 287: // --logpath ps->o.logpath = mstrcpy(optarg); @@ -4964,14 +4912,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (!parse_backend(ps, optarg)) exit(1); break; - case 291: - // --glx-no-stencil - ps->o.glx_no_stencil = true; - break; - case 292: - // --glx-copy-from-front - ps->o.glx_copy_from_front = true; - break; + P_CASEBOOL(291, glx_no_stencil); + P_CASEBOOL(292, glx_copy_from_front); case 293: // --benchmark ps->o.benchmark = atoi(optarg); @@ -4980,10 +4922,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --benchmark-wid ps->o.benchmark_wid = strtol(optarg, NULL, 0); break; - case 295: - // --glx-use-copysubbuffermesa - ps->o.glx_use_copysubbuffermesa = true; - break; + P_CASEBOOL(295, glx_use_copysubbuffermesa); case 296: // --blur-background-exclude condlst_add(ps, &ps->o.blur_background_blacklist, optarg); @@ -4992,9 +4931,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --active-opacity ps->o.active_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; + P_CASEBOOL(298, glx_no_rebind_pixmap); default: usage(); break; +#undef P_CASEBOOL } } @@ -5492,7 +5433,7 @@ init_filters(session_t *ps) { ps->xrfilter_convolution_exists = true; } } - XFree(pf); + cxfree(pf); // Turn features off if any required filter is not present if (!ps->xrfilter_convolution_exists) { @@ -6195,7 +6136,7 @@ session_init(session_t *ps_old, int argc, char **argv) { add_win(ps, children[i], i ? children[i-1] : None); } - XFree(children); + cxfree(children); } @@ -6445,7 +6386,7 @@ session_run(session_t *ps) { if (!ps->redirected) free_region(ps, &ps->all_damage); - if (ps->all_damage && !is_region_empty(ps, ps->all_damage)) { + if (ps->all_damage && !is_region_empty(ps, ps->all_damage, NULL, NULL)) { static int paint = 0; paint_all(ps, ps->all_damage, t); ps->reg_ignore_expire = false; diff --git a/compton.h b/compton.h index 77d3b6bc3..06bede6a2 100644 --- a/compton.h +++ b/compton.h @@ -269,8 +269,7 @@ make_text_prop(session_t *ps, char *str) { printf_errfq(1, "(): Failed to allocate memory."); if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { - if (pprop->value) - XFree(pprop->value); + cxfree(pprop->value); free(pprop); pprop = NULL; } @@ -547,14 +546,15 @@ win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity } static inline void -set_tgt_clip(session_t *ps, XserverRegion reg) { +set_tgt_clip(session_t *ps, XserverRegion reg, + const XRectangle * const cache_rects, const int cache_nrects) { switch (ps->o.backend) { case BKEND_XRENDER: XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg); break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glx_set_clip(ps, reg); + glx_set_clip(ps, reg, cache_rects, cache_nrects); break; #endif } @@ -854,7 +854,7 @@ dump_region(const session_t *ps, XserverRegion region) { printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, rects[i].width, rects[i].height); - XFree(rects); + cxfree(rects); } /** @@ -865,13 +865,22 @@ dump_region(const session_t *ps, XserverRegion region) { * * @param ps current session * @param region region to check for + * @param pcache_rects a place to cache the dumped rectangles + * @param ncache_nrects a place to cache the number of dumped rectangles */ static inline bool -is_region_empty(const session_t *ps, XserverRegion region) { +is_region_empty(const session_t *ps, XserverRegion region, + XRectangle **pcache_rects, int *pcache_nrects) { int nrects = 0; XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); - XFree(rects); + if (pcache_rects) + *pcache_rects = rects; + else + cxfree(rects); + + if (pcache_nrects) + *pcache_nrects = nrects; return !nrects; } diff --git a/opengl.c b/opengl.c index 45f7b5f32..a660ab5c5 100644 --- a/opengl.c +++ b/opengl.c @@ -135,8 +135,7 @@ glx_init(session_t *ps, bool need_render) { success = true; glx_init_end: - if (pvis) - XFree(pvis); + cxfree(pvis); if (!success) glx_destroy(ps); @@ -324,8 +323,7 @@ glx_update_fbconfig(session_t *ps) { } } - if (pfbcfgs) - XFree(pfbcfgs); + cxfree(pfbcfgs); // Sanity checks if (!ps->glx_fbconfigs[ps->depth]) { @@ -564,7 +562,7 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { { XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); - glx_set_clip(ps, reg_copy); + glx_set_clip(ps, reg_copy, NULL, 0); free_region(ps, ®_copy); } @@ -579,14 +577,15 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { } } - glx_set_clip(ps, *preg); + glx_set_clip(ps, *preg, NULL, 0); } /** * Set clipping region on the target window. */ void -glx_set_clip(session_t *ps, XserverRegion reg) { +glx_set_clip(session_t *ps, XserverRegion reg, + const XRectangle * const cache_rects, const int cache_nrects) { // Quit if we aren't using stencils if (ps->o.glx_no_stencil) return; @@ -601,10 +600,16 @@ glx_set_clip(session_t *ps, XserverRegion reg) { if (!reg) return; - int nrects = 0; - XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects); + int nrects = cache_nrects; + XRectangle *rects_free = NULL; + const XRectangle *rects = cache_rects; + if (!rects) { + nrects = 0; + rects = rects_free = XFixesFetchRegion(ps->dpy, reg, &nrects); + } if (!nrects) { - if (rects) XFree(rects); + cxfree(rects_free); + rects_free = NULL; nrects = 1; rects = &rect_blank; } @@ -649,8 +654,7 @@ glx_set_clip(session_t *ps, XserverRegion reg) { glDepthMask(GL_TRUE); } - if (rects && &rect_blank != rects) - XFree(rects); + cxfree(rects_free); } bool @@ -954,7 +958,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glEnd(); if (rects && rects != &rec_all) - XFree(rects); + cxfree(rects); free_region(ps, ®_new); } @@ -989,7 +993,7 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { glXSwapBuffers(ps->dpy, get_tgt_window(ps)); } else { - glx_set_clip(ps, None); + glx_set_clip(ps, None, NULL, 0); for (int i = 0; i < nrects; ++i) { const int x = rects[i].x; const int y = ps->root_height - rects[i].y - rects[i].height; @@ -1003,8 +1007,7 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { } } - if (rects) - XFree(rects); + cxfree(rects); } #ifdef CONFIG_VSYNC_OPENGL_GLSL From b75d417c52d25894e1a2459468ace7de6f39280d Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 6 Apr 2013 20:21:38 +0800 Subject: [PATCH 128/178] Bug fix: GLX: Fix dim and blur with --glx-no-stencil - GLX backend: Fix broken dim and blur with --glx-no-stencil when dealing with shaped windows. - GLX backend: Cache region contents and do a local region intersection instead of using XFixesIntersectRegion(). Drastic reduction in CPU usage for --glx-no-stencil. Now --glx-no-stencil substantially outperforms (~15%) normal mode. - Use macros to reuse paint-in-region code in opengl.c . Add new type "reg_data_t" to store XserverRegion cache. --- common.h | 32 +++++++-- compton.c | 63 +++++++++-------- compton.h | 33 +++++---- opengl.c | 202 ++++++++++++++++++++++++++++-------------------------- 4 files changed, 187 insertions(+), 143 deletions(-) diff --git a/common.h b/common.h index eb57250b0..d5abdfdf9 100644 --- a/common.h +++ b/common.h @@ -341,6 +341,14 @@ typedef struct _latom { struct _latom *next; } latom_t; +/// A representation of raw region data +typedef struct { + XRectangle *rects; + int nrects; +} reg_data_t; + +#define REG_DATA_INIT { NULL, 0 } + struct _timeout_t; struct _win; @@ -1532,6 +1540,20 @@ free_region(session_t *ps, XserverRegion *p) { } } +/** + * Crop a rectangle by another rectangle. + * + * psrc and pdst cannot be the same. + */ +static inline void +rect_crop(XRectangle *pdst, const XRectangle *psrc, const XRectangle *pbound) { + assert(psrc != pdst); + pdst->x = max_i(psrc->x, pbound->x); + pdst->y = max_i(psrc->y, pbound->y); + pdst->width = max_i(0, min_i(psrc->x + psrc->width, pbound->x + pbound->width) - pdst->x); + pdst->height = max_i(0, min_i(psrc->y + psrc->height, pbound->y + pbound->height) - pdst->y); +} + /** * Check if a rectangle includes the whole screen. */ @@ -1661,21 +1683,21 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { } void -glx_set_clip(session_t *ps, XserverRegion reg, - const XRectangle * const cache_rects, const int cache_nrects); +glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg); bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor_center); + GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg); bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor); + GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg); bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, - double opacity, bool neg, XserverRegion reg_tgt); + double opacity, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg); void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); diff --git a/compton.c b/compton.c index e86d74951..ae4130f6a 100644 --- a/compton.c +++ b/compton.c @@ -881,7 +881,7 @@ paint_root(session_t *ps, XserverRegion reg_paint) { get_root_tile(ps); win_render(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint, - ps->root_tile_paint.pict); + NULL, ps->root_tile_paint.pict); } /** @@ -1280,7 +1280,8 @@ paint_preprocess(session_t *ps, win *list) { * Paint the shadow of a window. */ static inline void -win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint) { +win_paint_shadow(session_t *ps, win *w, + XserverRegion reg_paint, const reg_data_t *pcache_reg) { if (!paint_isvalid(ps, &w->shadow_paint)) { printf_errf("(%#010lx): Missing painting data. This is a bad sign.", w->id); return; @@ -1288,7 +1289,7 @@ win_paint_shadow(session_t *ps, win *w, XserverRegion reg_paint) { render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, - w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint); + w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg); } /** @@ -1320,7 +1321,7 @@ win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { */ static inline void win_blur_background(session_t *ps, win *w, Picture tgt_buffer, - XserverRegion reg_paint) { + XserverRegion reg_paint, const reg_data_t *pcache_reg) { const int x = w->a.x; const int y = w->a.y; const int wid = w->widthb; @@ -1385,7 +1386,8 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center); + glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center, + reg_paint, pcache_reg); break; #endif default: @@ -1396,7 +1398,8 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, static void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, - Picture pict, glx_texture_t *ptex, XserverRegion reg_paint) { + Picture pict, glx_texture_t *ptex, + XserverRegion reg_paint, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: { @@ -1411,7 +1414,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: glx_render(ps, ptex, x, y, dx, dy, wid, hei, - ps->glx_z, opacity, neg, reg_paint); + ps->glx_z, opacity, neg, reg_paint, pcache_reg); ps->glx_z += 1; break; #endif @@ -1424,7 +1427,8 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, * Paint a window itself and dim it if asked. */ static inline void -win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { +win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, + const reg_data_t *pcache_reg) { // Fetch Pixmap if (!w->paint.pixmap && ps->has_name_pixmap) { set_ignore_next(ps); @@ -1494,7 +1498,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { double dopacity = get_opacity_percent(w);; if (!w->frame_opacity) { - win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict); + win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pcache_reg, pict); } else { // Painting parameters @@ -1505,7 +1509,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { #define COMP_BDR(cx, cy, cwid, chei) \ win_render(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity, \ - reg_paint, pict) + reg_paint, pcache_reg, pict) // The following complicated logic is required because some broken // window managers (I'm talking about you, Openbox!) that makes @@ -1540,7 +1544,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { pwid = wid - l - pwid; if (pwid > 0) { // body - win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pict); + win_render(ps, w, l, t, pwid, phei, dopacity, reg_paint, pcache_reg, pict); } } } @@ -1581,7 +1585,8 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint) { break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glx_dim_dst(ps, x, y, wid, hei, ps->glx_z - 0.7, dim_opacity); + glx_dim_dst(ps, x, y, wid, hei, ps->glx_z - 0.7, dim_opacity, + reg_paint, pcache_reg); break; #endif } @@ -1666,7 +1671,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { reg_paint = region; } - set_tgt_clip(ps, reg_paint, NULL, 0); + set_tgt_clip(ps, reg_paint, NULL); paint_root(ps, reg_paint); // Create temporary regions for use during painting @@ -1718,15 +1723,14 @@ paint_all(session_t *ps, XserverRegion region, win *t) { // Detect if the region is empty before painting { - int nrects = 0; - XRectangle *rects = NULL; + reg_data_t cache_reg = REG_DATA_INIT; if (region == reg_paint - || !is_region_empty(ps, reg_paint, &rects, &nrects)) { - set_tgt_clip(ps, reg_paint, rects, nrects); + || !is_region_empty(ps, reg_paint, &cache_reg)) { + set_tgt_clip(ps, reg_paint, &cache_reg); - win_paint_shadow(ps, w, reg_paint); + win_paint_shadow(ps, w, reg_paint, &cache_reg); } - cxfree(rects); + free_reg_data(&cache_reg); } } @@ -1751,20 +1755,19 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } { - int nrects = 0; - XRectangle *rects = NULL; - if (!is_region_empty(ps, reg_paint, &rects, &nrects)) { - set_tgt_clip(ps, reg_paint, rects, nrects); + reg_data_t cache_reg = REG_DATA_INIT; + if (!is_region_empty(ps, reg_paint, &cache_reg)) { + set_tgt_clip(ps, reg_paint, &cache_reg); // Blur window background if (w->blur_background && (WMODE_SOLID != w->mode || (ps->o.blur_background_frame && w->frame_opacity))) { - win_blur_background(ps, w, ps->tgt_buffer, reg_paint); + win_blur_background(ps, w, ps->tgt_buffer, reg_paint, &cache_reg); } // Painting the window - win_paint_win(ps, w, reg_paint); + win_paint_win(ps, w, reg_paint, &cache_reg); } - cxfree(rects); + free_reg_data(&cache_reg); } } @@ -1774,7 +1777,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { // Do this as early as possible if (!ps->o.dbe) - set_tgt_clip(ps, None, NULL, 0); + set_tgt_clip(ps, None, NULL); if (ps->o.vsync) { // Make sure all previous requests are processed to achieve best @@ -4227,8 +4230,8 @@ usage(void) { " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" " GLX backend: Avoid using stencil buffer. Might cause issues\n" - " when rendering transparent content. May have a positive or\n" - " negative effect on performance. (My test shows a 10% slowdown.)\n" + " when rendering transparent content. My tests show a 15% performance\n" + " boost.\n" "--glx-copy-from-front\n" " GLX backend: Copy unmodified regions from front buffer instead of\n" " redrawing them all. My tests with nvidia-drivers show a 10% decrease\n" @@ -6386,7 +6389,7 @@ session_run(session_t *ps) { if (!ps->redirected) free_region(ps, &ps->all_damage); - if (ps->all_damage && !is_region_empty(ps, ps->all_damage, NULL, NULL)) { + if (ps->all_damage && !is_region_empty(ps, ps->all_damage, NULL)) { static int paint = 0; paint_all(ps, ps->all_damage, t); ps->reg_ignore_expire = false; diff --git a/compton.h b/compton.h index 06bede6a2..09b1a4bfb 100644 --- a/compton.h +++ b/compton.h @@ -196,6 +196,16 @@ paint_bind_tex(session_t *ps, paint_t *ppaint, return true; } +/** + * Free data in a reg_data_t. + */ +static inline void +free_reg_data(reg_data_t *pregd) { + cxfree(pregd->rects); + pregd->rects = NULL; + pregd->nrects = 0; +} + /** * Free paint_t. */ @@ -532,29 +542,29 @@ paint_preprocess(session_t *ps, win *list); static void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, bool argb, bool neg, - Picture pict, glx_texture_t *ptex, XserverRegion reg_paint); + Picture pict, glx_texture_t *ptex, + XserverRegion reg_paint, const reg_data_t *pcache_reg); static inline void -win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, Picture pict) { +win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, Picture pict) { const int dx = (w ? w->a.x: 0) + x; const int dy = (w ? w->a.y: 0) + y; const bool argb = (w && w->mode == WMODE_ARGB); const bool neg = (w && w->invert_color); render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, - pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint); + pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint, pcache_reg); } static inline void -set_tgt_clip(session_t *ps, XserverRegion reg, - const XRectangle * const cache_rects, const int cache_nrects) { +set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg); break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: - glx_set_clip(ps, reg, cache_rects, cache_nrects); + glx_set_clip(ps, reg, pcache_reg); break; #endif } @@ -870,18 +880,17 @@ dump_region(const session_t *ps, XserverRegion region) { */ static inline bool is_region_empty(const session_t *ps, XserverRegion region, - XRectangle **pcache_rects, int *pcache_nrects) { + reg_data_t *pcache_reg) { int nrects = 0; XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); - if (pcache_rects) - *pcache_rects = rects; + if (pcache_reg) { + pcache_reg->rects = rects; + pcache_reg->nrects = nrects; + } else cxfree(rects); - if (pcache_nrects) - *pcache_nrects = nrects; - return !nrects; } diff --git a/opengl.c b/opengl.c index a660ab5c5..6560afe55 100644 --- a/opengl.c +++ b/opengl.c @@ -190,11 +190,6 @@ glx_on_root_change(session_t *ps) { bool glx_init_blur(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL_GLSL - if (ps->o.glx_no_stencil) { - printf_errf("(): I'm afraid blur background won't work so well without " - "stencil buffer support."); - } - // Build shader static const char *FRAG_SHADER_BLUR = "#version 110\n" @@ -562,7 +557,7 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { { XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); - glx_set_clip(ps, reg_copy, NULL, 0); + glx_set_clip(ps, reg_copy, NULL); free_region(ps, ®_copy); } @@ -577,22 +572,19 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { } } - glx_set_clip(ps, *preg, NULL, 0); + glx_set_clip(ps, *preg, NULL); } /** * Set clipping region on the target window. */ void -glx_set_clip(session_t *ps, XserverRegion reg, - const XRectangle * const cache_rects, const int cache_nrects) { +glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { // Quit if we aren't using stencils if (ps->o.glx_no_stencil) return; - static XRectangle rect_blank = { - .x = 0, .y = 0, .width = 0, .height = 0 - }; + static XRectangle rect_blank = { .x = 0, .y = 0, .width = 0, .height = 0 }; glDisable(GL_STENCIL_TEST); glDisable(GL_SCISSOR_TEST); @@ -600,13 +592,18 @@ glx_set_clip(session_t *ps, XserverRegion reg, if (!reg) return; - int nrects = cache_nrects; + int nrects = 0; XRectangle *rects_free = NULL; - const XRectangle *rects = cache_rects; + const XRectangle *rects = NULL; + if (pcache_reg) { + rects = pcache_reg->rects; + nrects = pcache_reg->nrects; + } if (!rects) { nrects = 0; rects = rects_free = XFixesFetchRegion(ps->dpy, reg, &nrects); } + // Use one empty rectangle if the region is empty if (!nrects) { cxfree(rects_free); rects_free = NULL; @@ -657,9 +654,45 @@ glx_set_clip(session_t *ps, XserverRegion reg, cxfree(rects_free); } +#define P_PAINTREG_START() \ + XserverRegion reg_new = None; \ + XRectangle rec_all = { .x = dx, .y = dy, .width = width, .height = height }; \ + XRectangle *rects = &rec_all; \ + int nrects = 1; \ + \ + if (ps->o.glx_no_stencil && reg_tgt) { \ + if (pcache_reg) { \ + rects = pcache_reg->rects; \ + nrects = pcache_reg->nrects; \ + } \ + else { \ + reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1); \ + XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt); \ + \ + nrects = 0; \ + rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); \ + } \ + } \ + glBegin(GL_QUADS); \ + \ + for (int i = 0; i < nrects; ++i) { \ + XRectangle crect; \ + rect_crop(&crect, &rects[i], &rec_all); \ + \ + if (!crect.width || !crect.height) \ + continue; \ + +#define P_PAINTREG_END() \ + } \ + glEnd(); \ + \ + if (rects && rects != &rec_all && !(pcache_reg && pcache_reg->rects == rects)) \ + cxfree(rects); \ + free_region(ps, ®_new); \ + bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor_center) { + GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { // Read destination pixels into a texture GLuint tex_scr = 0; glGenTextures(1, &tex_scr); @@ -699,37 +732,37 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); #endif - glBegin(GL_QUADS); - { - const GLfloat rx = 0.0; - const GLfloat ry = 1.0; - const GLfloat rxe = 1.0; - const GLfloat rye = 0.0; - const GLint rdx = dx; - const GLint rdy = ps->root_height - dy; - const GLint rdxe = rdx + width; - const GLint rdye = rdy - height; + P_PAINTREG_START(); + { + const GLfloat rx = (double) (crect.x - dx) / width; + const GLfloat ry = 1.0 - (double) (crect.y - dy) / height; + const GLfloat rxe = rx + (double) crect.width / width; + const GLfloat rye = ry - (double) crect.height / height; + const GLfloat rdx = crect.x; + const GLfloat rdy = ps->root_height - crect.y; + const GLfloat rdxe = rdx + crect.width; + const GLfloat rdye = rdy - crect.height; #ifdef DEBUG_GLX - printf_dbgf("(): %f, %f, %f, %f -> %d, %d, %d, %d\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); + printf_dbgf("(): %f, %f, %f, %f -> %d, %d, %d, %d\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif - glTexCoord2f(rx, ry); - glVertex3f(rdx, rdy, z); + glTexCoord2f(rx, ry); + glVertex3f(rdx, rdy, z); - glTexCoord2f(rxe, ry); - glVertex3f(rdxe, rdy, z); + glTexCoord2f(rxe, ry); + glVertex3f(rdxe, rdy, z); - glTexCoord2f(rxe, rye); - glVertex3f(rdxe, rdye, z); + glTexCoord2f(rxe, rye); + glVertex3f(rdxe, rdye, z); - glTexCoord2f(rx, rye); - glVertex3f(rdx, rdye, z); + glTexCoord2f(rx, rye); + glVertex3f(rdx, rdye, z); + } + P_PAINTREG_END(); } - glEnd(); - #ifdef CONFIG_VSYNC_OPENGL_GLSL glUseProgram(0); #endif @@ -743,25 +776,27 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor) { + GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { // It's possible to dim in glx_render(), but it would be over-complicated // considering all those mess in color negation and modulation glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glColor4f(0.0f, 0.0f, 0.0f, factor); - glBegin(GL_QUADS); - { - GLint rdx = dx; - GLint rdy = ps->root_height - dy; - GLint rdxe = rdx + width; - GLint rdye = rdy - height; - - glVertex3i(rdx, rdy, z); - glVertex3i(rdxe, rdy, z); - glVertex3i(rdxe, rdye, z); - glVertex3i(rdx, rdye, z); + P_PAINTREG_START(); + { + GLint rdx = crect.x; + GLint rdy = ps->root_height - crect.y; + GLint rdxe = rdx + crect.width; + GLint rdye = rdy - crect.height; + + glVertex3i(rdx, rdy, z); + glVertex3i(rdxe, rdy, z); + glVertex3i(rdxe, rdye, z); + glVertex3i(rdx, rdye, z); + } + P_PAINTREG_END(); } glEnd(); @@ -778,7 +813,8 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, int width, int height, int z, - double opacity, bool neg, XserverRegion reg_tgt) { + double opacity, bool neg, + XserverRegion reg_tgt, const reg_data_t *pcache_reg) { if (!ptex || !ptex->texture) { printf_errf("(): Missing texture."); return false; @@ -878,51 +914,30 @@ glx_render(session_t *ps, const glx_texture_t *ptex, } } - { - XserverRegion reg_new = None; - - XRectangle rec_all = { - .x = dx, - .y = dy, - .width = width, - .height = height - }; - - XRectangle *rects = &rec_all; - int nrects = 1; - #ifdef DEBUG_GLX - printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); + printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); #endif - // On no-stencil mode, calculate painting region here instead of relying - // on stencil buffer - if (ps->o.glx_no_stencil && reg_tgt) { - reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1); - XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt); - - nrects = 0; - rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects); - } - + // Bind texture + glBindTexture(ptex->target, ptex->texture); + if (dual_texture) { + glActiveTexture(GL_TEXTURE1); glBindTexture(ptex->target, ptex->texture); - if (dual_texture) { - glActiveTexture(GL_TEXTURE1); - glBindTexture(ptex->target, ptex->texture); - glActiveTexture(GL_TEXTURE0); - } - - glBegin(GL_QUADS); + glActiveTexture(GL_TEXTURE0); + } - for (int i = 0; i < nrects; ++i) { - GLfloat rx = (double) (rects[i].x - dx + x) / ptex->width; - GLfloat ry = (double) (rects[i].y - dy + y) / ptex->height; - GLfloat rxe = rx + (double) rects[i].width / ptex->width; - GLfloat rye = ry + (double) rects[i].height / ptex->height; - GLint rdx = rects[i].x; - GLint rdy = ps->root_height - rects[i].y; - GLint rdxe = rdx + rects[i].width; - GLint rdye = rdy - rects[i].height; + // Painting + { + P_PAINTREG_START(); + { + GLfloat rx = (double) (crect.x - dx + x) / ptex->width; + GLfloat ry = (double) (crect.y - dy + y) / ptex->height; + GLfloat rxe = rx + (double) crect.width / ptex->width; + GLfloat rye = ry + (double) crect.height / ptex->height; + GLint rdx = crect.x; + GLint rdy = ps->root_height - crect.y; + GLint rdxe = rdx + crect.width; + GLint rdye = rdy - crect.height; // Invert Y if needed, this may not work as expected, though. I don't // have such a FBConfig to test with. @@ -954,12 +969,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, P_TEXCOORD(rx, rye); glVertex3i(rdx, rdye, z); } - - glEnd(); - - if (rects && rects != &rec_all) - cxfree(rects); - free_region(ps, ®_new); + P_PAINTREG_END(); } // Cleanup @@ -993,7 +1003,7 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { glXSwapBuffers(ps->dpy, get_tgt_window(ps)); } else { - glx_set_clip(ps, None, NULL, 0); + glx_set_clip(ps, None, NULL); for (int i = 0; i < nrects; ++i) { const int x = rects[i].x; const int y = ps->root_height - rects[i].y - rects[i].height; From 39da27613fcfc70764769f5c458183f63a5f3ec5 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 21 Apr 2013 22:30:22 +0800 Subject: [PATCH 129/178] Improvement: --glx-swap-method & --fade-exclude - GLX backend: Add --glx-swap-method, to reduce painting region if the driver uses exchange or copy buffer swaps. Untested. - Add --fade-exclude, to disable fading on specific windows based on some conditions. Untested. - Expose GLX backend options through configuration file. Add fetching of GLX backend options through D-Bus. - Use NULL pointer instead of element count to delimit string arrays in parse_vsync()/parse_backend()/parse_glx_swap_method(). - Add documentation about "wintypes" section in configuration file. --- common.h | 36 ++++++++++++++++++++++++++++++++---- compton.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- dbus.c | 12 ++++++++++++ opengl.c | 14 +++++++++++++- 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/common.h b/common.h index d5abdfdf9..3058b4d58 100644 --- a/common.h +++ b/common.h @@ -284,6 +284,14 @@ enum backend { NUM_BKEND, }; +/// @brief Possible swap methods. +enum glx_swap_method { + SWAPM_UNDEFINED, + SWAPM_EXCHANGE, + SWAPM_COPY, + NUM_SWAPM, +}; + typedef struct _glx_texture glx_texture_t; #ifdef CONFIG_VSYNC_OPENGL @@ -374,6 +382,8 @@ typedef struct { bool glx_use_copysubbuffermesa; /// Whether to avoid rebinding pixmap on window damage. bool glx_no_rebind_pixmap; + /// GLX swap method we assume OpenGL uses. + enum glx_swap_method glx_swap_method; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -563,6 +573,8 @@ typedef struct { struct timeval time_start; /// The region needs to painted on next paint. XserverRegion all_damage; + /// The region damaged on the last paint. + XserverRegion all_damage_last; /// Whether all windows are currently redirected. bool redirected; /// Whether there's a highest full-screen window, and all windows could @@ -942,8 +954,9 @@ typedef enum { } win_evmode_t; extern const char * const WINTYPES[NUM_WINTYPES]; -extern const char * const VSYNC_STRS[NUM_VSYNC]; -extern const char * const BACKEND_STRS[NUM_BKEND]; +extern const char * const VSYNC_STRS[NUM_VSYNC + 1]; +extern const char * const BACKEND_STRS[NUM_BKEND + 1]; +extern const char * const GLX_SWAP_METHODS_STRS[NUM_SWAPM + 1]; extern session_t *ps_g; // == Debugging code == @@ -1313,11 +1326,12 @@ normalize_d(double d) { */ static inline bool parse_vsync(session_t *ps, const char *str) { - for (vsync_t i = 0; i < (sizeof(VSYNC_STRS) / sizeof(VSYNC_STRS[0])); ++i) + for (vsync_t i = 0; VSYNC_STRS[i]; ++i) if (!strcasecmp(str, VSYNC_STRS[i])) { ps->o.vsync = i; return true; } + printf_errf("(\"%s\"): Invalid vsync argument.", str); return false; } @@ -1327,7 +1341,7 @@ parse_vsync(session_t *ps, const char *str) { */ static inline bool parse_backend(session_t *ps, const char *str) { - for (enum backend i = 0; i < (sizeof(BACKEND_STRS) / sizeof(BACKEND_STRS[0])); ++i) + for (enum backend i = 0; BACKEND_STRS[i]; ++i) if (!strcasecmp(str, BACKEND_STRS[i])) { ps->o.backend = i; return true; @@ -1336,6 +1350,20 @@ parse_backend(session_t *ps, const char *str) { return false; } +/** + * Parse a glx_swap_method option argument. + */ +static inline bool +parse_glx_swap_method(session_t *ps, const char *str) { + for (enum glx_swap_method i = 0; GLX_SWAP_METHODS_STRS[i]; ++i) + if (!strcasecmp(str, GLX_SWAP_METHODS_STRS[i])) { + ps->o.glx_swap_method = i; + return true; + } + printf_errf("(\"%s\"): Invalid GLX swap method argument.", str); + return false; +} + timeout_t * timeout_insert(session_t *ps, time_ms_t interval, bool (*callback)(session_t *ps, timeout_t *ptmout), void *data); diff --git a/compton.c b/compton.c index ae4130f6a..eb94d9b5c 100644 --- a/compton.c +++ b/compton.c @@ -32,19 +32,29 @@ const char * const WINTYPES[NUM_WINTYPES] = { }; /// Names of VSync modes. -const char * const VSYNC_STRS[NUM_VSYNC] = { +const char * const VSYNC_STRS[NUM_VSYNC + 1] = { "none", // VSYNC_NONE "drm", // VSYNC_DRM "opengl", // VSYNC_OPENGL "opengl-oml", // VSYNC_OPENGL_OML "opengl-swc", // VSYNC_OPENGL_SWC "opengl-mswc", // VSYNC_OPENGL_MSWC + NULL }; /// Names of backends. -const char * const BACKEND_STRS[NUM_BKEND] = { +const char * const BACKEND_STRS[NUM_BKEND + 1] = { "xrender", // BKEND_XRENDER "glx", // BKEND_GLX + NULL +}; + +/// Names of GLX swap methods. +const char * const GLX_SWAP_METHODS_STRS[NUM_SWAPM + 1] = { + "undefined", // SWAPM_UNDEFINED + "exchange", // SWAPM_EXCHANGE + "copy", // SWAPM_COPY + NULL }; /// Function pointers to init VSync modes. @@ -2205,7 +2215,8 @@ calc_dim(session_t *ps, win *w) { */ static void win_determine_fade(session_t *ps, win *w) { - if (ps->o.no_fading_openclose && w->in_openclose) + if ((ps->o.no_fading_openclose && w->in_openclose) + || win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) w->fade = false; else w->fade = ps->o.wintype_fade[w->window_type]; @@ -4129,6 +4140,8 @@ usage(void) { " Try to detect WM windows and mark them as active.\n" "--shadow-exclude condition\n" " Exclude conditions for shadows.\n" + "--fade-exclude condition\n" + " Exclude conditions for fading.\n" "--mark-ovredir-focused\n" " Mark windows that have no WM frame as active.\n" "--no-fading-openclose\n" @@ -4246,6 +4259,12 @@ usage(void) { " GLX backend: Avoid rebinding pixmap on window damage. Probably\n" " could improve performance on rapid window content changes, but is\n" " known to break things on some drivers.\n" + "--glx-swap-method undefined/exchange/copy\n" + " GLX backend: GLX buffer swap method we assume. Could be\n" + " \"undefined\", \"exchange\", or \"copy\". \"undefined\" is the slowest\n" + " and the safest; \"exchange\" and \"copy\" are faster but may fail on\n" + " some drivers. Useless with --glx-use-copysubbuffermesa. Defaults to\n" + " \"undefined\".\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4631,6 +4650,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { &ps->o.detect_client_leader); // --shadow-exclude parse_cfg_condlst(ps, &cfg, &ps->o.shadow_blacklist, "shadow-exclude"); + // --fade-exclude + parse_cfg_condlst(ps, &cfg, &ps->o.fade_blacklist, "fade-exclude"); // --focus-exclude parse_cfg_condlst(ps, &cfg, &ps->o.focus_blacklist, "focus-exclude"); // --invert-color-include @@ -4645,6 +4666,18 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --blur-background-fixed lcfg_lookup_bool(&cfg, "blur-background-fixed", &ps->o.blur_background_fixed); + // --glx-no-stencil + lcfg_lookup_bool(&cfg, "glx-no-stencil", &ps->o.glx_no_stencil); + // --glx-copy-from-front + lcfg_lookup_bool(&cfg, "glx-copy-from-front", &ps->o.glx_copy_from_front); + // --glx-use-copysubbuffermesa + lcfg_lookup_bool(&cfg, "glx-use-copysubbuffermesa", &ps->o.glx_use_copysubbuffermesa); + // --glx-no-rebind-pixmap + lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &ps->o.glx_no_rebind_pixmap); + // --glx-swap-method + if (config_lookup_string(&cfg, "glx-swap-method", &sval) + && !parse_glx_swap_method(ps, sval)) + exit(1); // Wintype settings { wintype_t i; @@ -4721,6 +4754,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "blur-background-exclude", required_argument, NULL, 296 }, { "active-opacity", required_argument, NULL, 297 }, { "glx-no-rebind-pixmap", no_argument, NULL, 298 }, + { "glx-swap-method", required_argument, NULL, 299 }, + { "fade-exclude", required_argument, NULL, 300 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4935,6 +4970,15 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.active_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; P_CASEBOOL(298, glx_no_rebind_pixmap); + case 299: + // --glx-swap-method + if (!parse_glx_swap_method(ps, optarg)) + exit(1); + break; + case 300: + // --fade-exclude + condlst_add(ps, &ps->o.fade_blacklist, optarg); + break; default: usage(); break; @@ -5842,6 +5886,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .tmout_lst = NULL, .all_damage = None, + .all_damage_last = None, .time_start = { 0, 0 }, .redirected = false, .unredir_possible = false, @@ -6285,6 +6330,7 @@ session_destroy(session_t *ps) { free_root_tile(ps); free_region(ps, &ps->screen_reg); free_region(ps, &ps->all_damage); + free_region(ps, &ps->all_damage_last); free(ps->expose_rects); free(ps->shadow_corner); free(ps->shadow_top); diff --git a/dbus.c b/dbus.c index 5a1a3e344..bef51a73d 100644 --- a/dbus.c +++ b/dbus.c @@ -909,6 +909,18 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(detect_transient, cdbus_reply_bool); cdbus_m_opts_get_do(detect_client_leader, cdbus_reply_bool); +#ifdef CONFIG_VSYNC_OPENGL + cdbus_m_opts_get_do(glx_no_stencil, cdbus_reply_bool); + cdbus_m_opts_get_do(glx_copy_from_front, cdbus_reply_bool); + cdbus_m_opts_get_do(glx_use_copysubbuffermesa, cdbus_reply_bool); + cdbus_m_opts_get_do(glx_no_rebind_pixmap, cdbus_reply_bool); + if (!strcmp("glx_swap_method", target)) { + assert(ps->o.glx_swap_method < sizeof(GLX_SWAP_METHODS_STRS) / sizeof(GLX_SWAP_METHODS_STRS[0])); + cdbus_reply_string(ps, msg, GLX_SWAP_METHODS_STRS[ps->o.glx_swap_method]); + return true; + } +#endif + cdbus_m_opts_get_do(track_focus, cdbus_reply_bool); cdbus_m_opts_get_do(track_wdata, cdbus_reply_bool); cdbus_m_opts_get_do(track_leader, cdbus_reply_bool); diff --git a/opengl.c b/opengl.c index 6560afe55..2ea8a8c15 100644 --- a/opengl.c +++ b/opengl.c @@ -545,10 +545,20 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { ps->glx_z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + // Exchange swap is interested in the raw damaged region only + XserverRegion all_damage_last = ps->all_damage_last; + ps->all_damage_last = None; + if (SWAPM_EXCHANGE == ps->o.glx_swap_method && *preg) + ps->all_damage_last = copy_region(ps, *preg); + // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, // we could redraw the whole screen or copy unmodified pixels from // front buffer with --glx-copy-from-front. - if (ps->o.glx_use_copysubbuffermesa || !*preg) { + if (ps->o.glx_use_copysubbuffermesa || SWAPM_COPY == ps->o.glx_swap_method + || !*preg) { + } + else if (SWAPM_EXCHANGE == ps->o.glx_swap_method && all_damage_last) { + XFixesUnionRegion(ps->dpy, *preg, *preg, all_damage_last); } else if (!ps->o.glx_copy_from_front) { free_region(ps, preg); @@ -572,6 +582,8 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { } } + free_region(ps, &all_damage_last); + glx_set_clip(ps, *preg, NULL); } From 1dd41253b22c78bf3cd4497895c906fe07cb209b Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 25 Apr 2013 09:27:14 +0800 Subject: [PATCH 130/178] Misc: Fix wrong description & DEBUG_GLX_ERR - Fix description of "opengl" VSync. - Add DEBUG_GLX_ERR to check for OpenGL errors. - Update man page. --- compton.c | 2 +- opengl.c | 36 ++++++++++++++++++++++++++++++++++++ opengl.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index eb94d9b5c..00849cb0f 100644 --- a/compton.c +++ b/compton.c @@ -4175,7 +4175,7 @@ usage(void) { #else #define WARNING #endif - " opengl = Try to VSync with SGI_swap_control OpenGL extension. Only\n" + " opengl = Try to VSync with SGI_video_sync OpenGL extension. Only\n" " work on some drivers." WARNING"\n" " opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n" " Only work on some drivers. Experimental." WARNING"\n" diff --git a/opengl.c b/opengl.c index 2ea8a8c15..eb65e4f4c 100644 --- a/opengl.c +++ b/opengl.c @@ -238,6 +238,10 @@ glx_init_blur(session_t *ps) { #undef P_GET_UNIFM_LOC +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif + return true; #else printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend."); @@ -515,6 +519,10 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, glBindTexture(ptex->target, 0); glDisable(ptex->target); +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif + return true; } @@ -535,6 +543,10 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { glXDestroyPixmap(ps->dpy, ptex->glpixmap); ptex->glpixmap = 0; } + +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif } /** @@ -585,6 +597,10 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { free_region(ps, &all_damage_last); glx_set_clip(ps, *preg, NULL); + +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif } /** @@ -664,6 +680,10 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { } cxfree(rects_free); + +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif } #define P_PAINTREG_START() \ @@ -783,6 +803,10 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, glDeleteTextures(1, &tex_scr); glDisable(tex_tgt); +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif + return true; } @@ -816,6 +840,10 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glDisable(GL_BLEND); +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif + return true; } @@ -999,6 +1027,10 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glActiveTexture(GL_TEXTURE0); } +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif + return true; } @@ -1029,6 +1061,10 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { } } +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif + cxfree(rects); } diff --git a/opengl.h b/opengl.h index 7f6dae696..f6ba09b60 100644 --- a/opengl.h +++ b/opengl.h @@ -12,6 +12,54 @@ #include +#ifdef DEBUG_GLX_ERR + +/** + * Get a textual representation of an OpenGL error. + */ +static inline const char * +glx_dump_err_str(GLenum err) { + switch (err) { + CASESTRRET(GL_NO_ERROR); + CASESTRRET(GL_INVALID_ENUM); + CASESTRRET(GL_INVALID_VALUE); + CASESTRRET(GL_INVALID_OPERATION); + CASESTRRET(GL_INVALID_FRAMEBUFFER_OPERATION); + CASESTRRET(GL_OUT_OF_MEMORY); + CASESTRRET(GL_STACK_UNDERFLOW); + CASESTRRET(GL_STACK_OVERFLOW); + } + + return NULL; +} + +/** + * Check for GLX error. + * + * http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/ + */ +static inline void +glx_check_err_(session_t *ps, const char *func, int line) { + if (!ps->glx_context) return; + + GLenum err = GL_NO_ERROR; + + while (GL_NO_ERROR != (err = glGetError())) { + print_timestamp(ps); + printf("%s():%d: GLX error ", func, line); + const char *errtext = glx_dump_err_str(err); + if (errtext) { + printf_dbg("%s\n", errtext); + } + else { + printf_dbg("%d\n", err); + } + } +} + +#define glx_check_err(ps) glx_check_err_(ps, __func__, __LINE__) +#endif + /** * Check if a word is in string. */ From ec2cd6276d6b137b946a48f4dffaaecd793bff0f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 25 Apr 2013 22:23:35 +0800 Subject: [PATCH 131/178] Improvement: --blur-kern - Add blur convolution kernel customization, --blur-kern. The format is a bit tricky so be sure to read the description in `compton -h`. Not much tests received. - GLX backend: Tolerate missing GLSL uniforms for strangely shaped convolution kernel. - Fix a memory leak that blur-background blacklist is not freed. --- common.h | 2 + compton.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++------- opengl.c | 88 +++++++++++++++++-------- 3 files changed, 234 insertions(+), 49 deletions(-) diff --git a/common.h b/common.h index 3058b4d58..6e8c4cf5f 100644 --- a/common.h +++ b/common.h @@ -483,6 +483,8 @@ typedef struct { bool blur_background_fixed; /// Background blur blacklist. A linked list of conditions. c2_lptr_t *blur_background_blacklist; + /// Blur convolution kernel. + XFixed *blur_kern; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; /// Whether to use fixed inactive dim opacity, instead of deciding diff --git a/compton.c b/compton.c index 00849cb0f..61cd5bcc1 100644 --- a/compton.c +++ b/compton.c @@ -9,6 +9,7 @@ */ #include "compton.h" +#include // === Global constants === @@ -1348,21 +1349,6 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, switch (ps->o.backend) { case BKEND_XRENDER: { - const static int convolution_blur_size = 3; - // Convolution filter parameter (box blur) - // gaussian or binomial filters are definitely superior, yet looks - // like they aren't supported as of xorg-server-1.13.0 - XFixed convolution_blur[] = { - // Must convert to XFixed with XDoubleToFixed() - // Matrix size - XDoubleToFixed(convolution_blur_size), - XDoubleToFixed(convolution_blur_size), - // Matrix - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), - }; - // Directly copying from tgt_buffer does not work, so we create a // Picture in the middle. Picture tmp_picture = win_build_picture(ps, w, NULL); @@ -1370,7 +1356,12 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, if (!tmp_picture) return; - convolution_blur[2 + convolution_blur_size + ((convolution_blur_size - 1) / 2)] = XDoubleToFixed(factor_center); + XFixed *convolution_blur = ps->o.blur_kern; + int kwid = XFixedToDouble((ps->o.blur_kern[0])), + khei = XFixedToDouble((ps->o.blur_kern[1])); + + // Modify the factor of the center pixel + convolution_blur[2 + (khei / 2) * kwid + kwid / 2] = XDoubleToFixed(factor_center); // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. @@ -1386,7 +1377,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, // Copy the content to tmp_picture, then copy back. The filter must // be applied on tgt_buffer, to get the nearby pixels outside the // window. - XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, (XFixed *) convolution_blur, sizeof(convolution_blur) / sizeof(XFixed)); + XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, convolution_blur, kwid * khei + 2); XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei); xrfilter_reset(ps, tgt_buffer); XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); @@ -1665,10 +1656,18 @@ paint_all(session_t *ps, XserverRegion region, win *t) { XFixesSetPictureClipRegion(ps->dpy, ps->tgt_picture, 0, 0, region); #ifdef MONITOR_REPAINT - XRenderComposite( - ps->dpy, PictOpSrc, ps->black_picture, None, - ps->tgt_picture, 0, 0, 0, 0, 0, 0, - ps->root_width, ps->root_height); + switch (ps->o.backend) { + case BKEND_XRENDER: + XRenderComposite(ps->dpy, PictOpSrc, ps->black_picture, None, + ps->tgt_picture, 0, 0, 0, 0, 0, 0, + ps->root_width, ps->root_height); + break; + case BKEND_GLX: + glClearColor(0.0f, 0.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + break; + } #endif if (t && t->reg_ignore) { @@ -4234,6 +4233,14 @@ usage(void) { "--blur-background-fixed\n" " Use fixed blur strength instead of adjusting according to window\n" " opacity.\n" + "--blur-kern matrix\n" + " Specify the blur convolution kernel, with the following format:\n" + " WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n" + " The element in the center must not be included, it will be forever\n" + " 1.0 or changing based on opacity, depending on whether you have\n" + " --blur-background-fixed.\n" + " A 7x7 Guassian blur kernel looks like:\n" + " --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n" "--blur-background-exclude condition\n" " Exclude conditions for background blur.\n" "--invert-color-include condition\n" @@ -4476,10 +4483,113 @@ open_config_file(char *cpath, char **ppath) { return NULL; } +/** + * Parse a floating-point number in matrix. + */ +static inline const char * +parse_matrix_readnum(const char *src, double *dest) { + char *pc = NULL; + double val = strtod(src, &pc); + if (!pc || pc == src) { + printf_errf("(\"%s\"): No number found.", src); + return src; + } + + while (*pc && (isspace(*pc) || ',' == *pc)) + ++pc; + + *dest = val; + + return pc; +} + +/** + * Parse a matrix. + */ +static inline XFixed * +parse_matrix(session_t *ps, const char *src) { + int wid = 0, hei = 0; + const char *pc = NULL; + XFixed *matrix = NULL; + + // Get matrix width and height + { + double val = 0.0; + if (src == (pc = parse_matrix_readnum(src, &val))) + goto parse_matrix_err; + src = pc; + wid = val; + if (src == (pc = parse_matrix_readnum(src, &val))) + goto parse_matrix_err; + src = pc; + hei = val; + } + + // Validate matrix width and height + if (wid <= 0 || hei <= 0) { + printf_errf("(): Invalid matrix width/height."); + goto parse_matrix_err; + } + if (!(wid % 2 && hei % 2)) { + printf_errf("(): Width/height not odd."); + goto parse_matrix_err; + } + if (wid > 16 || hei > 16) { + printf_errf("(): Matrix width/height too large."); + goto parse_matrix_err; + } + + // Allocate memory + matrix = calloc(wid * hei + 2, sizeof(XFixed)); + if (!matrix) { + printf_errf("(): Failed to allocate memory for matrix."); + goto parse_matrix_err; + } + + // Read elements + { + int skip = hei / 2 * wid + wid / 2; + bool hasneg = false; + for (int i = 0; i < wid * hei; ++i) { + // Ignore the center element + if (i == skip) { + matrix[2 + i] = XDoubleToFixed(0); + continue; + } + double val = 0; + if (src == (pc = parse_matrix_readnum(src, &val))) + goto parse_matrix_err; + src = pc; + if (val < 0) hasneg = true; + matrix[2 + i] = XDoubleToFixed(val); + } + if (BKEND_XRENDER == ps->o.backend && hasneg) + printf_errf("(): A convolution kernel with negative values " + "may not work properly under X Render backend."); + } + + // Detect trailing characters + for ( ;*pc; ++pc) + if (!isspace(*pc) && ',' != *pc) { + printf_errf("(): Trailing characters in matrix string."); + goto parse_matrix_err; + } + + // Fill in width and height + matrix[0] = XDoubleToFixed(wid); + matrix[1] = XDoubleToFixed(hei); + + return matrix; + +parse_matrix_err: + free(matrix); + return NULL; +} + /** * Parse a condition list in configuration file. */ -static void +static inline void parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, const char *name) { config_setting_t *setting = config_lookup(pcfg, name); @@ -4756,6 +4866,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-no-rebind-pixmap", no_argument, NULL, 298 }, { "glx-swap-method", required_argument, NULL, 299 }, { "fade-exclude", required_argument, NULL, 300 }, + { "blur-kern", required_argument, NULL, 301 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -4979,6 +5090,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --fade-exclude condlst_add(ps, &ps->o.fade_blacklist, optarg); break; + case 301: + // --blur-kern + free(ps->o.blur_kern); + if (!(ps->o.blur_kern = parse_matrix(ps, optarg))) + exit(1); + break; default: usage(); break; @@ -5042,6 +5159,29 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.track_leader = true; } + // Fill default blur kernel + if (ps->o.blur_background && !ps->o.blur_kern) { + const static int convolution_blur_size = 3; + // Convolution filter parameter (box blur) + // gaussian or binomial filters are definitely superior, yet looks + // like they aren't supported as of xorg-server-1.13.0 + const static XFixed convolution_blur[] = { + // Must convert to XFixed with XDoubleToFixed() + // Matrix size + XDoubleToFixed(convolution_blur_size), + XDoubleToFixed(convolution_blur_size), + // Matrix + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), + }; + ps->o.blur_kern = malloc(sizeof(convolution_blur)); + if (!ps->o.blur_kern) { + printf_errf("(): Failed to allocate memory for convolution kernel."); + exit(1); + } + memcpy(ps->o.blur_kern, &convolution_blur, sizeof(convolution_blur)); + } } /** @@ -5864,6 +6004,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .blur_background_frame = false, .blur_background_fixed = false, .blur_background_blacklist = NULL, + .blur_kern = NULL, .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, @@ -5929,6 +6070,12 @@ session_init(session_t *ps_old, int argc, char **argv) { .glXWaitVideoSyncSGI = NULL, .glXGetSyncValuesOML = NULL, .glXWaitForMscOML = NULL, + +#ifdef CONFIG_VSYNC_OPENGL_GLSL + .glx_prog_blur_unifm_offset_x = -1, + .glx_prog_blur_unifm_offset_y = -1, + .glx_prog_blur_unifm_factor_center = -1, +#endif #endif .xfixes_event = 0, @@ -6277,6 +6424,7 @@ session_destroy(session_t *ps) { free_wincondlst(&ps->o.fade_blacklist); free_wincondlst(&ps->o.focus_blacklist); free_wincondlst(&ps->o.invert_color_list); + free_wincondlst(&ps->o.blur_background_blacklist); #endif // Free tracked atom list @@ -6338,6 +6486,7 @@ session_destroy(session_t *ps) { free(ps->o.display); free(ps->o.logpath); free(ps->o.config_file); + free(ps->o.blur_kern); free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); diff --git a/opengl.c b/opengl.c index eb65e4f4c..50f64f529 100644 --- a/opengl.c +++ b/opengl.c @@ -191,28 +191,60 @@ bool glx_init_blur(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL_GLSL // Build shader - static const char *FRAG_SHADER_BLUR = - "#version 110\n" - "uniform float offset_x;\n" - "uniform float offset_y;\n" - "uniform float factor_center;\n" - "uniform sampler2D tex_scr;\n" - "\n" - "void main() {\n" - " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y - offset_y));\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y));\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x - offset_x, gl_TexCoord[0].y + offset_y));\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - offset_y));\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + offset_y));\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y - offset_y));\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y));\n" - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x, gl_TexCoord[0].y + offset_y));\n" - " gl_FragColor = sum / (factor_center + 8.0);\n" - "}\n" - ; - ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR); + { + static const char *FRAG_SHADER_BLUR_PREFIX = + "#version 110\n" + "uniform float offset_x;\n" + "uniform float offset_y;\n" + "uniform float factor_center;\n" + "uniform sampler2D tex_scr;\n" + "\n" + "void main() {\n" + " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n"; + static const char *FRAG_SHADER_BLUR_ADD = + " sum += float(%.7g) * texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x * float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n"; + static const char *FRAG_SHADER_BLUR_SUFFIX = + " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n" + " gl_FragColor = sum / (factor_center + float(%.7g));\n" + "}\n"; + int wid = XFixedToDouble(ps->o.blur_kern[0]), hei = XFixedToDouble(ps->o.blur_kern[1]); + int nele = wid * hei - 1; + int len = strlen(FRAG_SHADER_BLUR_PREFIX) + (strlen(FRAG_SHADER_BLUR_ADD) + 42) * nele + + strlen(FRAG_SHADER_BLUR_SUFFIX) + 12 + 1; + char *shader_str = calloc(len, sizeof(char)); + if (!shader_str) { + printf_errf("(): Failed to allocate %d bytes for shader string.", len); + return false; + } + { + char *pc = shader_str; + strcpy(pc, FRAG_SHADER_BLUR_PREFIX); + pc += strlen(FRAG_SHADER_BLUR_PREFIX); + assert(strlen(shader_str) < len); + + double sum = 0.0; + for (int i = 0; i < hei; ++i) { + for (int j = 0; j < wid; ++j) { + if (hei / 2 == i && wid / 2 == j) + continue; + double val = XFixedToDouble(ps->o.blur_kern[2 + i * wid + j]); + sum += val; + sprintf(pc, FRAG_SHADER_BLUR_ADD, val, j - wid / 2, i - hei / 2); + pc += strlen(pc); + assert(strlen(shader_str) < len); + } + } + + sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, sum); + assert(strlen(shader_str) < len); +#ifdef DEBUG_GLX_GLSL + fputs(shader_str, stdout); + fflush(stdout); +#endif + } + ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); + } + if (!ps->glx_frag_shader_blur) { printf_errf("(): Failed to create fragment shader."); return false; @@ -227,8 +259,7 @@ glx_init_blur(session_t *ps) { #define P_GET_UNIFM_LOC(name, target) { \ ps->target = glGetUniformLocation(ps->glx_prog_blur, name); \ if (ps->target < 0) { \ - printf_errf("(): Failed to get location of uniform '" name "'."); \ - return false; \ + printf_errf("(): Failed to get location of uniform '" name "'. Might be troublesome."); \ } \ } @@ -759,9 +790,12 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); #ifdef CONFIG_VSYNC_OPENGL_GLSL glUseProgram(ps->glx_prog_blur); - glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width); - glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height); - glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); + if (ps->glx_prog_blur_unifm_offset_x >= 0) + glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width); + if (ps->glx_prog_blur_unifm_offset_y >= 0) + glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height); + if (ps->glx_prog_blur_unifm_factor_center >= 0) + glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); #endif { From b5116f646543d1bcc72f3fd1e9c54be10d9ea8a7 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 26 Apr 2013 14:01:20 +0800 Subject: [PATCH 132/178] Improvement: Enhance --glx-swap-method - Enhance --glx-swap-method to support longer buffers ages (3-6), and automatic buffer age detection via GLX_EXT_buffer_age. --- common.h | 73 ++++++++++++++++++++++++++++++++++++++++++++----------- compton.c | 25 ++++++++----------- dbus.c | 6 +---- opengl.c | 72 +++++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 122 insertions(+), 54 deletions(-) diff --git a/common.h b/common.h index 6e8c4cf5f..04eae104b 100644 --- a/common.h +++ b/common.h @@ -64,6 +64,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,10 @@ #define GL_TEXTURE_RECTANGLE 0x84F5 #endif +#ifndef GLX_BACK_BUFFER_AGE_EXT +#define GLX_BACK_BUFFER_AGE_EXT 0x20F4 +#endif + #endif // === Macros === @@ -175,6 +180,9 @@ /// @brief Maximum OpenGL FBConfig depth. #define OPENGL_MAX_DEPTH 32 +/// @brief Maximum OpenGL buffer age. +#define CGLX_MAX_BUFFER_AGE 5 + // Window flags // Window size is changed @@ -285,11 +293,11 @@ enum backend { }; /// @brief Possible swap methods. -enum glx_swap_method { - SWAPM_UNDEFINED, - SWAPM_EXCHANGE, - SWAPM_COPY, - NUM_SWAPM, +enum { + SWAPM_BUFFER_AGE = -1, + SWAPM_UNDEFINED = 0, + SWAPM_COPY = 1, + SWAPM_EXCHANGE = 2, }; typedef struct _glx_texture glx_texture_t; @@ -383,7 +391,7 @@ typedef struct { /// Whether to avoid rebinding pixmap on window damage. bool glx_no_rebind_pixmap; /// GLX swap method we assume OpenGL uses. - enum glx_swap_method glx_swap_method; + int glx_swap_method; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -576,7 +584,7 @@ typedef struct { /// The region needs to painted on next paint. XserverRegion all_damage; /// The region damaged on the last paint. - XserverRegion all_damage_last; + XserverRegion all_damage_last[CGLX_MAX_BUFFER_AGE]; /// Whether all windows are currently redirected. bool redirected; /// Whether there's a highest full-screen window, and all windows could @@ -958,7 +966,6 @@ typedef enum { extern const char * const WINTYPES[NUM_WINTYPES]; extern const char * const VSYNC_STRS[NUM_VSYNC + 1]; extern const char * const BACKEND_STRS[NUM_BKEND + 1]; -extern const char * const GLX_SWAP_METHODS_STRS[NUM_SWAPM + 1]; extern session_t *ps_g; // == Debugging code == @@ -1357,13 +1364,51 @@ parse_backend(session_t *ps, const char *str) { */ static inline bool parse_glx_swap_method(session_t *ps, const char *str) { - for (enum glx_swap_method i = 0; GLX_SWAP_METHODS_STRS[i]; ++i) - if (!strcasecmp(str, GLX_SWAP_METHODS_STRS[i])) { - ps->o.glx_swap_method = i; - return true; + // Parse alias + if (!strcmp("undefined", str)) { + ps->o.glx_swap_method = 0; + return true; + } + + if (!strcmp("copy", str)) { + ps->o.glx_swap_method = 1; + return true; + } + + if (!strcmp("exchange", str)) { + ps->o.glx_swap_method = 2; + return true; + } + + if (!strcmp("buffer-age", str)) { + ps->o.glx_swap_method = -1; + return true; + } + + // Parse number + { + char *pc = NULL; + int age = strtol(str, &pc, 0); + if (!pc || str == pc) { + printf_errf("(\"%s\"): Invalid number.", str); + return false; } - printf_errf("(\"%s\"): Invalid GLX swap method argument.", str); - return false; + + for (; *pc; ++pc) + if (!isspace(*pc)) { + printf_errf("(\"%s\"): Trailing characters.", str); + return false; + } + + if (age > CGLX_MAX_BUFFER_AGE + 1 || age < -1) { + printf_errf("(\"%s\"): Number too large / too small.", str); + return false; + } + + ps->o.glx_swap_method = age; + } + + return true; } timeout_t * diff --git a/compton.c b/compton.c index 61cd5bcc1..05e115c83 100644 --- a/compton.c +++ b/compton.c @@ -50,14 +50,6 @@ const char * const BACKEND_STRS[NUM_BKEND + 1] = { NULL }; -/// Names of GLX swap methods. -const char * const GLX_SWAP_METHODS_STRS[NUM_SWAPM + 1] = { - "undefined", // SWAPM_UNDEFINED - "exchange", // SWAPM_EXCHANGE - "copy", // SWAPM_COPY - NULL -}; - /// Function pointers to init VSync modes. static bool (* const (VSYNC_FUNCS_INIT[NUM_VSYNC]))(session_t *ps) = { [VSYNC_DRM ] = vsync_drm_init, @@ -4266,12 +4258,14 @@ usage(void) { " GLX backend: Avoid rebinding pixmap on window damage. Probably\n" " could improve performance on rapid window content changes, but is\n" " known to break things on some drivers.\n" - "--glx-swap-method undefined/exchange/copy\n" + "--glx-swap-method undefined/copy/exchange/3/4/5/6/buffer-age\n" " GLX backend: GLX buffer swap method we assume. Could be\n" - " \"undefined\", \"exchange\", or \"copy\". \"undefined\" is the slowest\n" - " and the safest; \"exchange\" and \"copy\" are faster but may fail on\n" - " some drivers. Useless with --glx-use-copysubbuffermesa. Defaults to\n" - " \"undefined\".\n" + " undefined (0), copy (1), exchange (2), 3-6, or buffer-age (-1).\n" + " \"undefined\" is the slowest and the safest, and the default value.\n" + " 1 is fastest, but may fail on some drivers, 2-6 are gradually slower\n" + " but safer (6 is still faster than 0). -1 means auto-detect using\n" + " GLX_EXT_buffer_age, supported by some drivers. Useless with\n" + " --glx-use-copysubbuffermesa.\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -6027,7 +6021,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .tmout_lst = NULL, .all_damage = None, - .all_damage_last = None, + .all_damage_last = { None }, .time_start = { 0, 0 }, .redirected = false, .unredir_possible = false, @@ -6478,7 +6472,8 @@ session_destroy(session_t *ps) { free_root_tile(ps); free_region(ps, &ps->screen_reg); free_region(ps, &ps->all_damage); - free_region(ps, &ps->all_damage_last); + for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) + free_region(ps, &ps->all_damage_last[i]); free(ps->expose_rects); free(ps->shadow_corner); free(ps->shadow_top); diff --git a/dbus.c b/dbus.c index bef51a73d..a80c012dc 100644 --- a/dbus.c +++ b/dbus.c @@ -914,11 +914,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(glx_copy_from_front, cdbus_reply_bool); cdbus_m_opts_get_do(glx_use_copysubbuffermesa, cdbus_reply_bool); cdbus_m_opts_get_do(glx_no_rebind_pixmap, cdbus_reply_bool); - if (!strcmp("glx_swap_method", target)) { - assert(ps->o.glx_swap_method < sizeof(GLX_SWAP_METHODS_STRS) / sizeof(GLX_SWAP_METHODS_STRS[0])); - cdbus_reply_string(ps, msg, GLX_SWAP_METHODS_STRS[ps->o.glx_swap_method]); - return true; - } + cdbus_m_opts_get_do(glx_swap_method, cdbus_reply_int32); #endif cdbus_m_opts_get_do(track_focus, cdbus_reply_bool); diff --git a/opengl.c b/opengl.c index 50f64f529..8fb4e153e 100644 --- a/opengl.c +++ b/opengl.c @@ -54,18 +54,20 @@ glx_init(session_t *ps, bool need_render) { if (need_render && !glx_hasglxext(ps, "GLX_EXT_texture_from_pixmap")) goto glx_init_end; - // Get GLX context - ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); - if (!ps->glx_context) { - printf_errf("(): Failed to get GLX context."); - goto glx_init_end; - } + // Get GLX context + ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); - // Attach GLX context - if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { - printf_errf("(): Failed to attach GLX context."); - goto glx_init_end; + if (!ps->glx_context) { + printf_errf("(): Failed to get GLX context."); + goto glx_init_end; + } + + // Attach GLX context + if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) { + printf_errf("(): Failed to attach GLX context."); + goto glx_init_end; + } } // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles @@ -111,6 +113,7 @@ glx_init(session_t *ps, bool need_render) { if (need_render && !glx_update_fbconfig(ps)) goto glx_init_end; + // Render preparations if (need_render) { glx_on_root_change(ps); @@ -588,20 +591,44 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { ps->glx_z = 0.0; // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Exchange swap is interested in the raw damaged region only - XserverRegion all_damage_last = ps->all_damage_last; - ps->all_damage_last = None; - if (SWAPM_EXCHANGE == ps->o.glx_swap_method && *preg) - ps->all_damage_last = copy_region(ps, *preg); + // Get buffer age + int buffer_age = ps->o.glx_swap_method; + bool trace_damage = (ps->o.glx_swap_method < 0 || ps->o.glx_swap_method > 1); + + // Query GLX_EXT_buffer_age for buffer age + if (SWAPM_BUFFER_AGE == buffer_age) { + unsigned val = 0; + glXQueryDrawable(ps->dpy, get_tgt_window(ps), + GLX_BACK_BUFFER_AGE_EXT, &val); + buffer_age = val; + } + + // Buffer age too high + if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) + buffer_age = 0; + + // Make sure buffer age >= 0 + buffer_age = max_i(buffer_age, 0); + + // Trace raw damage regions + XserverRegion newdamage = None; + if (trace_damage && *preg) + newdamage = copy_region(ps, *preg); // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, // we could redraw the whole screen or copy unmodified pixels from // front buffer with --glx-copy-from-front. - if (ps->o.glx_use_copysubbuffermesa || SWAPM_COPY == ps->o.glx_swap_method - || !*preg) { + if (ps->o.glx_use_copysubbuffermesa || 1 == buffer_age || !*preg) { } - else if (SWAPM_EXCHANGE == ps->o.glx_swap_method && all_damage_last) { - XFixesUnionRegion(ps->dpy, *preg, *preg, all_damage_last); + else if (buffer_age > 1) { + for (int i = 0; i < buffer_age - 1; ++i) { + XserverRegion dmg = ps->all_damage_last[i]; + if (!dmg) { + free_region(ps, preg); + break; + } + XFixesUnionRegion(ps->dpy, *preg, *preg, dmg); + } } else if (!ps->o.glx_copy_from_front) { free_region(ps, preg); @@ -625,7 +652,12 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { } } - free_region(ps, &all_damage_last); + if (trace_damage) { + free_region(ps, &ps->all_damage_last[CGLX_MAX_BUFFER_AGE - 1]); + memmove(ps->all_damage_last + 1, ps->all_damage_last, + (CGLX_MAX_BUFFER_AGE - 1) * sizeof(XserverRegion)); + ps->all_damage_last[0] = newdamage; + } glx_set_clip(ps, *preg, NULL); From 74d91eb38534746efa0b21f2244ba6b41e5440ba Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 26 Apr 2013 16:14:37 +0800 Subject: [PATCH 133/178] Misc: Fix gcc error Fix gcc compilation error about "initializer element is not constant". --- compton.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compton.c b/compton.c index 05e115c83..7935e4c16 100644 --- a/compton.c +++ b/compton.c @@ -5155,15 +5155,13 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // Fill default blur kernel if (ps->o.blur_background && !ps->o.blur_kern) { - const static int convolution_blur_size = 3; // Convolution filter parameter (box blur) // gaussian or binomial filters are definitely superior, yet looks // like they aren't supported as of xorg-server-1.13.0 const static XFixed convolution_blur[] = { // Must convert to XFixed with XDoubleToFixed() // Matrix size - XDoubleToFixed(convolution_blur_size), - XDoubleToFixed(convolution_blur_size), + XDoubleToFixed(3), XDoubleToFixed(3), // Matrix XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), From 5775cefe97e12c7786871b5efc4e3658c97309e5 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 27 Apr 2013 11:43:11 +0800 Subject: [PATCH 134/178] Improvement: --resize-damage - Add --resize-damage to enlarge/shrink repaint region by a specific number of pixels, used for solving the line corruption issue with blur. Thanks to Nuck and jerri in #104 for reporting. - Fix the memory leak of blur shader string. --- common.h | 2 ++ compton.c | 11 ++++++++++ compton.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++----- opengl.c | 1 + 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/common.h b/common.h index 04eae104b..2a98253d5 100644 --- a/common.h +++ b/common.h @@ -403,6 +403,8 @@ typedef struct { /// Whether to paint on X Composite overlay window instead of root /// window. bool paint_on_overlay; + /// Resize damage for a specific number of pixels. + int resize_damage; /// Whether to unredirect all windows if a full-screen opaque window /// is detected. bool unredir_if_possible; diff --git a/compton.c b/compton.c index 7935e4c16..cfd773c69 100644 --- a/compton.c +++ b/compton.c @@ -4235,6 +4235,11 @@ usage(void) { " --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n" "--blur-background-exclude condition\n" " Exclude conditions for background blur.\n" + "--resize-damage integer\n" + " Resize damaged region by a specific number of pixels. A positive\n" + " value enlarges it while a negative one shrinks it. Useful for\n" + " fixing the line corruption issues of blur. May or may not\n" + " work with --glx-no-stencil. Shrinking doesn't function correctly.\n" "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" @@ -4861,6 +4866,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-swap-method", required_argument, NULL, 299 }, { "fade-exclude", required_argument, NULL, 300 }, { "blur-kern", required_argument, NULL, 301 }, + { "resize-damage", required_argument, NULL, 302 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5089,6 +5095,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { free(ps->o.blur_kern); if (!(ps->o.blur_kern = parse_matrix(ps, optarg))) exit(1); + case 302: + // --resize-damage + ps->o.resize_damage = atoi(optarg); break; default: usage(); @@ -5952,6 +5961,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .synchronize = false, .detect_rounded_corners = false, .paint_on_overlay = false, + .resize_damage = 0, .unredir_if_possible = false, .dbus = false, .benchmark = 0, @@ -6577,6 +6587,7 @@ session_run(session_t *ps) { if (!ps->redirected) free_region(ps, &ps->all_damage); + resize_region(ps, ps->all_damage, ps->o.resize_damage); if (ps->all_damage && !is_region_empty(ps, ps->all_damage, NULL)) { static int paint = 0; paint_all(ps, ps->all_damage, t); diff --git a/compton.h b/compton.h index 09b1a4bfb..6b23e1d53 100644 --- a/compton.h +++ b/compton.h @@ -850,19 +850,69 @@ get_screen_region(session_t *ps) { return XFixesCreateRegion(ps->dpy, &r, 1); } +/** + * Resize a region. + */ +static inline void +resize_region(session_t *ps, XserverRegion region, short mod) { + if (!mod || !region) return; + + int nrects = 0, nnewrects = 0; + XRectangle *newrects = NULL; + XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); + if (!rects || !nrects) + goto resize_region_end; + + // Allocate memory for new rectangle list, because I don't know if it's + // safe to write in the memory Xlib allocates + newrects = calloc(nrects, sizeof(XRectangle)); + if (!newrects) { + printf_errf("(): Failed to allocate memory."); + exit(1); + } + + // Loop through all rectangles + for (int i = 0; i < nrects; ++i) { + int x1 = max_i(rects[i].x - mod, 0); + int y1 = max_i(rects[i].y - mod, 0); + int x2 = min_i(rects[i].x + rects[i].width + mod, ps->root_width); + int y2 = min_i(rects[i].y + rects[i].height + mod, ps->root_height); + int wid = x2 - x1; + int hei = y2 - y1; + if (wid <= 0 || hei <= 0) + continue; + newrects[nnewrects].x = x1; + newrects[nnewrects].y = y1; + newrects[nnewrects].width = wid; + newrects[nnewrects].height = hei; + ++nnewrects; + } + + // Set region + XFixesSetRegion(ps->dpy, region, newrects, nnewrects); + +resize_region_end: + cxfree(rects); + free(newrects); +} + /** * Dump a region. */ static inline void dump_region(const session_t *ps, XserverRegion region) { - int nrects = 0, i; - XRectangle *rects = XFixesFetchRegion(ps->dpy, region, &nrects); - if (!rects) - return; + int nrects = 0; + XRectangle *rects = NULL; + if (!rects && region) + rects = XFixesFetchRegion(ps->dpy, region, &nrects); - for (i = 0; i < nrects; ++i) + printf_dbgf("(%#010lx): %d rects\n", region, nrects); + if (!rects) return; + for (int i = 0; i < nrects; ++i) printf("Rect #%d: %8d, %8d, %8d, %8d\n", i, rects[i].x, rects[i].y, rects[i].width, rects[i].height); + putchar('\n'); + fflush(stdout); cxfree(rects); } diff --git a/opengl.c b/opengl.c index 8fb4e153e..4d6d65947 100644 --- a/opengl.c +++ b/opengl.c @@ -246,6 +246,7 @@ glx_init_blur(session_t *ps) { #endif } ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); + free(shader_str); } if (!ps->glx_frag_shader_blur) { From 19471a428959f681f1f4c14a46e6dc01dd212ad4 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 27 Apr 2013 17:34:42 +0800 Subject: [PATCH 135/178] Bug fix: Fix --resize-damage - Fix --resize-damage. I forgot to shrink the painting region back when actually copying to destination. - Include extra pixels around the blur texture to avoid some possible small issues, if --resize-damage is positive. - Known issue: Line artifacts may still appear with --dbe (X Render backend) or --glx-swap-method (GLX backend). I doubt if there's way to fix this without very inefficient mechanisms. --- common.h | 3 +++ compton.c | 18 +++++++++++++----- compton.h | 2 +- opengl.c | 30 +++++++++++++++++++++--------- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/common.h b/common.h index 2a98253d5..9a693c65f 100644 --- a/common.h +++ b/common.h @@ -1599,6 +1599,9 @@ find_focused(session_t *ps) { */ static inline XserverRegion copy_region(const session_t *ps, XserverRegion oldregion) { + if (!oldregion) + return None; + XserverRegion region = XFixesCreateRegion(ps->dpy, NULL, 0); XFixesCopyRegion(ps->dpy, region, oldregion); diff --git a/compton.c b/compton.c index cfd773c69..d5989d668 100644 --- a/compton.c +++ b/compton.c @@ -1597,7 +1597,10 @@ rebuild_screen_reg(session_t *ps) { } static void -paint_all(session_t *ps, XserverRegion region, win *t) { +paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t) { + if (!region_real) + region_real = region; + #ifdef DEBUG_REPAINT static struct timespec last_paint = { 0 }; #endif @@ -1645,7 +1648,8 @@ paint_all(session_t *ps, XserverRegion region, win *t) { } #endif - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_picture, 0, 0, region); + if (BKEND_XRENDER == ps->o.backend) + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_picture, 0, 0, region_real); #ifdef MONITOR_REPAINT switch (ps->o.backend) { @@ -1820,7 +1824,7 @@ paint_all(session_t *ps, XserverRegion region, win *t) { #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: if (ps->o.glx_use_copysubbuffermesa) - glx_swap_copysubbuffermesa(ps, region); + glx_swap_copysubbuffermesa(ps, region_real); else glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; @@ -6551,7 +6555,7 @@ session_run(session_t *ps) { t = paint_preprocess(ps, ps->list); if (ps->redirected) - paint_all(ps, None, t); + paint_all(ps, None, None, t); // Initialize idling ps->idling = false; @@ -6587,10 +6591,13 @@ session_run(session_t *ps) { if (!ps->redirected) free_region(ps, &ps->all_damage); + XserverRegion all_damage_orig = None; + if (ps->o.resize_damage > 0) + all_damage_orig = copy_region(ps, ps->all_damage); resize_region(ps, ps->all_damage, ps->o.resize_damage); if (ps->all_damage && !is_region_empty(ps, ps->all_damage, NULL)) { static int paint = 0; - paint_all(ps, ps->all_damage, t); + paint_all(ps, ps->all_damage, all_damage_orig, t); ps->reg_ignore_expire = false; paint++; if (ps->o.benchmark && paint >= ps->o.benchmark) @@ -6598,6 +6605,7 @@ session_run(session_t *ps) { XSync(ps->dpy, False); ps->all_damage = None; } + free_region(ps, &all_damage_orig); if (ps->idling) ps->fade_time = 0L; diff --git a/compton.h b/compton.h index 6b23e1d53..b069620cf 100644 --- a/compton.h +++ b/compton.h @@ -571,7 +571,7 @@ set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { } static void -paint_all(session_t *ps, XserverRegion region, win *t); +paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t); static void add_damage(session_t *ps, XserverRegion damage); diff --git a/opengl.c b/opengl.c index 4d6d65947..71017f7bf 100644 --- a/opengl.c +++ b/opengl.c @@ -797,6 +797,18 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, return false; } + int mdx = dx, mdy = dy, mwidth = width, mheight = height; + if (ps->o.resize_damage > 0) { + int inc_x = min_i(ps->o.resize_damage, XFixedToDouble(ps->o.blur_kern[0]) / 2), + inc_y = min_i(ps->o.resize_damage, XFixedToDouble(ps->o.blur_kern[1]) / 2); + mdx = max_i(dx - inc_x, 0); + mdy = max_i(dy - inc_y, 0); + int mdx2 = min_i(dx + width + inc_x, ps->root_width), + mdy2 = min_i(dy + height + inc_y, ps->root_height); + mwidth = mdx2 - mdx; + mheight = mdy2 - mdy; + } + GLenum tex_tgt = GL_TEXTURE_RECTANGLE; if (ps->glx_has_texture_non_power_of_two) tex_tgt = GL_TEXTURE_2D; @@ -807,11 +819,11 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); - glCopyTexSubImage2D(tex_tgt, 0, 0, 0, dx, ps->root_height - dy - height, width, height); + glTexImage2D(tex_tgt, 0, GL_RGB, mwidth, mheight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + glCopyTexSubImage2D(tex_tgt, 0, 0, 0, mdx, ps->root_height - mdy - mheight, mwidth, mheight); #ifdef DEBUG_GLX - printf_dbgf("(): %d, %d, %d, %d\n", dx, ps->root_height - dy - height, width, height); + printf_dbgf("(): %d, %d, %d, %d\n", mdx, ps->root_height - mdy - mheight, mwidth, mheight); #endif // Paint it back @@ -824,9 +836,9 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, #ifdef CONFIG_VSYNC_OPENGL_GLSL glUseProgram(ps->glx_prog_blur); if (ps->glx_prog_blur_unifm_offset_x >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / width); + glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / mwidth); if (ps->glx_prog_blur_unifm_offset_y >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / height); + glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / mheight); if (ps->glx_prog_blur_unifm_factor_center >= 0) glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); #endif @@ -834,10 +846,10 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, { P_PAINTREG_START(); { - const GLfloat rx = (double) (crect.x - dx) / width; - const GLfloat ry = 1.0 - (double) (crect.y - dy) / height; - const GLfloat rxe = rx + (double) crect.width / width; - const GLfloat rye = ry - (double) crect.height / height; + const GLfloat rx = (double) (crect.x - mdx) / mwidth; + const GLfloat ry = 1.0 - (double) (crect.y - mdy) / mheight; + const GLfloat rxe = rx + (double) crect.width / mwidth; + const GLfloat rye = ry - (double) crect.height / mheight; const GLfloat rdx = crect.x; const GLfloat rdy = ps->root_height - crect.y; const GLfloat rdxe = rdx + crect.width; From 7f97bf939620124f8f3157e5bf72378bae9cb931 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 27 Apr 2013 20:43:39 +0800 Subject: [PATCH 136/178] Bug fix: Fix a BadRegion error with --glx-use-copysubbuffermesa Just a small and mostly harmless issue. Sorry, didn't test throughly. --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index d5989d668..a97ca17c9 100644 --- a/compton.c +++ b/compton.c @@ -1614,7 +1614,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t #endif if (!region) { - region = get_screen_region(ps); + region_real = region = get_screen_region(ps); } else { // Remove the damaged area out of screen From e94746bbbb104fd11dec9c0a218b0d8f9f5c9252 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 29 Apr 2013 22:42:46 +0800 Subject: [PATCH 137/178] Improvement: Add predefined blur kernels - Add a few predefined blur kernels, requested by jerri in #104. - Add compton-convgen.py to generate blur kernels. --- compton.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index a97ca17c9..82ee6c714 100644 --- a/compton.c +++ b/compton.c @@ -4237,6 +4237,9 @@ usage(void) { " --blur-background-fixed.\n" " A 7x7 Guassian blur kernel looks like:\n" " --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n" + " May also be one the predefined kernels: 3x3box (default), 5x5box,\n" + " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n" + " 11x11gaussian.\n" "--blur-background-exclude condition\n" " Exclude conditions for background blur.\n" "--resize-damage integer\n" @@ -4589,6 +4592,31 @@ parse_matrix_err: return NULL; } +/** + * Parse a convolution kernel. + */ +static inline XFixed * +parse_conv_kern(session_t *ps, const char *src) { + static const struct { + const char *name; + const char *kern_str; + } CONV_KERN_PREDEF[] = { + { "3x3box", "3,3,1,1,1,1,1,1,1,1," }, + { "5x5box", "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1," }, + { "7x7box", "7,7,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1," }, + { "3x3gaussian", "3,3,0.243117,0.493069,0.243117,0.493069,0.493069,0.243117,0.493069,0.243117," }, + { "5x5gaussian", "5,5,0.003493,0.029143,0.059106,0.029143,0.003493,0.029143,0.243117,0.493069,0.243117,0.029143,0.059106,0.493069,0.493069,0.059106,0.029143,0.243117,0.493069,0.243117,0.029143,0.003493,0.029143,0.059106,0.029143,0.003493," }, + { "7x7gaussian", "7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003," }, + { "9x9gaussian", "9,9,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000012,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000012,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000," }, + { "11x11gaussian", "11,11,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000000,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000000,0.000000,0.000012,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000012,0.000000,0.000000,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000000,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000," }, + }; + for (int i = 0; + i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i) + if (!strcmp(CONV_KERN_PREDEF[i].name, src)) + return parse_matrix(ps, CONV_KERN_PREDEF[i].kern_str); + return parse_matrix(ps, src); +} + /** * Parse a condition list in configuration file. */ @@ -5097,7 +5125,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 301: // --blur-kern free(ps->o.blur_kern); - if (!(ps->o.blur_kern = parse_matrix(ps, optarg))) + if (!(ps->o.blur_kern = parse_conv_kern(ps, optarg))) exit(1); case 302: // --resize-damage From c742c97a8c68dc168cca8b135609eb1501ac3225 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 1 May 2013 22:08:43 +0800 Subject: [PATCH 138/178] Misc: Validate wallpaper pixmap & Documentation update - Split Pixmap validation out to validate_pixmap(). Validate wallpaper Pixmap as well. - Update README.md and man page. --- compton.c | 28 ++++------------------------ compton.h | 28 ++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/compton.c b/compton.c index 82ee6c714..1111c190b 100644 --- a/compton.c +++ b/compton.c @@ -669,30 +669,6 @@ win_rounded_corners(session_t *ps, win *w) { cxfree(rects); } -/** - * Validate pixmap of a window, and destroy pixmap and picture if invalid. - */ -static void -win_validate_pixmap(session_t *ps, win *w) { - if (!w->paint.pixmap) - return; - - // Detect whether the pixmap is valid with XGetGeometry. Well, maybe there - // are better ways. - bool invalid = false; - { - Window rroot = None; - int rx = 0, ry = 0; - unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0; - invalid = (!XGetGeometry(ps->dpy, w->paint.pixmap, &rroot, &rx, &ry, - &rwid, &rhei, &rborder, &rdepth) || !rwid || !rhei); - } - - // Destroy pixmap and picture, if invalid - if (invalid) - free_paint(ps, &w->paint); -} - /** * Add a pattern to a condition linked list. */ @@ -840,6 +816,10 @@ get_root_tile(session_t *ps) { free_winprop(&prop); } + // Make sure the pixmap we got is valid + if (pixmap && !validate_pixmap(ps, pixmap)) + pixmap = None; + // Create a pixmap if there isn't any if (!pixmap) { pixmap = XCreatePixmap(ps->dpy, ps->root, 1, 1, ps->depth); diff --git a/compton.h b/compton.h index b069620cf..2e4e3d130 100644 --- a/compton.h +++ b/compton.h @@ -443,8 +443,32 @@ win_is_fullscreen(session_t *ps, const win *w) { static void win_rounded_corners(session_t *ps, win *w); -static void -win_validate_pixmap(session_t *ps, win *w); +/** + * Validate a pixmap. + * + * Detect whether the pixmap is valid with XGetGeometry. Well, maybe there + * are better ways. + */ +static inline bool +validate_pixmap(session_t *ps, Pixmap pxmap) { + if (!pxmap) return false; + + Window rroot = None; + int rx = 0, ry = 0; + unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0; + return XGetGeometry(ps->dpy, pxmap, &rroot, &rx, &ry, + &rwid, &rhei, &rborder, &rdepth) && rwid && rhei; +} + +/** + * Validate pixmap of a window, and destroy pixmap and picture if invalid. + */ +static inline void +win_validate_pixmap(session_t *ps, win *w) { + // Destroy pixmap and picture, if invalid + if (!validate_pixmap(ps, w->paint.pixmap)) + free_paint(ps, &w->paint); +} /** * Wrapper of c2_match(). From 08b6bfe946b635725fc204c7e8b0e8a10c856e2a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 8 May 2013 22:44:36 +0800 Subject: [PATCH 139/178] Imp: Fix GL_TEXTURE_RECTANGLE & Enhance --glx-copy-from-front - Fix GL_TEXTURE_RECTANGLE support. Thanks to amonakov for guides. (#107) - Enhance --glx-copy-from-front to improve performance and make it work with --glx-swap-method, copied from kwin patch. Thanks to bwat47 for info. (#107) - Add texture2Doffset() support in blur GLSL shader. Thanks to amonakov for advice. No visible benefit here, though. (#107) - Only limited tests are done and I'm super sleepy. Bugs expected --- common.h | 16 ++++- compton.c | 12 +++- opengl.c | 202 ++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 160 insertions(+), 70 deletions(-) diff --git a/common.h b/common.h index 9a693c65f..6d7cea107 100644 --- a/common.h +++ b/common.h @@ -392,6 +392,8 @@ typedef struct { bool glx_no_rebind_pixmap; /// GLX swap method we assume OpenGL uses. int glx_swap_method; + /// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring. + bool glx_use_gpushader4; /// Whether to try to detect WM windows and mark them as focused. bool mark_wmwin_focused; /// Whether to mark override-redirect windows as focused. @@ -1234,7 +1236,7 @@ mstrncpy(const char *src, unsigned len) { /** * Allocate the space and join two strings. */ -static inline char * __attribute__((const)) +static inline char * mstrjoin(const char *src1, const char *src2) { char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); @@ -1247,7 +1249,7 @@ mstrjoin(const char *src1, const char *src2) { /** * Allocate the space and join two strings; */ -static inline char * __attribute__((const)) +static inline char * mstrjoin3(const char *src1, const char *src2, const char *src3) { char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + strlen(src3) + 1)); @@ -1259,6 +1261,16 @@ mstrjoin3(const char *src1, const char *src2, const char *src3) { return str; } +/** + * Concatenate a string on heap with another string. + */ +static inline void +mstrextend(char **psrc1, const char *src2) { + *psrc1 = realloc(*psrc1, (*psrc1 ? strlen(*psrc1): 0) + strlen(src2) + 1); + + strcat(*psrc1, src2); +} + /** * Normalize an int value to a specific range. * diff --git a/compton.c b/compton.c index 1111c190b..0fb040bf8 100644 --- a/compton.c +++ b/compton.c @@ -4238,9 +4238,10 @@ usage(void) { " boost.\n" "--glx-copy-from-front\n" " GLX backend: Copy unmodified regions from front buffer instead of\n" - " redrawing them all. My tests with nvidia-drivers show a 10% decrease\n" - " in performance when the whole screen is modified, but a 20% increase\n" - " when only 1/4 is. My tests on nouveau show terrible slowdown.\n" + " redrawing them all. My tests with nvidia-drivers show a 5% decrease\n" + " in performance when the whole screen is modified, but a 30% increase\n" + " when only 1/4 is. My tests on nouveau show terrible slowdown. Could\n" + " work with --glx-swap-method but not --glx-use-copysubbuffermesa.\n" "--glx-use-copysubbuffermesa\n" " GLX backend: Use MESA_copy_sub_buffer to do partial screen update.\n" " My tests on nouveau shows a 200% performance boost when only 1/4 of\n" @@ -4258,6 +4259,9 @@ usage(void) { " but safer (6 is still faster than 0). -1 means auto-detect using\n" " GLX_EXT_buffer_age, supported by some drivers. Useless with\n" " --glx-use-copysubbuffermesa.\n" + "--glx-use-gpushader4\n" + " GLX backend: Use GL_EXT_gpu_shader4 for some optimization on blur\n" + " GLSL code. My tests on GTX 670 show no noticeable effect.\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -4879,6 +4883,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "fade-exclude", required_argument, NULL, 300 }, { "blur-kern", required_argument, NULL, 301 }, { "resize-damage", required_argument, NULL, 302 }, + { "glx-use-gpushader4", no_argument, NULL, 303 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5111,6 +5116,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --resize-damage ps->o.resize_damage = atoi(optarg); break; + P_CASEBOOL(303, glx_use_gpushader4); default: usage(); break; diff --git a/opengl.c b/opengl.c index 71017f7bf..d7b411622 100644 --- a/opengl.c +++ b/opengl.c @@ -197,23 +197,41 @@ glx_init_blur(session_t *ps) { { static const char *FRAG_SHADER_BLUR_PREFIX = "#version 110\n" + "%s" "uniform float offset_x;\n" "uniform float offset_y;\n" "uniform float factor_center;\n" - "uniform sampler2D tex_scr;\n" + "uniform %s tex_scr;\n" "\n" "void main() {\n" " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n"; static const char *FRAG_SHADER_BLUR_ADD = - " sum += float(%.7g) * texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x * float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n"; + " sum += float(%.7g) * %s(tex_scr, vec2(gl_TexCoord[0].x + offset_x * float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n"; + static const char *FRAG_SHADER_BLUR_ADD_GPUSHADER4 = + " sum += float(%.7g) * %sOffset(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y), ivec2(%d, %d));\n"; static const char *FRAG_SHADER_BLUR_SUFFIX = - " sum += texture2D(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n" + " sum += %s(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * factor_center;\n" " gl_FragColor = sum / (factor_center + float(%.7g));\n" "}\n"; - int wid = XFixedToDouble(ps->o.blur_kern[0]), hei = XFixedToDouble(ps->o.blur_kern[1]); + + const bool use_texture_rect = !ps->glx_has_texture_non_power_of_two; + const char *sampler_type = (use_texture_rect ? + "sampler2DRect": "sampler2D"); + const char *texture_func = (use_texture_rect ? + "texture2DRect": "texture2D"); + const char *shader_add = FRAG_SHADER_BLUR_ADD; + char *extension = mstrcpy(""); + if (use_texture_rect) + mstrextend(&extension, "#extension GL_ARB_texture_rectangle : require\n"); + if (ps->o.glx_use_gpushader4) { + mstrextend(&extension, "#extension GL_EXT_gpu_shader4 : require\n"); + shader_add = FRAG_SHADER_BLUR_ADD_GPUSHADER4; + } + + int wid = XFixedToDouble(ps->o.blur_kern[0]), + hei = XFixedToDouble(ps->o.blur_kern[1]); int nele = wid * hei - 1; - int len = strlen(FRAG_SHADER_BLUR_PREFIX) + (strlen(FRAG_SHADER_BLUR_ADD) + 42) * nele - + strlen(FRAG_SHADER_BLUR_SUFFIX) + 12 + 1; + int len = strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele + strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; char *shader_str = calloc(len, sizeof(char)); if (!shader_str) { printf_errf("(): Failed to allocate %d bytes for shader string.", len); @@ -221,8 +239,8 @@ glx_init_blur(session_t *ps) { } { char *pc = shader_str; - strcpy(pc, FRAG_SHADER_BLUR_PREFIX); - pc += strlen(FRAG_SHADER_BLUR_PREFIX); + sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type); + pc += strlen(pc); assert(strlen(shader_str) < len); double sum = 0.0; @@ -232,13 +250,13 @@ glx_init_blur(session_t *ps) { continue; double val = XFixedToDouble(ps->o.blur_kern[2 + i * wid + j]); sum += val; - sprintf(pc, FRAG_SHADER_BLUR_ADD, val, j - wid / 2, i - hei / 2); + sprintf(pc, shader_add, val, texture_func, j - wid / 2, i - hei / 2); pc += strlen(pc); assert(strlen(shader_str) < len); } } - sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, sum); + sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); assert(strlen(shader_str) < len); #ifdef DEBUG_GLX_GLSL fputs(shader_str, stdout); @@ -246,6 +264,7 @@ glx_init_blur(session_t *ps) { #endif } ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); + free(extension); free(shader_str); } @@ -268,8 +287,10 @@ glx_init_blur(session_t *ps) { } P_GET_UNIFM_LOC("factor_center", glx_prog_blur_unifm_factor_center); - P_GET_UNIFM_LOC("offset_x", glx_prog_blur_unifm_offset_x); - P_GET_UNIFM_LOC("offset_y", glx_prog_blur_unifm_offset_y); + if (!ps->o.glx_use_gpushader4) { + P_GET_UNIFM_LOC("offset_x", glx_prog_blur_unifm_offset_x); + P_GET_UNIFM_LOC("offset_y", glx_prog_blur_unifm_offset_y); + } #undef P_GET_UNIFM_LOC @@ -593,24 +614,8 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Get buffer age - int buffer_age = ps->o.glx_swap_method; bool trace_damage = (ps->o.glx_swap_method < 0 || ps->o.glx_swap_method > 1); - // Query GLX_EXT_buffer_age for buffer age - if (SWAPM_BUFFER_AGE == buffer_age) { - unsigned val = 0; - glXQueryDrawable(ps->dpy, get_tgt_window(ps), - GLX_BACK_BUFFER_AGE_EXT, &val); - buffer_age = val; - } - - // Buffer age too high - if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) - buffer_age = 0; - - // Make sure buffer age >= 0 - buffer_age = max_i(buffer_age, 0); - // Trace raw damage regions XserverRegion newdamage = None; if (trace_damage && *preg) @@ -619,37 +624,89 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { // OpenGL doesn't support partial repaint without GLX_MESA_copy_sub_buffer, // we could redraw the whole screen or copy unmodified pixels from // front buffer with --glx-copy-from-front. - if (ps->o.glx_use_copysubbuffermesa || 1 == buffer_age || !*preg) { - } - else if (buffer_age > 1) { - for (int i = 0; i < buffer_age - 1; ++i) { - XserverRegion dmg = ps->all_damage_last[i]; - if (!dmg) { - free_region(ps, preg); - break; - } - XFixesUnionRegion(ps->dpy, *preg, *preg, dmg); - } - } - else if (!ps->o.glx_copy_from_front) { - free_region(ps, preg); + if (ps->o.glx_use_copysubbuffermesa || !*preg) { } else { + int buffer_age = ps->o.glx_swap_method; + + // Getting buffer age { - XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); - XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); - glx_set_clip(ps, reg_copy, NULL); - free_region(ps, ®_copy); + // Query GLX_EXT_buffer_age for buffer age + if (SWAPM_BUFFER_AGE == buffer_age) { + unsigned val = 0; + glXQueryDrawable(ps->dpy, get_tgt_window(ps), + GLX_BACK_BUFFER_AGE_EXT, &val); + buffer_age = val; + } + + // Buffer age too high + if (buffer_age > CGLX_MAX_BUFFER_AGE + 1) + buffer_age = 0; + + // Make sure buffer age >= 0 + buffer_age = max_i(buffer_age, 0); + + // Check if we have we have empty regions + if (buffer_age > 1) { + for (int i = 0; i < buffer_age - 1; ++i) + if (!ps->all_damage_last[i]) { buffer_age = 0; break; } + } } - { - GLfloat raster_pos[4]; - glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos); - glReadBuffer(GL_FRONT); - glRasterPos2f(0.0, 0.0); - glCopyPixels(0, 0, ps->root_width, ps->root_height, GL_COLOR); - glReadBuffer(GL_BACK); - glRasterPos4fv(raster_pos); + // Do nothing for buffer_age 1 (copy) + if (1 != buffer_age) { + // Copy pixels + if (ps->o.glx_copy_from_front) { + // Determine copy area + XserverRegion reg_copy = XFixesCreateRegion(ps->dpy, NULL, 0); + if (!buffer_age) { + XFixesSubtractRegion(ps->dpy, reg_copy, ps->screen_reg, *preg); + } + else { + for (int i = 0; i < buffer_age - 1; ++i) + XFixesUnionRegion(ps->dpy, reg_copy, reg_copy, + ps->all_damage_last[i]); + XFixesSubtractRegion(ps->dpy, reg_copy, reg_copy, *preg); + } + + // Actually copy pixels + { + GLfloat raster_pos[4]; + GLfloat curx = 0.0f, cury = 0.0f; + glGetFloatv(GL_CURRENT_RASTER_POSITION, raster_pos); + glReadBuffer(GL_FRONT); + glRasterPos2f(0.0, 0.0); + { + int nrects = 0; + XRectangle *rects = XFixesFetchRegion(ps->dpy, reg_copy, &nrects); + for (int i = 0; i < nrects; ++i) { + const int x = rects[i].x; + const int y = ps->root_height - rects[i].y - rects[i].height; + // Kwin patch says glRasterPos2f() causes artifacts on bottom + // screen edge with some drivers + glBitmap(0, 0, 0, 0, x - curx, y - cury, NULL); + curx = x; + cury = y; + glCopyPixels(x, y, rects[i].width, rects[i].height, GL_COLOR); + } + cxfree(rects); + } + glReadBuffer(GL_BACK); + glRasterPos4fv(raster_pos); + } + + free_region(ps, ®_copy); + } + + // Determine paint area + if (ps->o.glx_copy_from_front) { } + else if (buffer_age) { + for (int i = 0; i < buffer_age - 1; ++i) + XFixesUnionRegion(ps->dpy, *preg, *preg, ps->all_damage_last[i]); + } + else { + free_region(ps, preg); + } } } @@ -819,13 +876,20 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(tex_tgt, 0, GL_RGB, mwidth, mheight, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(tex_tgt, 0, GL_RGB, mwidth, mheight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glCopyTexSubImage2D(tex_tgt, 0, 0, 0, mdx, ps->root_height - mdy - mheight, mwidth, mheight); #ifdef DEBUG_GLX printf_dbgf("(): %d, %d, %d, %d\n", mdx, ps->root_height - mdy - mheight, mwidth, mheight); #endif + // Texture scaling factor + GLfloat texfac_x = 1.0f, texfac_y = 1.0f; + if (GL_TEXTURE_2D == tex_tgt) { + texfac_x /= mwidth; + texfac_y /= mheight; + } + // Paint it back // Color negation for testing... // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); @@ -836,9 +900,9 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, #ifdef CONFIG_VSYNC_OPENGL_GLSL glUseProgram(ps->glx_prog_blur); if (ps->glx_prog_blur_unifm_offset_x >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_x, 1.0f / mwidth); + glUniform1f(ps->glx_prog_blur_unifm_offset_x, texfac_x); if (ps->glx_prog_blur_unifm_offset_y >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_y, 1.0f / mheight); + glUniform1f(ps->glx_prog_blur_unifm_offset_y, texfac_y); if (ps->glx_prog_blur_unifm_factor_center >= 0) glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); #endif @@ -846,10 +910,10 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, { P_PAINTREG_START(); { - const GLfloat rx = (double) (crect.x - mdx) / mwidth; - const GLfloat ry = 1.0 - (double) (crect.y - mdy) / mheight; - const GLfloat rxe = rx + (double) crect.width / mwidth; - const GLfloat rye = ry - (double) crect.height / mheight; + const GLfloat rx = (crect.x - mdx) * texfac_x; + const GLfloat ry = (mheight - (crect.y - mdy)) * texfac_y; + const GLfloat rxe = rx + crect.width * texfac_x; + const GLfloat rye = ry - crect.height * texfac_y; const GLfloat rdx = crect.x; const GLfloat rdy = ps->root_height - crect.y; const GLfloat rdxe = rdx + crect.width; @@ -1049,10 +1113,18 @@ glx_render(session_t *ps, const glx_texture_t *ptex, { P_PAINTREG_START(); { - GLfloat rx = (double) (crect.x - dx + x) / ptex->width; - GLfloat ry = (double) (crect.y - dy + y) / ptex->height; - GLfloat rxe = rx + (double) crect.width / ptex->width; - GLfloat rye = ry + (double) crect.height / ptex->height; + GLfloat rx = (double) (crect.x - dx + x); + GLfloat ry = (double) (crect.y - dy + y); + GLfloat rxe = rx + (double) crect.width; + GLfloat rye = ry + (double) crect.height; + // Rectangle textures have [0-w] [0-h] while 2D texture has [0-1] [0-1] + // Thanks to amonakov for pointing out! + if (GL_TEXTURE_2D == ptex->target) { + rx = rx / ptex->width; + ry = ry / ptex->height; + rxe = rxe / ptex->width; + rye = rye / ptex->height; + } GLint rdx = crect.x; GLint rdy = ps->root_height - crect.y; GLint rdxe = rdx + crect.width; From a09589b2adb8c0bab7fc6395e1f8084726ddcbc3 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 9 May 2013 09:43:40 +0800 Subject: [PATCH 140/178] Misc: Add DEBUG_GLX_PAINTREG - GLX: Add DEBUG_GLX_PAINTREG, for debugging painting region issues, for p4ddy's problem. --- opengl.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ opengl.h | 8 ++++++ 2 files changed, 95 insertions(+) diff --git a/opengl.c b/opengl.c index d7b411622..a4cb858f3 100644 --- a/opengl.c +++ b/opengl.c @@ -719,6 +719,10 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { glx_set_clip(ps, *preg, NULL); +#ifdef DEBUG_GLX_PAINTREG + glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL); +#endif + #ifdef DEBUG_GLX_ERR glx_check_err(ps); #endif @@ -1003,6 +1007,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return false; } +#ifdef DEBUG_GLX_PAINTREG + glx_render_dots(ps, dx, dy, width, height, z, reg_tgt, pcache_reg); + return true; +#endif + const bool argb = (GLX_TEXTURE_FORMAT_RGBA_EXT == ps->glx_fbconfigs[ptex->depth]->texture_fmt); bool dual_texture = false; @@ -1185,6 +1194,84 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; } +/** + * Render a region with color. + */ +static void +glx_render_color(session_t *ps, int dx, int dy, int width, int height, int z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg) { + static int color = 0; + + color = color % (3 * 3 * 3 - 1) + 1; + glColor4f(1.0 / 3.0 * (color / (3 * 3)), + 1.0 / 3.0 * (color % (3 * 3) / 3), + 1.0 / 3.0 * (color % 3), + 1.0f + ); + z -= 0.2; + + { + P_PAINTREG_START(); + { + GLint rdx = crect.x; + GLint rdy = ps->root_height - crect.y; + GLint rdxe = rdx + crect.width; + GLint rdye = rdy - crect.height; + + glVertex3i(rdx, rdy, z); + glVertex3i(rdxe, rdy, z); + glVertex3i(rdxe, rdye, z); + glVertex3i(rdx, rdye, z); + } + P_PAINTREG_END(); + } + glColor4f(0.0f, 0.0f, 0.0f, 0.0f); + +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif +} + +/** + * Render a region with dots. + */ +static void +glx_render_dots(session_t *ps, int dx, int dy, int width, int height, int z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg) { + glColor4f(0.0f, 0.0f, 0.0f, 1.0f); + z -= 0.1; + + { + P_PAINTREG_START(); + { + static const GLint BLK_WID = 5, BLK_HEI = 5; + + glEnd(); + glPointSize(1.0); + glBegin(GL_POINTS); + + GLint rdx = crect.x; + GLint rdy = ps->root_height - crect.y; + GLint rdxe = rdx + crect.width; + GLint rdye = rdy - crect.height; + rdx = (rdx) / BLK_WID * BLK_WID; + rdy = (rdy) / BLK_HEI * BLK_HEI; + rdxe = (rdxe) / BLK_WID * BLK_WID; + rdye = (rdye) / BLK_HEI * BLK_HEI; + + for (GLint cdx = rdx; cdx < rdxe; cdx += BLK_WID) + for (GLint cdy = rdy; cdy > rdye; cdy -= BLK_HEI) + glVertex3i(cdx + BLK_WID / 2, cdy - BLK_HEI / 2, z); + } + P_PAINTREG_END(); + } + glColor4f(0.0f, 0.0f, 0.0f, 0.0f); + +#ifdef DEBUG_GLX_ERR + glx_check_err(ps); +#endif +} + /** * Swap buffer with glXCopySubBufferMESA(). */ diff --git a/opengl.h b/opengl.h index f6ba09b60..eae8bcf86 100644 --- a/opengl.h +++ b/opengl.h @@ -132,3 +132,11 @@ glx_update_fbconfig(session_t *ps); static int glx_cmp_fbconfig(session_t *ps, const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b); + +static void +glx_render_color(session_t *ps, int dx, int dy, int width, int height, int z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg); + +static void +glx_render_dots(session_t *ps, int dx, int dy, int width, int height, int z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg); From 060b5ab23c3fb18a8695e8f7e5d145f023b7b3b2 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 9 May 2013 21:47:09 +0800 Subject: [PATCH 141/178] Bug fix: Compilation failure with NO_LIBCONFIG / NO_C2 - Fix compilation failure with NO_LIBCONFIG or NO_C2. Thanks to Spaulding for reporting. --- compton.c | 168 +++++++++++++++++++++++++++--------------------------- compton.h | 4 +- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/compton.c b/compton.c index 0fb040bf8..cffee1a30 100644 --- a/compton.c +++ b/compton.c @@ -4389,90 +4389,6 @@ fork_after(session_t *ps) { return success; } -#ifdef CONFIG_LIBCONFIG -/** - * Get a file stream of the configuration file to read. - * - * Follows the XDG specification to search for the configuration file. - */ -static FILE * -open_config_file(char *cpath, char **ppath) { - const static char *config_filename = "/compton.conf"; - const static char *config_filename_legacy = "/.compton.conf"; - const static char *config_home_suffix = "/.config"; - const static char *config_system_dir = "/etc/xdg"; - - char *dir = NULL, *home = NULL; - char *path = cpath; - FILE *f = NULL; - - if (path) { - f = fopen(path, "r"); - if (f && ppath) - *ppath = path; - return f; - } - - // Check user configuration file in $XDG_CONFIG_HOME firstly - if (!((dir = getenv("XDG_CONFIG_HOME")) && strlen(dir))) { - if (!((home = getenv("HOME")) && strlen(home))) - return NULL; - - path = mstrjoin3(home, config_home_suffix, config_filename); - } - else - path = mstrjoin(dir, config_filename); - - f = fopen(path, "r"); - - if (f && ppath) - *ppath = path; - else - free(path); - if (f) - return f; - - // Then check user configuration file in $HOME - if ((home = getenv("HOME")) && strlen(home)) { - path = mstrjoin(home, config_filename_legacy); - f = fopen(path, "r"); - if (f && ppath) - *ppath = path; - else - free(path); - if (f) - return f; - } - - // Check system configuration file in $XDG_CONFIG_DIRS at last - if ((dir = getenv("XDG_CONFIG_DIRS")) && strlen(dir)) { - char *part = strtok(dir, ":"); - while (part) { - path = mstrjoin(part, config_filename); - f = fopen(path, "r"); - if (f && ppath) - *ppath = path; - else - free(path); - if (f) - return f; - part = strtok(NULL, ":"); - } - } - else { - path = mstrjoin(config_system_dir, config_filename); - f = fopen(path, "r"); - if (f && ppath) - *ppath = path; - else - free(path); - if (f) - return f; - } - - return NULL; -} - /** * Parse a floating-point number in matrix. */ @@ -4601,6 +4517,90 @@ parse_conv_kern(session_t *ps, const char *src) { return parse_matrix(ps, src); } +#ifdef CONFIG_LIBCONFIG +/** + * Get a file stream of the configuration file to read. + * + * Follows the XDG specification to search for the configuration file. + */ +static FILE * +open_config_file(char *cpath, char **ppath) { + const static char *config_filename = "/compton.conf"; + const static char *config_filename_legacy = "/.compton.conf"; + const static char *config_home_suffix = "/.config"; + const static char *config_system_dir = "/etc/xdg"; + + char *dir = NULL, *home = NULL; + char *path = cpath; + FILE *f = NULL; + + if (path) { + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + return f; + } + + // Check user configuration file in $XDG_CONFIG_HOME firstly + if (!((dir = getenv("XDG_CONFIG_HOME")) && strlen(dir))) { + if (!((home = getenv("HOME")) && strlen(home))) + return NULL; + + path = mstrjoin3(home, config_home_suffix, config_filename); + } + else + path = mstrjoin(dir, config_filename); + + f = fopen(path, "r"); + + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + + // Then check user configuration file in $HOME + if ((home = getenv("HOME")) && strlen(home)) { + path = mstrjoin(home, config_filename_legacy); + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + } + + // Check system configuration file in $XDG_CONFIG_DIRS at last + if ((dir = getenv("XDG_CONFIG_DIRS")) && strlen(dir)) { + char *part = strtok(dir, ":"); + while (part) { + path = mstrjoin(part, config_filename); + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + part = strtok(NULL, ":"); + } + } + else { + path = mstrjoin(config_system_dir, config_filename); + f = fopen(path, "r"); + if (f && ppath) + *ppath = path; + else + free(path); + if (f) + return f; + } + + return NULL; +} + /** * Parse a condition list in configuration file. */ diff --git a/compton.h b/compton.h index 2e4e3d130..aba07c0d1 100644 --- a/compton.h +++ b/compton.h @@ -146,16 +146,16 @@ free_damage(session_t *ps, Damage *p) { } } -#ifdef CONFIG_C2 /** * Destroy a condition list. */ static inline void free_wincondlst(c2_lptr_t **pcondlst) { +#ifdef CONFIG_C2 while ((*pcondlst = c2_free_lptr(*pcondlst))) continue; -} #endif +} /** * Check whether a paint_t contains enough data. From 2b0dfa9b968ce34f35919f9df216e668fec761b8 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 12 May 2013 18:21:16 +0800 Subject: [PATCH 142/178] Misc: Add DEBUG_GLX_MARK & Misc - GLX backend: Add DEBUG_GLX_MARK, to add GL marks around functions with glStringMarkerGREMEDY(), and mark frame termination with glFrameTerminatorGREMEDY(). - Print output of `compton -h` to stdout. (#110) - GLX backend: Strip out elements with factor 0 in GLSL blur code. Thanks to jrfonseca for guides. (#107) --- common.h | 43 +++++++++++++++++++++++++++++++++++++++++++ compton.c | 18 ++++++++++++------ compton.h | 2 +- dbus.c | 20 ++++++++++++++++++++ opengl.c | 9 +++++++++ 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/common.h b/common.h index 6d7cea107..bf9f8ae01 100644 --- a/common.h +++ b/common.h @@ -317,6 +317,11 @@ typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, in typedef void (*f_CopySubBuffer) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); +#ifdef DEBUG_GLX_MARK +typedef void (*f_StringMarkerGREMEDY) (GLsizei len, const void *string); +typedef void (*f_FrameTerminatorGREMEDY) (void); +#endif + /// @brief Wrapper of a GLX FBConfig. typedef struct { GLXFBConfig cfg; @@ -688,6 +693,12 @@ typedef struct { f_ReleaseTexImageEXT glXReleaseTexImageProc; /// Pointer to glXCopySubBufferMESA function. f_CopySubBuffer glXCopySubBufferProc; +#ifdef DEBUG_GLX_MARK + /// Pointer to StringMarkerGREMEDY function. + f_StringMarkerGREMEDY glStringMarkerGREMEDY; + /// Pointer to FrameTerminatorGREMEDY function. + f_FrameTerminatorGREMEDY glFrameTerminatorGREMEDY; +#endif /// FBConfig-s for GLX pixmap of different depths. glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; #ifdef CONFIG_VSYNC_OPENGL_GLSL @@ -1803,6 +1814,38 @@ glx_create_program(const GLuint * const shaders, int nshaders); #endif #endif +/** + * Add a OpenGL debugging marker. + */ +static inline void +glx_mark_(session_t *ps, const char *func, XID xid, bool start) { +#ifdef DEBUG_GLX_MARK + if (BKEND_GLX == ps->o.backend && ps->glStringMarkerGREMEDY) { + if (!func) func = "(unknown)"; + const char *postfix = (start ? " (start)": " (end)"); + char *str = malloc((strlen(func) + 12 + 2 + + strlen(postfix) + 5) * sizeof(char)); + strcpy(str, func); + sprintf(str + strlen(str), "(%#010lx)%s", xid, postfix); + ps->glStringMarkerGREMEDY(strlen(str), str); + free(str); + } +#endif +} + +#define glx_mark(ps, xid, start) glx_mark_(ps, __func__, xid, start) + +/** + * Add a OpenGL debugging marker. + */ +static inline void +glx_mark_frame(session_t *ps) { +#ifdef DEBUG_GLX_MARK + if (BKEND_GLX == ps->o.backend && ps->glFrameTerminatorGREMEDY) + ps->glFrameTerminatorGREMEDY(); +#endif +} + static inline void free_texture(session_t *ps, glx_texture_t **pptex) { #ifdef CONFIG_VSYNC_OPENGL diff --git a/compton.c b/compton.c index cffee1a30..7e005b638 100644 --- a/compton.c +++ b/compton.c @@ -1402,6 +1402,8 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, static inline void win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, const reg_data_t *pcache_reg) { + glx_mark(ps, w->id, true); + // Fetch Pixmap if (!w->paint.pixmap && ps->has_name_pixmap) { set_ignore_next(ps); @@ -1564,6 +1566,8 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, #endif } } + + glx_mark(ps, w->id, false); } /** @@ -1812,6 +1816,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t default: assert(0); } + glx_mark_frame(ps); if (ps->o.vsync_aggressive) vsync_wait(ps); @@ -4050,7 +4055,7 @@ ev_handle(session_t *ps, XEvent *ev) { * Print usage text and exit. */ static void -usage(void) { +usage(int ret) { #define WARNING_DISABLED " (DISABLED AT COMPILE TIME)" #define WARNING const static char *usage_text = @@ -4277,11 +4282,12 @@ usage(void) { " Specify window ID to repaint in benchmark mode. If omitted or is 0,\n" " the whole screen is repainted.\n" ; - fputs(usage_text , stderr); + FILE *f = (ret ? stderr: stdout); + fputs(usage_text, f); #undef WARNING #undef WARNING_DISABLED - exit(1); + exit(ret); } /** @@ -4903,7 +4909,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { else if ('d' == o) ps->o.display = mstrcpy(optarg); else if ('?' == o || ':' == o) - usage(); + usage(1); } // Check for abundant positional arguments @@ -4944,7 +4950,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { #define P_CASEBOOL(idx, option) case idx: ps->o.option = true; break // Short options case 'h': - usage(); + usage(0); break; case 'd': break; @@ -5118,7 +5124,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; P_CASEBOOL(303, glx_use_gpushader4); default: - usage(); + usage(1); break; #undef P_CASEBOOL } diff --git a/compton.h b/compton.h index aba07c0d1..24325ab12 100644 --- a/compton.h +++ b/compton.h @@ -813,7 +813,7 @@ ev_window(session_t *ps, XEvent *ev); #endif static void __attribute__ ((noreturn)) -usage(void); +usage(int ret); static bool register_cm(session_t *ps); diff --git a/dbus.c b/dbus.c index a80c012dc..e48aa1a02 100644 --- a/dbus.c +++ b/dbus.c @@ -605,6 +605,20 @@ cdbus_process(session_t *ps, DBusMessage *msg) { "org.freedesktop.DBus.Introspectable", "Introspect")) { success = cdbus_process_introspect(ps, msg); } + else if (dbus_message_is_method_call(msg, + "org.freedesktop.DBus.Peer", "Ping")) { + cdbus_reply(ps, msg, NULL, NULL); + success = true; + } + else if (dbus_message_is_method_call(msg, + "org.freedesktop.DBus.Peer", "GetMachineId")) { + char *uuid = dbus_get_local_machine_id(); + if (uuid) { + cdbus_reply_string(ps, msg, uuid); + dbus_free(uuid); + success = true; + } + } else if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameAcquired") || dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameLost")) { success = true; @@ -1028,6 +1042,12 @@ cdbus_process_introspect(session_t *ps, DBusMessage *msg) { " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" " \n" " \n" diff --git a/opengl.c b/opengl.c index a4cb858f3..edd615132 100644 --- a/opengl.c +++ b/opengl.c @@ -90,6 +90,13 @@ glx_init(session_t *ps, bool need_render) { // Acquire function addresses if (need_render) { +#ifdef DEBUG_GLX_MARK + ps->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY) + glXGetProcAddress((const GLubyte *) "glStringMarkerGREMEDY"); + ps->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY) + glXGetProcAddress((const GLubyte *) "glFrameTerminatorGREMEDY"); +#endif + ps->glXBindTexImageProc = (f_BindTexImageEXT) glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT"); ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT) @@ -249,6 +256,8 @@ glx_init_blur(session_t *ps) { if (hei / 2 == i && wid / 2 == j) continue; double val = XFixedToDouble(ps->o.blur_kern[2 + i * wid + j]); + if (0.0 == val) + continue; sum += val; sprintf(pc, shader_add, val, texture_func, j - wid / 2, i - hei / 2); pc += strlen(pc); From 1bdd035974a0166434cdb2dd5d34405d6ddf184d Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 20 May 2013 18:04:40 +0800 Subject: [PATCH 143/178] Imp: Multi-pass blur & D-Bus fading control - Add multipass blur support. Note GLX Framebuffer support is required. My benchmark shows multipass blur brings 5% performance boost for X Render backend (3x3box). On GLX backend it brings 10% performance boost for 5x5box but negatively affects performance for 3x3box. Thanks to jrfonseca for advice. (#107) - GLX backend: Cache blur texture for each window, for a 12% performance boost. - Add D-Bus fading control. Thanks to yulan6248 for testing. (#112) - Fix FAQ link in README.md. Thanks to lorenzos for report. (#111) - Correctly deinitialize VSync on on-the-fly VSync method switch. - X Render backend: Normalize blur kernel. - Code clean-up. - Known issue: Linear corruption on border of a window may appear with X Render multi-pass blur. Possible to fix but probably not worthwhile. --- common.h | 172 +++++++++++++++++----- compton.c | 313 ++++++++++++++++++++++++++++++++-------- compton.h | 27 ++++ dbus.c | 53 ++++++- opengl.c | 424 ++++++++++++++++++++++++++++++++++++++---------------- 5 files changed, 765 insertions(+), 224 deletions(-) diff --git a/common.h b/common.h index bf9f8ae01..cab29f76a 100644 --- a/common.h +++ b/common.h @@ -26,6 +26,10 @@ // #define DEBUG_FRAME 1 // #define DEBUG_LEADER 1 // #define DEBUG_C2 1 +// #define DEBUG_GLX 1 +// #define DEBUG_GLX_GLSL 1 +// #define DEBUG_GLX_ERR 1 +// #define DEBUG_GLX_MARK 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled @@ -40,8 +44,12 @@ // #define CONFIG_LIBCONFIG_LEGACY 1 // Whether to enable DRM VSync support // #define CONFIG_VSYNC_DRM 1 -// Whether to enable OpenGL VSync support +// Whether to enable OpenGL support // #define CONFIG_VSYNC_OPENGL 1 +// Whether to enable GLX GLSL support +// #define CONFIG_VSYNC_OPENGL_GLSL 1 +// Whether to enable GLX FBO support +// #define CONFIG_VSYNC_OPENGL_FBO 1 // Whether to enable DBus support with libdbus. // #define CONFIG_DBUS 1 // Whether to enable condition support. @@ -96,7 +104,7 @@ // libGL #ifdef CONFIG_VSYNC_OPENGL -#ifdef CONFIG_VSYNC_OPENGL_GLSL +#if defined(CONFIG_VSYNC_OPENGL_GLSL) || defined(CONFIG_VSYNC_OPENGL_FBO) #define GL_GLEXT_PROTOTYPES #endif @@ -183,6 +191,9 @@ /// @brief Maximum OpenGL buffer age. #define CGLX_MAX_BUFFER_AGE 5 +/// @brief Maximum passes for blur. +#define MAX_BLUR_PASS 5 + // Window flags // Window size is changed @@ -343,6 +354,32 @@ struct _glx_texture { }; #endif +#ifdef CONFIG_VSYNC_OPENGL_GLSL +typedef struct { + /// Fragment shader for blur. + GLuint frag_shader; + /// GLSL program for blur. + GLuint prog; + /// Location of uniform "offset_x" in blur GLSL program. + GLint unifm_offset_x; + /// Location of uniform "offset_y" in blur GLSL program. + GLint unifm_offset_y; + /// Location of uniform "factor_center" in blur GLSL program. + GLint unifm_factor_center; +} glx_blur_pass_t; + +typedef struct { + /// Framebuffer used for blurring. + GLuint fbo; + /// Textures used for blurring. + GLuint textures[2]; + /// Width of the textures. + int width; + /// Height of the textures. + int height; +} glx_blur_cache_t; +#endif + typedef struct { Pixmap pixmap; Picture pict; @@ -501,7 +538,7 @@ typedef struct { /// Background blur blacklist. A linked list of conditions. c2_lptr_t *blur_background_blacklist; /// Blur convolution kernel. - XFixed *blur_kern; + XFixed *blur_kerns[MAX_BLUR_PASS]; /// How much to dim an inactive window. 0.0 - 1.0, 0 to disable. double inactive_dim; /// Whether to use fixed inactive dim opacity, instead of deciding @@ -614,6 +651,8 @@ typedef struct { /// Current GLX Z value. int glx_z; #endif + // Cached blur convolution kernels. + XFixed *blur_kerns_cache[MAX_BLUR_PASS]; /// Reset program after next paint. bool reset; @@ -702,16 +741,7 @@ typedef struct { /// FBConfig-s for GLX pixmap of different depths. glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1]; #ifdef CONFIG_VSYNC_OPENGL_GLSL - /// Fragment shader for blur. - GLuint glx_frag_shader_blur; - /// GLSL program for blur. - GLuint glx_prog_blur; - /// Location of uniform "offset_x" in blur GLSL program. - GLint glx_prog_blur_unifm_offset_x; - /// Location of uniform "offset_y" in blur GLSL program. - GLint glx_prog_blur_unifm_offset_y; - /// Location of uniform "factor_center" in blur GLSL program. - GLint glx_prog_blur_unifm_factor_center; + glx_blur_pass_t glx_blur_passes[MAX_BLUR_PASS]; #endif #endif @@ -908,6 +938,8 @@ typedef struct _win { /// Do not fade if it's false. Change on window type change. /// Used by fading blacklist in the future. bool fade; + /// Override value of window fade state. Set by D-Bus method calls. + switch_t fade_force; /// Callback to be called after fading completed. void (*fade_callback) (session_t *ps, struct _win *w); @@ -950,6 +982,11 @@ typedef struct _win { /// Whether to blur window background. bool blur_background; + +#ifdef CONFIG_VSYNC_OPENGL_GLSL + /// Textures and FBO background blur use. + glx_blur_cache_t glx_blur_cache; +#endif } win; /// Temporary structure used for communication between @@ -1749,6 +1786,9 @@ force_repaint(session_t *ps); bool vsync_init(session_t *ps); +void +vsync_deinit(session_t *ps); + #ifdef CONFIG_VSYNC_OPENGL /** @name GLX */ @@ -1788,9 +1828,13 @@ glx_tex_binded(const glx_texture_t *ptex, Pixmap pixmap) { void glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg); +#ifdef CONFIG_VSYNC_OPENGL_GLSL bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg); + GLfloat factor_center, + XserverRegion reg_tgt, const reg_data_t *pcache_reg, + glx_blur_cache_t *pbc); +#endif bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, @@ -1812,7 +1856,78 @@ glx_create_shader(GLenum shader_type, const char *shader_str); GLuint glx_create_program(const GLuint * const shaders, int nshaders); #endif + +/** + * Free a GLX texture. + */ +static inline void +free_texture_r(session_t *ps, GLuint *ptexture) { + if (*ptexture) { + assert(ps->glx_context); + glDeleteTextures(1, ptexture); + *ptexture = 0; + } +} + +/** + * Free a GLX Framebuffer object. + */ +static inline void +free_glx_fbo(session_t *ps, GLuint *pfbo) { +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (*pfbo) { + glDeleteFramebuffers(1, pfbo); + *pfbo = 0; + } #endif + assert(!*pfbo); +} + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +/** + * Free data in glx_blur_cache_t on resize. + */ +static inline void +free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) { + free_texture_r(ps, &pbc->textures[0]); + free_texture_r(ps, &pbc->textures[1]); + pbc->width = 0; + pbc->height = 0; +} + +/** + * Free a glx_blur_cache_t + */ +static inline void +free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) { + free_glx_fbo(ps, &pbc->fbo); + free_glx_bc_resize(ps, pbc); +} +#endif +#endif + +/** + * Free a glx_texture_t. + */ +static inline void +free_texture(session_t *ps, glx_texture_t **pptex) { + glx_texture_t *ptex = *pptex; + + // Quit if there's nothing + if (!ptex) + return; + +#ifdef CONFIG_VSYNC_OPENGL + glx_release_pixmap(ps, ptex); + + free_texture_r(ps, &ptex->texture); + + // Free structure itself + free(ptex); + *pptex = NULL; +#endif + assert(!*pptex); +} /** * Add a OpenGL debugging marker. @@ -1846,29 +1961,6 @@ glx_mark_frame(session_t *ps) { #endif } -static inline void -free_texture(session_t *ps, glx_texture_t **pptex) { -#ifdef CONFIG_VSYNC_OPENGL - glx_texture_t *ptex = *pptex; - - // Quit if there's nothing - if (!ptex) - return; - - glx_release_pixmap(ps, ptex); - - // Free texture - if (ptex->texture) { - glDeleteTextures(1, &ptex->texture); - ptex->texture = 0; - } - - // Free structure itself - free(ptex); - *pptex = NULL; -#endif -} - ///@} /** @name DBus handling @@ -1912,6 +2004,9 @@ cdbus_ev_win_focusin(session_t *ps, win *w); void win_set_shadow_force(session_t *ps, win *w, switch_t val); +void +win_set_fade_force(session_t *ps, win *w, switch_t val); + void win_set_focused_force(session_t *ps, win *w, switch_t val); @@ -1920,6 +2015,9 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val); void opts_init_track_focus(session_t *ps); + +void +opts_set_no_fading_openclose(session_t *ps, bool newval); //!@} #endif diff --git a/compton.c b/compton.c index 7e005b638..f058bf927 100644 --- a/compton.c +++ b/compton.c @@ -70,6 +70,14 @@ static int (* const (VSYNC_FUNCS_WAIT[NUM_VSYNC]))(session_t *ps) = { #endif }; +/// Function pointers to deinitialize VSync. +static void (* const (VSYNC_FUNCS_DEINIT[NUM_VSYNC]))(session_t *ps) = { +#ifdef CONFIG_VSYNC_OPENGL + [VSYNC_OPENGL_SWC ] = vsync_opengl_swc_deinit, + [VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_deinit, +#endif +}; + /// Names of root window properties that could point to a pixmap of /// background. const static char *background_props_str[] = { @@ -1276,13 +1284,11 @@ win_paint_shadow(session_t *ps, win *w, } /** - * Create an alternative picture for a window. + * Create an picture. */ static inline Picture -win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { - const int wid = w->widthb; - const int hei = w->heightb; - +xr_build_picture(session_t *ps, int wid, int hei, + XRenderPictFormat *pictfmt) { if (!pictfmt) pictfmt = XRenderFindVisualFormat(ps->dpy, ps->vis); @@ -1299,6 +1305,73 @@ win_build_picture(session_t *ps, win *w, XRenderPictFormat *pictfmt) { return tmp_picture; } +/** + * @brief Blur an area on a buffer. + * + * @param ps current session + * @param tgt_buffer a buffer as both source and destination + * @param x x pos + * @param y y pos + * @param wid width + * @param hei height + * @param blur_kerns blur kernels, ending with a NULL, guaranteed to have at + * least one kernel + * @param reg_clip a clipping region to be applied on intermediate buffers + * + * @return true if successful, false otherwise + */ +static bool +xr_blur_dst(session_t *ps, Picture tgt_buffer, + int x, int y, int wid, int hei, XFixed **blur_kerns, + XserverRegion reg_clip) { + assert(blur_kerns[0]); + + // Directly copying from tgt_buffer to it does not work, so we create a + // Picture in the middle. + Picture tmp_picture = xr_build_picture(ps, wid, hei, NULL); + + if (!tmp_picture) { + printf_errf("(): Failed to build intermediate Picture."); + return false; + } + + if (reg_clip && tmp_picture) + XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_clip, 0, 0); + + Picture src_pict = tgt_buffer, dst_pict = tmp_picture; + for (int i = 0; blur_kerns[i]; ++i) { + assert(i < MAX_BLUR_PASS - 1); + XFixed *convolution_blur = blur_kerns[i]; + int kwid = XFixedToDouble(convolution_blur[0]), + khei = XFixedToDouble(convolution_blur[1]); + bool rd_from_tgt = (tgt_buffer == src_pict); + + // Copy from source picture to destination. The filter must + // be applied on source picture, to get the nearby pixels outside the + // window. + XRenderSetPictureFilter(ps->dpy, src_pict, XRFILTER_CONVOLUTION, + convolution_blur, kwid * khei + 2); + XRenderComposite(ps->dpy, PictOpSrc, src_pict, None, dst_pict, + (rd_from_tgt ? x: 0), (rd_from_tgt ? y: 0), 0, 0, + (rd_from_tgt ? 0: x), (rd_from_tgt ? 0: y), wid, hei); + xrfilter_reset(ps, src_pict); + + { + XserverRegion tmp = src_pict; + src_pict = dst_pict; + dst_pict = tmp; + } + } + + if (src_pict != tgt_buffer) + XRenderComposite(ps->dpy, PictOpSrc, src_pict, None, tgt_buffer, + 0, 0, 0, 0, x, y, wid, hei); + + free_picture(ps, &tmp_picture); + + return true; +} + /** * Blur the background of a window. */ @@ -1321,46 +1394,63 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, switch (ps->o.backend) { case BKEND_XRENDER: { - // Directly copying from tgt_buffer does not work, so we create a - // Picture in the middle. - Picture tmp_picture = win_build_picture(ps, w, NULL); + // Normalize blur kernels + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + XFixed *kern_src = ps->o.blur_kerns[i]; + XFixed *kern_dst = ps->blur_kerns_cache[i]; + assert(i < MAX_BLUR_PASS); + if (!kern_src) { + assert(!kern_dst); + break; + } + + assert(!kern_dst + || (kern_src[0] == kern_dst[0] && kern_src[1] == kern_dst[1])); + + // Skip for fixed factor_center if the cache exists already + if (ps->o.blur_background_fixed && kern_dst) continue; + + int kwid = XFixedToDouble(kern_src[0]), + khei = XFixedToDouble(kern_src[1]); - if (!tmp_picture) - return; + // Allocate cache space if needed + if (!kern_dst) { + kern_dst = malloc((kwid * khei + 2) * sizeof(XFixed)); + if (!kern_dst) { + printf_errf("(): Failed to allocate memory for blur kernel."); + return; + } + ps->blur_kerns_cache[i] = kern_dst; + } - XFixed *convolution_blur = ps->o.blur_kern; - int kwid = XFixedToDouble((ps->o.blur_kern[0])), - khei = XFixedToDouble((ps->o.blur_kern[1])); + // Modify the factor of the center pixel + kern_src[2 + (khei / 2) * kwid + kwid / 2] = + XDoubleToFixed(factor_center); - // Modify the factor of the center pixel - convolution_blur[2 + (khei / 2) * kwid + kwid / 2] = XDoubleToFixed(factor_center); + // Copy over + memcpy(kern_dst, kern_src, (kwid * khei + 2) * sizeof(XFixed)); + normalize_conv_kern(kwid, khei, kern_dst + 2); + } // Minimize the region we try to blur, if the window itself is not // opaque, only the frame is. - if (WMODE_SOLID == w->mode && w->frame_opacity) { + XserverRegion reg_noframe = None; + if (WMODE_SOLID == w->mode) { XserverRegion reg_all = border_size(ps, w, false); - XserverRegion reg_noframe = win_get_region_noframe(ps, w, false); + reg_noframe = win_get_region_noframe(ps, w, false); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); - XFixesSetPictureClipRegion(ps->dpy, tmp_picture, reg_noframe, 0, 0); free_region(ps, ®_all); - free_region(ps, ®_noframe); } - - // Copy the content to tmp_picture, then copy back. The filter must - // be applied on tgt_buffer, to get the nearby pixels outside the - // window. - XRenderSetPictureFilter(ps->dpy, tgt_buffer, XRFILTER_CONVOLUTION, convolution_blur, kwid * khei + 2); - XRenderComposite(ps->dpy, PictOpSrc, tgt_buffer, None, tmp_picture, x, y, 0, 0, 0, 0, wid, hei); - xrfilter_reset(ps, tgt_buffer); - XRenderComposite(ps->dpy, PictOpSrc, tmp_picture, None, tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); - - free_picture(ps, &tmp_picture); + xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, + reg_noframe); + free_region(ps, ®_noframe); } break; -#ifdef CONFIG_VSYNC_OPENGL +#ifdef CONFIG_VSYNC_OPENGL_GLSL case BKEND_GLX: + // TODO: Handle frame opacity glx_blur_dst(ps, x, y, wid, hei, ps->glx_z - 0.5, factor_center, - reg_paint, pcache_reg); + reg_paint, pcache_reg, &w->glx_blur_cache); break; #endif default: @@ -1447,7 +1537,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, // Invert window color, if required if (BKEND_XRENDER == ps->o.backend && w->invert_color) { - Picture newpict = win_build_picture(ps, w, w->pictfmt); + Picture newpict = xr_build_picture(ps, wid, hei, w->pictfmt); if (newpict) { // Apply clipping region to save some CPU if (reg_paint) { @@ -1993,14 +2083,8 @@ map_win(session_t *ps, Window id) { win_determine_shadow(ps, w); // Set fading state - w->in_openclose = false; - if (ps->o.no_fading_openclose) { - set_fade_callback(ps, w, finish_map_win, true); - w->in_openclose = true; - } - else { - set_fade_callback(ps, w, NULL, true); - } + w->in_openclose = true; + set_fade_callback(ps, w, finish_map_win, true); win_determine_fade(ps, w); win_determine_blur_background(ps, w); @@ -2066,9 +2150,7 @@ unmap_win(session_t *ps, win *w) { // Fading out w->flags |= WFLAG_OPCT_CHANGE; set_fade_callback(ps, w, unmap_callback, false); - if (ps->o.no_fading_openclose) { - w->in_openclose = true; - } + w->in_openclose = true; win_determine_fade(ps, w); // Validate pixmap if we have to do fading @@ -2195,7 +2277,9 @@ calc_dim(session_t *ps, win *w) { */ static void win_determine_fade(session_t *ps, win *w) { - if ((ps->o.no_fading_openclose && w->in_openclose) + if (UNSET != w->fade_force) + w->fade = w->fade_force; + else if ((ps->o.no_fading_openclose && w->in_openclose) || win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) w->fade = false; else @@ -2593,6 +2677,7 @@ add_win(session_t *ps, Window id, Window prev) { .opacity_prop_client = OPAQUE, .fade = false, + .fade_force = UNSET, .fade_callback = NULL, .frame_opacity = 0.0, @@ -3418,6 +3503,19 @@ win_set_shadow_force(session_t *ps, win *w, switch_t val) { if (val != w->shadow_force) { w->shadow_force = val; win_determine_shadow(ps, w); + ps->ev_received = true; + } +} + +/** + * Set w->fade_force of a window. + */ +void +win_set_fade_force(session_t *ps, win *w, switch_t val) { + if (val != w->fade_force) { + w->fade_force = val; + win_determine_fade(ps, w); + ps->ev_received = true; } } @@ -3429,6 +3527,7 @@ win_set_focused_force(session_t *ps, win *w, switch_t val) { if (val != w->focused_force) { w->focused_force = val; win_update_focused(ps, w); + ps->ev_received = true; } } @@ -3440,6 +3539,7 @@ win_set_invert_color_force(session_t *ps, win *w, switch_t val) { if (val != w->invert_color_force) { w->invert_color_force = val; win_determine_invert_color(ps, w); + ps->ev_received = true; } } @@ -3465,6 +3565,20 @@ opts_init_track_focus(session_t *ps) { // Recheck focus recheck_focus(ps); } + +/** + * Set no_fading_openclose option. + */ +void +opts_set_no_fading_openclose(session_t *ps, bool newval) { + if (newval != ps->o.no_fading_openclose) { + ps->o.no_fading_openclose = newval; + for (win *w = ps->list; w; w = w->next) + win_determine_fade(ps, w); + ps->ev_received = true; + } +} + //!@} #endif @@ -4222,6 +4336,8 @@ usage(int ret) { " --blur-background-fixed.\n" " A 7x7 Guassian blur kernel looks like:\n" " --blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'\n" + " Up to 4 blur kernels may be specified, separated with semicolon, for\n" + " multi-pass blur.\n" " May also be one the predefined kernels: 3x3box (default), 5x5box,\n" " 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian,\n" " 11x11gaussian.\n" @@ -4419,7 +4535,7 @@ parse_matrix_readnum(const char *src, double *dest) { * Parse a matrix. */ static inline XFixed * -parse_matrix(session_t *ps, const char *src) { +parse_matrix(session_t *ps, const char *src, const char **endptr) { int wid = 0, hei = 0; const char *pc = NULL; XFixed *matrix = NULL; @@ -4481,12 +4597,28 @@ parse_matrix(session_t *ps, const char *src) { } // Detect trailing characters - for ( ;*pc; ++pc) + for ( ;*pc && ';' != *pc; ++pc) if (!isspace(*pc) && ',' != *pc) { printf_errf("(): Trailing characters in matrix string."); goto parse_matrix_err; } + // Jump over spaces after ';' + if (';' == *pc) { + ++pc; + while (*pc && isspace(*pc)) + ++pc; + } + + // Require an end of string if endptr is not provided, otherwise + // copy end pointer to endptr + if (endptr) + *endptr = pc; + else if (*pc) { + printf_errf("(): Only one matrix expected."); + goto parse_matrix_err; + } + // Fill in width and height matrix[0] = XDoubleToFixed(wid); matrix[1] = XDoubleToFixed(hei); @@ -4502,7 +4634,12 @@ parse_matrix_err: * Parse a convolution kernel. */ static inline XFixed * -parse_conv_kern(session_t *ps, const char *src) { +parse_conv_kern(session_t *ps, const char *src, const char **endptr) { + return parse_matrix(ps, src, endptr); +} + +static bool +parse_conv_kern_lst(session_t *ps, const char *src, XFixed **dest, int max) { static const struct { const char *name; const char *kern_str; @@ -4519,8 +4656,30 @@ parse_conv_kern(session_t *ps, const char *src) { for (int i = 0; i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i) if (!strcmp(CONV_KERN_PREDEF[i].name, src)) - return parse_matrix(ps, CONV_KERN_PREDEF[i].kern_str); - return parse_matrix(ps, src); + return parse_conv_kern_lst(ps, CONV_KERN_PREDEF[i].kern_str, dest, max); + + int i = 0; + const char *pc = src; + + // Free old kernels + for (i = 0; i < max; ++i) { + free(dest[i]); + dest[i] = NULL; + } + + // Continue parsing until the end of source string + i = 0; + while (pc && *pc && i < max - 1) { + if (!(dest[i++] = parse_conv_kern(ps, pc, &pc))) + return false; + } + + if (*pc) { + printf_errf("(): Too many blur kernels!"); + return false; + } + + return true; } #ifdef CONFIG_LIBCONFIG @@ -5115,9 +5274,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; case 301: // --blur-kern - free(ps->o.blur_kern); - if (!(ps->o.blur_kern = parse_conv_kern(ps, optarg))) + if (!parse_conv_kern_lst(ps, optarg, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); + break; case 302: // --resize-damage ps->o.resize_damage = atoi(optarg); @@ -5187,7 +5346,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { } // Fill default blur kernel - if (ps->o.blur_background && !ps->o.blur_kern) { + if (ps->o.blur_background && !ps->o.blur_kerns[0]) { // Convolution filter parameter (box blur) // gaussian or binomial filters are definitely superior, yet looks // like they aren't supported as of xorg-server-1.13.0 @@ -5200,12 +5359,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), XDoubleToFixed(1), }; - ps->o.blur_kern = malloc(sizeof(convolution_blur)); - if (!ps->o.blur_kern) { + ps->o.blur_kerns[0] = malloc(sizeof(convolution_blur)); + if (!ps->o.blur_kerns[0]) { printf_errf("(): Failed to allocate memory for convolution kernel."); exit(1); } - memcpy(ps->o.blur_kern, &convolution_blur, sizeof(convolution_blur)); + memcpy(ps->o.blur_kerns[0], &convolution_blur, sizeof(convolution_blur)); } } @@ -5536,6 +5695,19 @@ vsync_opengl_oml_wait(session_t *ps) { return 0; } + +static void +vsync_opengl_swc_deinit(session_t *ps) { + // The standard says it doesn't accept 0, but in fact it probably does + if (ps->glx_context && ps->glXSwapIntervalProc) + ps->glXSwapIntervalProc(0); +} + +static void +vsync_opengl_mswc_deinit(session_t *ps) { + if (ps->glx_context && ps->glXSwapIntervalMESAProc) + ps->glXSwapIntervalMESAProc(0); +} #endif /** @@ -5564,6 +5736,16 @@ vsync_wait(session_t *ps) { VSYNC_FUNCS_WAIT[ps->o.vsync](ps); } +/** + * Deinitialize current VSync method. + */ +void +vsync_deinit(session_t *ps) { + if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync]) + VSYNC_FUNCS_DEINIT[ps->o.vsync](ps); + ps->o.vsync = VSYNC_NONE; +} + /** * Pregenerate alpha pictures. */ @@ -6030,7 +6212,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .blur_background_frame = false, .blur_background_fixed = false, .blur_background_blacklist = NULL, - .blur_kern = NULL, + .blur_kerns = { NULL }, .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, @@ -6096,12 +6278,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .glXWaitVideoSyncSGI = NULL, .glXGetSyncValuesOML = NULL, .glXWaitForMscOML = NULL, - -#ifdef CONFIG_VSYNC_OPENGL_GLSL - .glx_prog_blur_unifm_offset_x = -1, - .glx_prog_blur_unifm_offset_y = -1, - .glx_prog_blur_unifm_factor_center = -1, -#endif #endif .xfixes_event = 0, @@ -6151,6 +6327,14 @@ session_init(session_t *ps_old, int argc, char **argv) { // Allocate a session and copy default values into it session_t *ps = malloc(sizeof(session_t)); memcpy(ps, &s_def, sizeof(session_t)); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + ppass->unifm_factor_center = -1; + ppass->unifm_offset_x = -1; + ppass->unifm_offset_y = -1; + } +#endif ps_g = ps; ps->ignore_tail = &ps->ignore_head; gettimeofday(&ps->time_start, NULL); @@ -6513,7 +6697,10 @@ session_destroy(session_t *ps) { free(ps->o.display); free(ps->o.logpath); free(ps->o.config_file); - free(ps->o.blur_kern); + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + free(ps->o.blur_kerns[i]); + free(ps->blur_kerns_cache[i]); + } free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); diff --git a/compton.h b/compton.h index 24325ab12..b3695af29 100644 --- a/compton.h +++ b/compton.h @@ -231,6 +231,9 @@ free_win_res(session_t *ps, win *w) { free(w->class_instance); free(w->class_general); free(w->role); +#ifdef CONFIG_VSYNC_OPENGL_GLSL + free_glx_bc(ps, &w->glx_blur_cache); +#endif } /** @@ -594,6 +597,24 @@ set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { } } +static bool +xr_blur_dst(session_t *ps, Picture tgt_buffer, + int x, int y, int wid, int hei, XFixed **blur_kerns, + XserverRegion reg_clip); + +/** + * Normalize a convolution kernel. + */ +static inline void +normalize_conv_kern(int wid, int hei, XFixed *kern) { + double sum = 0.0; + for (int i = 0; i < wid * hei; ++i) + sum += XFixedToDouble(kern[i]); + double factor = 1.0 / sum; + for (int i = 0; i < wid * hei; ++i) + kern[i] = XDoubleToFixed(XFixedToDouble(kern[i]) * factor); +} + static void paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t); @@ -1095,6 +1116,12 @@ vsync_opengl_wait(session_t *ps); static int vsync_opengl_oml_wait(session_t *ps); + +static void +vsync_opengl_swc_deinit(session_t *ps); + +static void +vsync_opengl_mswc_deinit(session_t *ps); #endif static void diff --git a/dbus.c b/dbus.c index e48aa1a02..f2b019aa3 100644 --- a/dbus.c +++ b/dbus.c @@ -717,6 +717,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(wmwin, cdbus_reply_bool); cdbus_m_win_get_do(leader, cdbus_reply_wid); cdbus_m_win_get_do(focused_real, cdbus_reply_bool); + cdbus_m_win_get_do(fade_force, cdbus_reply_enum); cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); cdbus_m_win_get_do(focused_force, cdbus_reply_enum); cdbus_m_win_get_do(invert_color_force, cdbus_reply_enum); @@ -770,8 +771,6 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) { return true; } - ps->ev_received = true; - #define cdbus_m_win_set_do(tgt, type, real_type) \ if (!strcmp(MSTR(tgt), target)) { \ real_type val; \ @@ -789,6 +788,14 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg) { goto cdbus_process_win_set_success; } + if (!strcmp("fade_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) + return false; + win_set_fade_force(ps, w, val); + goto cdbus_process_win_set_success; + } + if (!strcmp("focused_force", target)) { cdbus_enum_t val = UNSET; if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) @@ -912,6 +919,11 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double); cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); + cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32); + cdbus_m_opts_get_do(fade_in_step, cdbus_reply_int32); + cdbus_m_opts_get_do(fade_out_step, cdbus_reply_int32); + cdbus_m_opts_get_do(no_fading_openclose, cdbus_reply_bool); + cdbus_m_opts_get_do(blur_background, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background_frame, cdbus_reply_bool); cdbus_m_opts_get_do(blur_background_fixed, cdbus_reply_bool); @@ -961,6 +973,42 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { goto cdbus_process_opts_set_success; \ } + // fade_delta + if (!strcmp("fade_delta", target)) { + int32_t val = 0.0; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_INT32, &val)) + return false; + ps->o.fade_delta = max_i(val, 1); + goto cdbus_process_opts_set_success; + } + + // fade_in_step + if (!strcmp("fade_in_step", target)) { + double val = 0.0; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) + return false; + ps->o.fade_in_step = normalize_d(val) * OPAQUE; + goto cdbus_process_opts_set_success; + } + + // fade_out_step + if (!strcmp("fade_out_step", target)) { + double val = 0.0; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) + return false; + ps->o.fade_out_step = normalize_d(val) * OPAQUE; + goto cdbus_process_opts_set_success; + } + + // no_fading_openclose + if (!strcmp("no_fading_openclose", target)) { + dbus_bool_t val = FALSE; + if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) + return false; + opts_set_no_fading_openclose(ps, val); + goto cdbus_process_opts_set_success; + } + // unredir_if_possible if (!strcmp("unredir_if_possible", target)) { dbus_bool_t val = FALSE; @@ -1002,6 +1050,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { const char * val = NULL; if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_STRING, &val)) return false; + vsync_deinit(ps); if (!parse_vsync(ps, val)) { printf_errf("(): " CDBUS_ERROR_BADARG_S, 1, "Value invalid."); cdbus_reply_err(ps, msg, CDBUS_ERROR_BADARG, CDBUS_ERROR_BADARG_S, 1, "Value invalid."); diff --git a/opengl.c b/opengl.c index edd615132..635cd33ef 100644 --- a/opengl.c +++ b/opengl.c @@ -160,10 +160,13 @@ void glx_destroy(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL_GLSL // Free GLSL shaders/programs - if (ps->glx_frag_shader_blur) - glDeleteShader(ps->glx_frag_shader_blur); - if (ps->glx_prog_blur) - glDeleteProgram(ps->glx_prog_blur); + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + if (ppass->frag_shader) + glDeleteShader(ppass->frag_shader); + if (ppass->prog) + glDeleteProgram(ppass->prog); + } #endif // Free FBConfigs @@ -199,8 +202,28 @@ glx_on_root_change(session_t *ps) { */ bool glx_init_blur(session_t *ps) { + assert(ps->o.blur_kerns[0]); + + // Allocate PBO if more than one blur kernel is present + if (ps->o.blur_kerns[1]) { +#ifdef CONFIG_VSYNC_OPENGL_FBO + // Try to generate a framebuffer + GLuint fbo = 0; + glGenFramebuffers(1, &fbo); + if (!fbo) { + printf_errf("(): Failed to generate Framebuffer. Cannot do " + "multi-pass blur with GLX backend."); + return false; + } + glDeleteFramebuffers(1, &fbo); +#else + printf_errf("(): FBO support not compiled in. Cannot do multi-pass blur " + "with GLX backend."); + return false; +#endif + } + #ifdef CONFIG_VSYNC_OPENGL_GLSL - // Build shader { static const char *FRAG_SHADER_BLUR_PREFIX = "#version 110\n" @@ -235,73 +258,81 @@ glx_init_blur(session_t *ps) { shader_add = FRAG_SHADER_BLUR_ADD_GPUSHADER4; } - int wid = XFixedToDouble(ps->o.blur_kern[0]), - hei = XFixedToDouble(ps->o.blur_kern[1]); - int nele = wid * hei - 1; - int len = strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele + strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; - char *shader_str = calloc(len, sizeof(char)); - if (!shader_str) { - printf_errf("(): Failed to allocate %d bytes for shader string.", len); - return false; - } - { - char *pc = shader_str; - sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type); - pc += strlen(pc); - assert(strlen(shader_str) < len); - - double sum = 0.0; - for (int i = 0; i < hei; ++i) { - for (int j = 0; j < wid; ++j) { - if (hei / 2 == i && wid / 2 == j) - continue; - double val = XFixedToDouble(ps->o.blur_kern[2 + i * wid + j]); - if (0.0 == val) - continue; - sum += val; - sprintf(pc, shader_add, val, texture_func, j - wid / 2, i - hei / 2); + for (int i = 0; i < MAX_BLUR_PASS && ps->o.blur_kerns[i]; ++i) { + XFixed *kern = ps->o.blur_kerns[i]; + if (!kern) + break; + + glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + + // Build shader + { + int wid = XFixedToDouble(kern[0]), hei = XFixedToDouble(kern[1]); + int nele = wid * hei - 1; + int len = strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + strlen(extension) + (strlen(shader_add) + strlen(texture_func) + 42) * nele + strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; + char *shader_str = calloc(len, sizeof(char)); + if (!shader_str) { + printf_errf("(): Failed to allocate %d bytes for shader string.", len); + return false; + } + { + char *pc = shader_str; + sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type); pc += strlen(pc); assert(strlen(shader_str) < len); + + double sum = 0.0; + for (int j = 0; j < hei; ++j) { + for (int k = 0; k < wid; ++k) { + if (hei / 2 == j && wid / 2 == k) + continue; + double val = XFixedToDouble(kern[2 + j * wid + k]); + if (0.0 == val) + continue; + sum += val; + sprintf(pc, shader_add, val, texture_func, k - wid / 2, j - hei / 2); + pc += strlen(pc); + assert(strlen(shader_str) < len); + } + } + + sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); + assert(strlen(shader_str) < len); } + ppass->frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); + free(shader_str); } - sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); - assert(strlen(shader_str) < len); -#ifdef DEBUG_GLX_GLSL - fputs(shader_str, stdout); - fflush(stdout); -#endif - } - ps->glx_frag_shader_blur = glx_create_shader(GL_FRAGMENT_SHADER, shader_str); - free(extension); - free(shader_str); - } - - if (!ps->glx_frag_shader_blur) { - printf_errf("(): Failed to create fragment shader."); - return false; - } + if (!ppass->frag_shader) { + printf_errf("(): Failed to create fragment shader %d.", i); + return false; + } - ps->glx_prog_blur = glx_create_program(&ps->glx_frag_shader_blur, 1); - if (!ps->glx_prog_blur) { - printf_errf("(): Failed to create GLSL program."); - return false; - } + // Build program + ppass->prog = glx_create_program(&ppass->frag_shader, 1); + if (!ppass->prog) { + printf_errf("(): Failed to create GLSL program."); + return false; + } + // Get uniform addresses #define P_GET_UNIFM_LOC(name, target) { \ - ps->target = glGetUniformLocation(ps->glx_prog_blur, name); \ - if (ps->target < 0) { \ - printf_errf("(): Failed to get location of uniform '" name "'. Might be troublesome."); \ - } \ -} + ppass->target = glGetUniformLocation(ppass->prog, name); \ + if (ppass->target < 0) { \ + printf_errf("(): Failed to get location of %d-th uniform '" name "'. Might be troublesome.", i); \ + } \ + } - P_GET_UNIFM_LOC("factor_center", glx_prog_blur_unifm_factor_center); - if (!ps->o.glx_use_gpushader4) { - P_GET_UNIFM_LOC("offset_x", glx_prog_blur_unifm_offset_x); - P_GET_UNIFM_LOC("offset_y", glx_prog_blur_unifm_offset_y); + P_GET_UNIFM_LOC("factor_center", unifm_factor_center); + if (!ps->o.glx_use_gpushader4) { + P_GET_UNIFM_LOC("offset_x", unifm_offset_x); + P_GET_UNIFM_LOC("offset_y", unifm_offset_y); + } +#undef P_GET_UNIFM_LOC + } + free(extension); } -#undef P_GET_UNIFM_LOC #ifdef DEBUG_GLX_ERR glx_check_err(ps); @@ -841,9 +872,9 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { } \ glBegin(GL_QUADS); \ \ - for (int i = 0; i < nrects; ++i) { \ + for (int ri = 0; ri < nrects; ++ri) { \ XRectangle crect; \ - rect_crop(&crect, &rects[i], &rec_all); \ + rect_crop(&crect, &rects[ri], &rec_all); \ \ if (!crect.width || !crect.height) \ continue; \ @@ -856,21 +887,69 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { cxfree(rects); \ free_region(ps, ®_new); \ +static inline GLuint +glx_gen_texture(session_t *ps, GLenum tex_tgt, int width, int height) { + GLuint tex = 0; + glGenTextures(1, &tex); + if (!tex) return 0; + glEnable(tex_tgt); + glBindTexture(tex_tgt, tex); + glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_RGB, + GL_UNSIGNED_BYTE, NULL); + glBindTexture(tex_tgt, 0); + + return tex; +} + +static inline void +glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int basex, int basey, + int dx, int dy, int width, int height) { + if (width > 0 && height > 0) + glCopyTexSubImage2D(tex_tgt, 0, dx - basex, dy - basey, + dx, ps->root_height - dy - height, width, height); +} + +#ifdef CONFIG_VSYNC_OPENGL_GLSL +/** + * Blur contents in a particular region. + */ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor_center, XserverRegion reg_tgt, const reg_data_t *pcache_reg) { - // Read destination pixels into a texture - GLuint tex_scr = 0; - glGenTextures(1, &tex_scr); - if (!tex_scr) { - printf_errf("(): Failed to allocate texture."); - return false; - } + GLfloat factor_center, + XserverRegion reg_tgt, const reg_data_t *pcache_reg, + glx_blur_cache_t *pbc) { + assert(ps->glx_blur_passes[0].prog); + const bool more_passes = ps->glx_blur_passes[1].prog; + const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); + const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); + bool ret = false; + + // Calculate copy region size + glx_blur_cache_t ibc = { .width = 0, .height = 0 }; + if (!pbc) + pbc = &ibc; int mdx = dx, mdy = dy, mwidth = width, mheight = height; +#ifdef DEBUG_GLX + printf_dbgf("(): %d, %d, %d, %d\n", mdx, mdy, mwidth, mheight); +#endif + + /* if (ps->o.resize_damage > 0) { - int inc_x = min_i(ps->o.resize_damage, XFixedToDouble(ps->o.blur_kern[0]) / 2), - inc_y = min_i(ps->o.resize_damage, XFixedToDouble(ps->o.blur_kern[1]) / 2); + int inc_x = 0, inc_y = 0; + for (int i = 0; i < MAX_BLUR_PASS; ++i) { + XFixed *kern = ps->o.blur_kerns[i]; + if (!kern) break; + inc_x += XFixedToDouble(kern[0]) / 2; + inc_y += XFixedToDouble(kern[1]) / 2; + } + inc_x = min_i(ps->o.resize_damage, inc_x); + inc_y = min_i(ps->o.resize_damage, inc_y); + mdx = max_i(dx - inc_x, 0); mdy = max_i(dy - inc_y, 0); int mdx2 = min_i(dx + width + inc_x, ps->root_width), @@ -878,24 +957,57 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, mwidth = mdx2 - mdx; mheight = mdy2 - mdy; } + */ GLenum tex_tgt = GL_TEXTURE_RECTANGLE; if (ps->glx_has_texture_non_power_of_two) tex_tgt = GL_TEXTURE_2D; - glEnable(tex_tgt); - glBindTexture(tex_tgt, tex_scr); - glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(tex_tgt, 0, GL_RGB, mwidth, mheight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glCopyTexSubImage2D(tex_tgt, 0, 0, 0, mdx, ps->root_height - mdy - mheight, mwidth, mheight); + // Free textures if size inconsistency discovered + if (mwidth != pbc->width || mheight != pbc->height) + free_glx_bc_resize(ps, pbc); + + // Generate FBO and textures if needed + if (!pbc->textures[0]) + pbc->textures[0] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); + GLuint tex_scr = pbc->textures[0]; + if (more_passes && !pbc->textures[1]) + pbc->textures[1] = glx_gen_texture(ps, tex_tgt, mwidth, mheight); + pbc->width = mwidth; + pbc->height = mheight; + GLuint tex_scr2 = pbc->textures[1]; +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (more_passes && !pbc->fbo) + glGenFramebuffers(1, &pbc->fbo); + const GLuint fbo = pbc->fbo; +#endif -#ifdef DEBUG_GLX - printf_dbgf("(): %d, %d, %d, %d\n", mdx, ps->root_height - mdy - mheight, mwidth, mheight); + if (!tex_scr || (more_passes && !tex_scr2)) { + printf_errf("(): Failed to allocate texture."); + goto glx_blur_dst_end; + } +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (more_passes && !fbo) { + printf_errf("(): Failed to allocate framebuffer."); + goto glx_blur_dst_end; + } #endif + // Read destination pixels into a texture + glEnable(tex_tgt); + glBindTexture(tex_tgt, tex_scr); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, mheight); + /* + if (tex_scr2) { + glBindTexture(tex_tgt, tex_scr2); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, dx - mdx); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy + height, + mwidth, mdy + mheight - dy - height); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy, dx - mdx, height); + glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, dx + width, dy, + mdx + mwidth - dx - width, height); + } */ + // Texture scaling factor GLfloat texfac_x = 1.0f, texfac_y = 1.0f; if (GL_TEXTURE_2D == tex_tgt) { @@ -904,67 +1016,130 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, } // Paint it back - // Color negation for testing... - // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - // glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); - // glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + if (more_passes) { + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + } - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); -#ifdef CONFIG_VSYNC_OPENGL_GLSL - glUseProgram(ps->glx_prog_blur); - if (ps->glx_prog_blur_unifm_offset_x >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_x, texfac_x); - if (ps->glx_prog_blur_unifm_offset_y >= 0) - glUniform1f(ps->glx_prog_blur_unifm_offset_y, texfac_y); - if (ps->glx_prog_blur_unifm_factor_center >= 0) - glUniform1f(ps->glx_prog_blur_unifm_factor_center, factor_center); + bool last_pass = false; + for (int i = 0; !last_pass; ++i) { + last_pass = !ps->glx_blur_passes[i + 1].prog; + assert(i < MAX_BLUR_PASS - 1); + const glx_blur_pass_t *ppass = &ps->glx_blur_passes[i]; + assert(ppass->prog); + + assert(tex_scr); + glBindTexture(tex_tgt, tex_scr); + +#ifdef CONFIG_VSYNC_OPENGL_FBO + if (!last_pass) { + static const GLenum DRAWBUFS[2] = { GL_COLOR_ATTACHMENT0 }; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_scr2, 0); + glDrawBuffers(1, DRAWBUFS); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) + != GL_FRAMEBUFFER_COMPLETE) { + printf_errf("(): Framebuffer attachment failed."); + goto glx_blur_dst_end; + } + } + else { + static const GLenum DRAWBUFS[2] = { GL_BACK }; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDrawBuffers(1, DRAWBUFS); + if (have_scissors) + glEnable(GL_SCISSOR_TEST); + if (have_stencil) + glEnable(GL_STENCIL_TEST); + } #endif - { - P_PAINTREG_START(); + // Color negation for testing... + // glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + // glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + // glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glUseProgram(ppass->prog); + if (ppass->unifm_offset_x >= 0) + glUniform1f(ppass->unifm_offset_x, texfac_x); + if (ppass->unifm_offset_y >= 0) + glUniform1f(ppass->unifm_offset_y, texfac_y); + if (ppass->unifm_factor_center >= 0) + glUniform1f(ppass->unifm_factor_center, factor_center); + { - const GLfloat rx = (crect.x - mdx) * texfac_x; - const GLfloat ry = (mheight - (crect.y - mdy)) * texfac_y; - const GLfloat rxe = rx + crect.width * texfac_x; - const GLfloat rye = ry - crect.height * texfac_y; - const GLfloat rdx = crect.x; - const GLfloat rdy = ps->root_height - crect.y; - const GLfloat rdxe = rdx + crect.width; - const GLfloat rdye = rdy - crect.height; + P_PAINTREG_START(); + { + const GLfloat rx = (crect.x - mdx) * texfac_x; + const GLfloat ry = (mheight - (crect.y - mdy)) * texfac_y; + const GLfloat rxe = rx + crect.width * texfac_x; + const GLfloat rye = ry - crect.height * texfac_y; + GLfloat rdx = crect.x - mdx; + GLfloat rdy = mheight - crect.y + mdy; + GLfloat rdxe = rdx + crect.width; + GLfloat rdye = rdy - crect.height; + + if (last_pass) { + rdx = crect.x; + rdy = ps->root_height - crect.y; + rdxe = rdx + crect.width; + rdye = rdy - crect.height; + } #ifdef DEBUG_GLX - printf_dbgf("(): %f, %f, %f, %f -> %d, %d, %d, %d\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); + printf_dbgf("(): %f, %f, %f, %f -> %f, %f, %f, %f\n", rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif - glTexCoord2f(rx, ry); - glVertex3f(rdx, rdy, z); + glTexCoord2f(rx, ry); + glVertex3f(rdx, rdy, z); - glTexCoord2f(rxe, ry); - glVertex3f(rdxe, rdy, z); + glTexCoord2f(rxe, ry); + glVertex3f(rdxe, rdy, z); - glTexCoord2f(rxe, rye); - glVertex3f(rdxe, rdye, z); + glTexCoord2f(rxe, rye); + glVertex3f(rdxe, rdye, z); - glTexCoord2f(rx, rye); - glVertex3f(rdx, rdye, z); + glTexCoord2f(rx, rye); + glVertex3f(rdx, rdye, z); + } + P_PAINTREG_END(); + } + + glUseProgram(0); + + // Swap tex_scr and tex_scr2 + { + GLuint tmp = tex_scr2; + tex_scr2 = tex_scr; + tex_scr = tmp; } - P_PAINTREG_END(); } -#ifdef CONFIG_VSYNC_OPENGL_GLSL - glUseProgram(0); + ret = true; + +glx_blur_dst_end: +#ifdef CONFIG_VSYNC_OPENGL_FBO + glBindFramebuffer(GL_FRAMEBUFFER, 0); #endif - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBindTexture(tex_tgt, 0); - glDeleteTextures(1, &tex_scr); glDisable(tex_tgt); + if (have_scissors) + glEnable(GL_SCISSOR_TEST); + if (have_stencil) + glEnable(GL_STENCIL_TEST); + + if (&ibc == pbc) { + free_glx_bc(ps, pbc); + } #ifdef DEBUG_GLX_ERR glx_check_err(ps); #endif - return true; + return ret; } +#endif bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, @@ -1156,7 +1331,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, } #ifdef DEBUG_GLX - printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); + printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye); #endif #define P_TEXCOORD(cx, cy) { \ @@ -1318,6 +1493,11 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { #ifdef CONFIG_VSYNC_OPENGL_GLSL GLuint glx_create_shader(GLenum shader_type, const char *shader_str) { +#ifdef DEBUG_GLX_GLSL + printf("glx_create_shader(): ===\n%s\n===\n", shader_str); + fflush(stdout); +#endif + bool success = false; GLuint shader = glCreateShader(shader_type); if (!shader) { From d800a62b878fa152c94253b2c0f60ef4f5b9b9d1 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 21 May 2013 09:18:41 +0800 Subject: [PATCH 144/178] Feature #113: Set opacity based on conditions - Add --opacity-rule, which sets opacity based on conditions, as requested by zabbal. (#113) - Add a data field for each condition. - Correct the FAQ link in README.md. Silly me. - Code clean-up. --- c2.c | 16 +++++++++--- c2.h | 2 ++ common.h | 17 ++++++++++--- compton.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ compton.h | 11 ++++++++ 5 files changed, 115 insertions(+), 7 deletions(-) diff --git a/c2.c b/c2.c index 13c194050..bd286278a 100644 --- a/c2.c +++ b/c2.c @@ -14,7 +14,8 @@ * Parse a condition string. */ c2_lptr_t * -c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) { +c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, + void *data) { if (!pattern) return NULL; @@ -41,6 +42,7 @@ c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) { " list element."); memcpy(plptr, &lptr_def, sizeof(c2_lptr_t)); plptr->ptr = result; + plptr->data = data; if (pcondlst) { plptr->next = *pcondlst; *pcondlst = plptr; @@ -1274,20 +1276,26 @@ c2_match_once(session_t *ps, win *w, const c2_ptr_t cond) { * Match a window against a condition linked list. * * @param cache a place to cache the last matched condition + * @param pdata a place to return the data * @return true if matched, false otherwise. */ bool -c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, - const c2_lptr_t **cache) { +c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, + const c2_lptr_t **cache, void **pdata) { // Check if the cached entry matches firstly - if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) + if (cache && *cache && c2_match_once(ps, w, (*cache)->ptr)) { + if (pdata) + *pdata = (*cache)->data; return true; + } // Then go through the whole linked list for (; condlst; condlst = condlst->next) { if (c2_match_once(ps, w, condlst->ptr)) { if (cache) *cache = condlst; + if (pdata) + *pdata = condlst->data; return true; } } diff --git a/c2.h b/c2.h index c794da1c0..b26a687de 100644 --- a/c2.h +++ b/c2.h @@ -156,12 +156,14 @@ const static c2_l_t leaf_def = C2_L_INIT; /// Linked list type of conditions. struct _c2_lptr { c2_ptr_t ptr; + void *data; struct _c2_lptr *next; }; /// Initializer for c2_lptr_t. #define C2_LPTR_INIT { \ .ptr = C2_PTR_INIT, \ + .data = NULL, \ .next = NULL, \ } diff --git a/common.h b/common.h index cab29f76a..0b97d5009 100644 --- a/common.h +++ b/common.h @@ -546,6 +546,8 @@ typedef struct { bool inactive_dim_fixed; /// Conditions of windows to have inverted colors. c2_lptr_t *invert_color_list; + /// Rules to change window opacity. + c2_lptr_t *opacity_rules; // === Focus related === /// Consider windows of specific types to be always focused. @@ -921,6 +923,7 @@ typedef struct _win { const c2_lptr_t *cache_fcblst; const c2_lptr_t *cache_ivclst; const c2_lptr_t *cache_bbblst; + const c2_lptr_t *cache_oparule; // Opacity-related members /// Current window opacity. @@ -933,6 +936,8 @@ typedef struct _win { /// broken window managers not transferring client window's /// _NET_WM_OPACITY value opacity_t opacity_prop_client; + /// Last window opacity value we set. + long opacity_set; // Fading-related members /// Do not fade if it's false. Change on window type change. @@ -2027,14 +2032,20 @@ opts_set_no_fading_openclose(session_t *ps, bool newval); ///@{ c2_lptr_t * -c2_parse(session_t *ps, c2_lptr_t **pcondlst, const char *pattern); +c2_parsed(session_t *ps, c2_lptr_t **pcondlst, const char *pattern, + void *data); + +#define c2_parse(ps, pcondlst, pattern) c2_parsed((ps), (pcondlst), (pattern), NULL) c2_lptr_t * c2_free_lptr(c2_lptr_t *lp); bool -c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, - const c2_lptr_t **cache); +c2_matchd(session_t *ps, win *w, const c2_lptr_t *condlst, + const c2_lptr_t **cache, void **pdata); + +#define c2_match(ps, w, condlst, cache) c2_matchd((ps), (w), (condlst), \ + (cache), NULL) #endif ///@} diff --git a/compton.c b/compton.c index f058bf927..c55d045d8 100644 --- a/compton.c +++ b/compton.c @@ -2426,6 +2426,28 @@ win_determine_blur_background(session_t *ps, win *w) { add_damage_win(ps, w); } +/** + * Update window opacity according to opacity rules. + */ +static void +win_update_opacity_rule(session_t *ps, win *w) { + // If long is 32-bit, unfortunately there's no way could we express "unset", + // so we just entirely don't distinguish "unset" and OPAQUE + long opacity = OPAQUE; + void *val = NULL; + if (c2_matchd(ps, w, ps->o.opacity_rules, &w->cache_oparule, &val)) + opacity = ((double) (long) val) / 100.0 * OPAQUE; + + if (opacity == w->opacity_set) + return; + + if (OPAQUE != opacity) + wid_set_opacity_prop(ps, w->id, opacity); + else if (OPAQUE != w->opacity_set) + wid_rm_opacity_prop(ps, w->id); + w->opacity_set = opacity; +} + /** * Function to be called on window type changes. */ @@ -2436,6 +2458,8 @@ win_on_wtype_change(session_t *ps, win *w) { win_update_focused(ps, w); if (ps->o.invert_color_list) win_determine_invert_color(ps, w); + if (ps->o.opacity_rules) + win_update_opacity_rule(ps, w); } /** @@ -2453,6 +2477,8 @@ win_on_factor_change(session_t *ps, win *w) { win_update_focused(ps, w); if (ps->o.blur_background_blacklist) win_determine_blur_background(ps, w); + if (ps->o.opacity_rules) + win_update_opacity_rule(ps, w); } /** @@ -2670,11 +2696,13 @@ add_win(session_t *ps, Window id, Window prev) { .cache_fcblst = NULL, .cache_ivclst = NULL, .cache_bbblst = NULL, + .cache_oparule = NULL, .opacity = 0, .opacity_tgt = 0, .opacity_prop = OPAQUE, .opacity_prop_client = OPAQUE, + .opacity_set = OPAQUE, .fade = false, .fade_force = UNSET, @@ -4351,6 +4379,12 @@ usage(int ret) { "--invert-color-include condition\n" " Specify a list of conditions of windows that should be painted with\n" " inverted color. Resource-hogging, and is not well tested.\n" + "--opacity-rule opacity:condition\n" + " Specify a list of opacity rules, in the format \"PERCENT:PATTERN\",\n" + " like \'50:name *= \"Firefox\"'. compton-trans is recommended over\n" + " this. Note we do not distinguish 100% and unset, and we don't make\n" + " any guarantee about possible conflicts with other programs that set\n" + " _NET_WM_WINDOW_OPACITY on frame or client windows.\n" "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" @@ -4638,6 +4672,9 @@ parse_conv_kern(session_t *ps, const char *src, const char **endptr) { return parse_matrix(ps, src, endptr); } +/** + * Parse a list of convolution kernels. + */ static bool parse_conv_kern_lst(session_t *ps, const char *src, XFixed **dest, int max) { static const struct { @@ -4682,6 +4719,37 @@ parse_conv_kern_lst(session_t *ps, const char *src, XFixed **dest, int max) { return true; } +/** + * Parse a list of opacity rules. + */ +static inline bool +parse_rule_opacity(session_t *ps, const char *src) { + // Find opacity value + char *endptr = NULL; + long val = strtol(src, &endptr, 0); + if (!endptr || endptr == src) { + printf_errf("(\"%s\"): No opacity specified?", src); + return false; + } + if (val > 100 || val < 0) { + printf_errf("(\"%s\"): Opacity %ld invalid.", src, val); + return false; + } + + // Skip over spaces + while (*endptr && isspace(*endptr)) + ++endptr; + if (':' != *endptr) { + printf_errf("(\"%s\"): Opacity terminator not found.", src); + return false; + } + ++endptr; + + // Parse pattern + // I hope 1-100 is acceptable for (void *) + return c2_parsed(ps, &ps->o.opacity_rules, endptr, (void *) val); +} + #ifdef CONFIG_LIBCONFIG /** * Get a file stream of the configuration file to read. @@ -5049,6 +5117,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "blur-kern", required_argument, NULL, 301 }, { "resize-damage", required_argument, NULL, 302 }, { "glx-use-gpushader4", no_argument, NULL, 303 }, + { "opacity-rule", required_argument, NULL, 304 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5282,6 +5351,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.resize_damage = atoi(optarg); break; P_CASEBOOL(303, glx_use_gpushader4); + case 304: + // --opacity-rule + if (!parse_rule_opacity(ps, optarg)) + exit(1); + break; default: usage(1); break; @@ -6216,6 +6290,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .inactive_dim = 0.0, .inactive_dim_fixed = false, .invert_color_list = NULL, + .opacity_rules = NULL, .wintype_focus = { false }, .use_ewmh_active_win = false, @@ -6635,6 +6710,7 @@ session_destroy(session_t *ps) { free_wincondlst(&ps->o.focus_blacklist); free_wincondlst(&ps->o.invert_color_list); free_wincondlst(&ps->o.blur_background_blacklist); + free_wincondlst(&ps->o.opacity_rules); #endif // Free tracked atom list diff --git a/compton.h b/compton.h index b3695af29..334e7372a 100644 --- a/compton.h +++ b/compton.h @@ -415,6 +415,17 @@ win_has_frame(const win *w) { || w->top_width || w->left_width || w->right_width || w->bottom_width; } +static inline void +wid_set_opacity_prop(session_t *ps, Window wid, long val) { + XChangeProperty(ps->dpy, wid, ps->atom_opacity, XA_CARDINAL, 32, + PropModeReplace, (unsigned char *) &val, 1); +} + +static inline void +wid_rm_opacity_prop(session_t *ps, Window wid) { + XDeleteProperty(ps->dpy, wid, ps->atom_opacity); +} + /** * Dump an drawable's info. */ From 0dca9aa79edbc5e2d665d9f65321adc32a595d2a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 9 Jun 2013 17:06:35 +0800 Subject: [PATCH 145/178] Feature #116: Shadow exclusion region - Add --shadow-exclude-reg, which excludes certain regions on the screen to have shadows painted in. (#116) - Adjust session initialization order. Now X root and screen info and basic X extensions are available in configuration parsing step. --- common.h | 12 +++++ compton.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++------ compton.h | 35 ++++++++++++++ 3 files changed, 167 insertions(+), 14 deletions(-) diff --git a/common.h b/common.h index 0b97d5009..76a7b34e3 100644 --- a/common.h +++ b/common.h @@ -234,6 +234,14 @@ typedef enum { UNSET } switch_t; +/// Structure representing a X geometry. +typedef struct { + int wid; + int hei; + int x; + int y; +} geometry_t; + /// Enumeration type of window painting mode. typedef enum { WMODE_TRANS, @@ -484,6 +492,8 @@ typedef struct { int shadow_offset_x, shadow_offset_y; double shadow_opacity; bool clear_shadow; + /// Geometry of a region in which shadow is not painted on. + geometry_t shadow_exclude_reg_geom; /// Shadow blacklist. A linked list of conditions. c2_lptr_t *shadow_blacklist; /// Whether bounding-shaped window should be ignored. @@ -695,6 +705,8 @@ typedef struct { unsigned char *shadow_corner; /// Pre-computed color table for a side of shadow. unsigned char *shadow_top; + /// A region in which shadow is not painted on. + XserverRegion shadow_exclude_reg; // === Software-optimization-related === /// Currently used refresh rate. diff --git a/compton.c b/compton.c index c55d045d8..74bebfb47 100644 --- a/compton.c +++ b/compton.c @@ -1670,6 +1670,16 @@ rebuild_screen_reg(session_t *ps) { ps->screen_reg = get_screen_region(ps); } +/** + * Rebuild shadow_exclude_reg. + */ +static void +rebuild_shadow_exclude_reg(session_t *ps) { + free_region(ps, &ps->shadow_exclude_reg); + XRectangle rect = geom_to_rect(ps, &ps->o.shadow_exclude_reg_geom, NULL); + ps->shadow_exclude_reg = rect_to_reg(ps, &rect); +} + static void paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t) { if (!region_real) @@ -1781,6 +1791,10 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XFixesIntersectRegion(ps->dpy, reg_paint, region, w->extents); } + if (ps->shadow_exclude_reg) + XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, + ps->shadow_exclude_reg); + // Might be worthwhile to crop the region to shadow border { XRectangle rec_shadow_border = { @@ -2888,10 +2902,12 @@ configure_win(session_t *ps, XConfigureEvent *ce) { XRenderFreePicture(ps->dpy, ps->tgt_buffer); ps->tgt_buffer = None; } + ps->root_width = ce->width; ps->root_height = ce->height; rebuild_screen_reg(ps); + rebuild_shadow_exclude_reg(ps); #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend) @@ -4385,6 +4401,11 @@ usage(int ret) { " this. Note we do not distinguish 100% and unset, and we don't make\n" " any guarantee about possible conflicts with other programs that set\n" " _NET_WM_WINDOW_OPACITY on frame or client windows.\n" + "--shadow-exclude-reg geometry\n" + " Specify a X geometry that describes the region in which shadow\n" + " should not be painted in, such as a dock window region.\n" + " Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n" + " on the bottom of the screen should not have shadows painted on.\n" "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" @@ -4719,6 +4740,83 @@ parse_conv_kern_lst(session_t *ps, const char *src, XFixed **dest, int max) { return true; } +/** + * Parse a X geometry. + */ +static inline bool +parse_geometry(session_t *ps, const char *src, geometry_t *dest) { + geometry_t geom = { .wid = -1, .hei = -1, .x = -1, .y = -1 }; + long val = 0L; + char *endptr = NULL; + +#define T_STRIPSPACE() do { \ + while (*src && isspace(*src)) ++src; \ + if (!*src) goto parse_geometry_end; \ +} while(0) + + T_STRIPSPACE(); + + // Parse width + // Must be base 10, because "0x0..." may appear + if (!('+' == *src || '-' == *src)) { + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.wid = val; + assert(geom.wid >= 0); + src = endptr; + } + T_STRIPSPACE(); + } + + // Parse height + if ('x' == *src) { + ++src; + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.hei = val; + if (geom.hei < 0) { + printf_errf("(\"%s\"): Invalid height.", src); + return false; + } + src = endptr; + } + T_STRIPSPACE(); + } + + // Parse x + if ('+' == *src || '-' == *src) { + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.x = val; + if ('-' == *src && geom.x <= 0) + geom.x -= 2; + src = endptr; + } + T_STRIPSPACE(); + } + + // Parse y + if ('+' == *src || '-' == *src) { + val = strtol(src, &endptr, 10); + if (endptr && src != endptr) { + geom.y = val; + if ('-' == *src && geom.y <= 0) + geom.y -= 2; + src = endptr; + } + T_STRIPSPACE(); + } + + if (*src) { + printf_errf("(\"%s\"): Trailing characters.", src); + return false; + } + +parse_geometry_end: + *dest = geom; + return true; +} + /** * Parse a list of opacity rules. */ @@ -5118,6 +5216,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "resize-damage", required_argument, NULL, 302 }, { "glx-use-gpushader4", no_argument, NULL, 303 }, { "opacity-rule", required_argument, NULL, 304 }, + { "shadow-exclude-reg", required_argument, NULL, 305 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5356,6 +5455,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (!parse_rule_opacity(ps, optarg)) exit(1); break; + case 305: + // --shadow-exclude-reg + if (!parse_geometry(ps, optarg, &ps->o.shadow_exclude_reg_geom)) + exit(1); + break; default: usage(1); break; @@ -5440,6 +5544,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { } memcpy(ps->o.blur_kerns[0], &convolution_blur, sizeof(convolution_blur)); } + + rebuild_shadow_exclude_reg(ps); } /** @@ -6434,9 +6540,6 @@ session_init(session_t *ps_old, int argc, char **argv) { } } - // Second pass - get_cfg(ps, argc, argv, false); - XSetErrorHandler(error); if (ps->o.synchronize) { XSynchronize(ps->dpy, 1); @@ -6448,6 +6551,17 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->vis = DefaultVisual(ps->dpy, ps->scr); ps->depth = DefaultDepth(ps->dpy, ps->scr); + // Start listening to events on root earlier to catch all possible + // root geometry changes + XSelectInput(ps->dpy, ps->root, + SubstructureNotifyMask + | ExposureMask + | StructureNotifyMask + | PropertyChangeMask); + + ps->root_width = DisplayWidth(ps->dpy, ps->scr); + ps->root_height = DisplayHeight(ps->dpy, ps->scr); + if (!XRenderQueryExtension(ps->dpy, &ps->render_event, &ps->render_error)) { fprintf(stderr, "No render extension\n"); @@ -6485,6 +6599,9 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->shape_exists = true; } + // Second pass + get_cfg(ps, argc, argv, false); + // Query X RandR if (ps->o.sw_opti && !ps->o.refresh_rate) { if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) @@ -6510,17 +6627,6 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } - // Start listening to events on root earlier to catch all possible - // root geometry changes - XSelectInput(ps->dpy, ps->root, - SubstructureNotifyMask - | ExposureMask - | StructureNotifyMask - | PropertyChangeMask); - - ps->root_width = DisplayWidth(ps->dpy, ps->scr); - ps->root_height = DisplayHeight(ps->dpy, ps->scr); - rebuild_screen_reg(ps); // Overlay must be initialized before double buffer, and before creation diff --git a/compton.h b/compton.h index 334e7372a..06d48e1b3 100644 --- a/compton.h +++ b/compton.h @@ -111,6 +111,41 @@ array_wid_exists(const Window *arr, int count, Window wid) { return false; } +/** + * Convert a geometry_t value to XRectangle. + */ +static inline XRectangle +geom_to_rect(session_t *ps, const geometry_t *src, const XRectangle *def) { + XRectangle rect_def = { .x = 0, .y = 0, + .width = ps->root_width, .height = ps->root_height }; + if (!def) def = &rect_def; + + XRectangle rect = { .x = src->x, .y = src->y, + .width = src->wid, .height = src->hei }; + if (src->wid < 0) rect.width = def->width; + if (src->hei < 0) rect.height = def->height; + if (-1 == src->x) rect.x = def->x; + else if (src->x < 0) rect.x = ps->root_width + rect.x + 2 - rect.width; + if (-1 == src->y) rect.y = def->y; + else if (src->y < 0) rect.y = ps->root_height + rect.y + 2 - rect.height; + return rect; +} + +/** + * Convert a XRectangle to a XServerRegion. + */ +static inline XserverRegion +rect_to_reg(session_t *ps, const XRectangle *src) { + if (!src) return None; + XRectangle bound = { .x = 0, .y = 0, + .width = ps->root_width, .height = ps->root_height }; + XRectangle res = { }; + rect_crop(&res, src, &bound); + if (res.width && res.height) + return XFixesCreateRegion(ps->dpy, &res, 1); + return None; +} + /** * Destroy a Picture. */ From 1096bf90d96bc18a7e09fe5f26fa0c703f1fbdf8 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 19 Jun 2013 19:36:48 +0800 Subject: [PATCH 146/178] Misc: --paint-exclude & #119 - Add --paint-exclude to prevent certain windows from being painted, for debugging purposes. - Add predefined matching target "x", "y", "width", "height", "widthb", "heightb", "border_width", and "fullscreen". - Fix bug #119, wrong man page install dir in CMake configuration. Thanks to sstewartgallus for reporting. --- c2.c | 8 ++++++++ c2.h | 16 ++++++++++++++++ common.h | 16 ++++++++++++++++ compton.c | 17 +++++++++++++++++ compton.h | 11 ----------- 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/c2.c b/c2.c index bd286278a..d2ca7be1f 100644 --- a/c2.c +++ b/c2.c @@ -1043,6 +1043,14 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, *perr = false; switch (pleaf->predef) { case C2_L_PID: tgt = wid; break; + case C2_L_PX: tgt = w->a.x; break; + case C2_L_PY: tgt = w->a.y; break; + case C2_L_PWIDTH: tgt = w->a.width; break; + case C2_L_PHEIGHT: tgt = w->a.height; break; + case C2_L_PWIDTHB: tgt = w->widthb; break; + case C2_L_PHEIGHTB: tgt = w->heightb; break; + case C2_L_PBDW: tgt = w->a.border_width; break; + case C2_L_PFULLSCREEN: tgt = win_is_fullscreen(ps, w); break; case C2_L_POVREDIR: tgt = w->a.override_redirect; break; case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break; case C2_L_PFOCUSED: tgt = w->focused_real; break; diff --git a/c2.h b/c2.h index b26a687de..e2ab38d83 100644 --- a/c2.h +++ b/c2.h @@ -99,6 +99,14 @@ struct _c2_l { enum { C2_L_PUNDEFINED, C2_L_PID, + C2_L_PX, + C2_L_PY, + C2_L_PWIDTH, + C2_L_PHEIGHT, + C2_L_PWIDTHB, + C2_L_PHEIGHTB, + C2_L_PBDW, + C2_L_PFULLSCREEN, C2_L_POVREDIR, C2_L_PARGB, C2_L_PFOCUSED, @@ -177,6 +185,14 @@ typedef struct { // Predefined targets. const static c2_predef_t C2_PREDEFS[] = { [C2_L_PID ] = { "id" , C2_L_TCARDINAL , 0 }, + [C2_L_PX ] = { "x" , C2_L_TCARDINAL , 0 }, + [C2_L_PY ] = { "y" , C2_L_TCARDINAL , 0 }, + [C2_L_PWIDTH ] = { "width" , C2_L_TCARDINAL , 0 }, + [C2_L_PHEIGHT ] = { "height" , C2_L_TCARDINAL , 0 }, + [C2_L_PWIDTHB ] = { "widthb" , C2_L_TCARDINAL , 0 }, + [C2_L_PHEIGHTB ] = { "heightb" , C2_L_TCARDINAL , 0 }, + [C2_L_PBDW ] = { "border_width" , C2_L_TCARDINAL , 0 }, + [C2_L_PFULLSCREEN ] = { "fullscreen" , C2_L_TCARDINAL , 0 }, [C2_L_POVREDIR ] = { "override_redirect" , C2_L_TCARDINAL , 0 }, [C2_L_PARGB ] = { "argb" , C2_L_TCARDINAL , 0 }, [C2_L_PFOCUSED ] = { "focused" , C2_L_TCARDINAL , 0 }, diff --git a/common.h b/common.h index 76a7b34e3..d3bc58023 100644 --- a/common.h +++ b/common.h @@ -468,6 +468,8 @@ typedef struct { int benchmark; /// Window to constantly repaint in benchmark mode. 0 for full-screen. Window benchmark_wid; + /// A list of conditions of windows not to paint. + c2_lptr_t *paint_blacklist; /// Whether to work under synchronized mode for debugging. bool synchronize; @@ -896,6 +898,8 @@ typedef struct _win { bool rounded_corners; /// Whether this window is to be painted. bool to_paint; + /// Whether the window is painting excluded. + bool paint_excluded; /// Whether this window is in open/close state. bool in_openclose; @@ -936,6 +940,7 @@ typedef struct _win { const c2_lptr_t *cache_ivclst; const c2_lptr_t *cache_bbblst; const c2_lptr_t *cache_oparule; + const c2_lptr_t *cache_pblst; // Opacity-related members /// Current window opacity. @@ -1720,6 +1725,17 @@ rect_is_fullscreen(session_t *ps, int x, int y, unsigned wid, unsigned hei) { && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height); } +/** + * Check if a window is a fullscreen window. + * + * It's not using w->border_size for performance measures. + */ +static inline bool +win_is_fullscreen(session_t *ps, const win *w) { + return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) + && !w->bounding_shaped; +} + /** * Determine if a window has a specific property. * diff --git a/compton.c b/compton.c index 74bebfb47..0f578791f 100644 --- a/compton.c +++ b/compton.c @@ -1128,6 +1128,8 @@ paint_preprocess(session_t *ps, win *list) { to_paint = false; } + to_paint = to_paint && !w->paint_excluded; + if (to_paint) { // If opacity changes if (w->opacity != opacity_old) { @@ -2493,6 +2495,9 @@ win_on_factor_change(session_t *ps, win *w) { win_determine_blur_background(ps, w); if (ps->o.opacity_rules) win_update_opacity_rule(ps, w); + if (ps->o.paint_blacklist) + w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist, + &w->cache_pblst); } /** @@ -2934,6 +2939,8 @@ configure_win(session_t *ps, XConfigureEvent *ce) { restack_win(ps, w, ce->above); } + bool factor_change = false; + // Windows restack (including window restacks happened when this // window is not mapped) could mess up all reg_ignore ps->reg_ignore_expire = true; @@ -2949,6 +2956,7 @@ configure_win(session_t *ps, XConfigureEvent *ce) { if (w->a.x != ce->x || w->a.y != ce->y || w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) { + factor_change = true; free_region(ps, &w->extents); free_region(ps, &w->border_size); } @@ -2979,6 +2987,9 @@ configure_win(session_t *ps, XConfigureEvent *ce) { XFixesDestroyRegion(ps->dpy, extents); add_damage(ps, damage); } + + if (factor_change) + win_on_factor_change(ps, w); } // override_redirect flag cannot be changed after window creation, as far @@ -5217,6 +5228,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "glx-use-gpushader4", no_argument, NULL, 303 }, { "opacity-rule", required_argument, NULL, 304 }, { "shadow-exclude-reg", required_argument, NULL, 305 }, + { "paint-exclude", required_argument, NULL, 306 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5460,6 +5472,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (!parse_geometry(ps, optarg, &ps->o.shadow_exclude_reg_geom)) exit(1); break; + case 306: + // --paint-exclude + condlst_add(ps, &ps->o.paint_blacklist, optarg); + break; default: usage(1); break; @@ -6817,6 +6833,7 @@ session_destroy(session_t *ps) { free_wincondlst(&ps->o.invert_color_list); free_wincondlst(&ps->o.blur_background_blacklist); free_wincondlst(&ps->o.opacity_rules); + free_wincondlst(&ps->o.paint_blacklist); #endif // Free tracked atom list diff --git a/compton.h b/compton.h index 06d48e1b3..c0773db99 100644 --- a/compton.h +++ b/compton.h @@ -478,17 +478,6 @@ dump_drawable(session_t *ps, Drawable drawable) { } } -/** - * Check if a window is a fullscreen window. - * - * It's not using w->border_size for performance measures. - */ -static inline bool -win_is_fullscreen(session_t *ps, const win *w) { - return rect_is_fullscreen(ps, w->a.x, w->a.y, w->widthb, w->heightb) - && !w->bounding_shaped; -} - static void win_rounded_corners(session_t *ps, win *w); From 291fba3b6dc0e24ca7e5e17bbca5e7fa5ff51ca0 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 25 Jun 2013 23:06:08 +0800 Subject: [PATCH 147/178] Misc: OpenGL 3.0 compatibility & misc - Fix OpenGL 3.0 compatibility by changing glFramebufferTexture() to glFramebufferTexture2D(). Thanks to danfe for suggestion! - Add notification about DocBook toolchina dependencies. Thanks to danfe. (#121) --- opengl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opengl.c b/opengl.c index 635cd33ef..c9be5beec 100644 --- a/opengl.c +++ b/opengl.c @@ -1035,7 +1035,8 @@ glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, if (!last_pass) { static const GLenum DRAWBUFS[2] = { GL_COLOR_ATTACHMENT0 }; glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_scr2, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, tex_scr2, 0); glDrawBuffers(1, DRAWBUFS); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { From bd40b36f01e2f114b0647ec7365550fb9f610326 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 5 Jul 2013 23:21:05 +0800 Subject: [PATCH 148/178] Bug fix #124: GLX: Missing check on FBConfig X visual depth - Check FBConfig X visual depth, like Compiz, to fix issues with nvidia-drivers-325.08 . Thanks to guotsuan for reporting. --- opengl.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/opengl.c b/opengl.c index c9be5beec..a788cd509 100644 --- a/opengl.c +++ b/opengl.c @@ -383,18 +383,31 @@ glx_update_fbconfig(session_t *ps) { .texture_tgts = 0, .y_inverted = false, }; + int id = (int) (pcur - pfbcfgs); int depth = 0, depth_alpha = 0, val = 0; if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth) || Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) { - printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", (int) (pcur - pfbcfgs)); + printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", id); continue; } if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) { - printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", (int) (pcur - pfbcfgs)); + printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", id); continue; } + int visualdepth = 0; + { + XVisualInfo *pvi = glXGetVisualFromFBConfig(ps->dpy, *pcur); + if (!pvi) { + // On nvidia-drivers-325.08 this happens slightly too often... + // printf_errf("(): Failed to retrieve X Visual of FBConfig %d.", id); + continue; + } + visualdepth = pvi->depth; + cxfree(pvi); + } + bool rgb = false; bool rgba = false; @@ -407,12 +420,15 @@ glx_update_fbconfig(session_t *ps) { if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val)) fbinfo.y_inverted = val; - if ((depth - depth_alpha) < 32 && rgb) { - fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT; - glx_update_fbconfig_bydepth(ps, depth - depth_alpha, &fbinfo); + { + int tgtdpt = depth - depth_alpha; + if (tgtdpt == visualdepth && tgtdpt < 32 && rgb) { + fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT; + glx_update_fbconfig_bydepth(ps, tgtdpt, &fbinfo); + } } - if (rgba) { + if (depth == visualdepth && rgba) { fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT; glx_update_fbconfig_bydepth(ps, depth, &fbinfo); } From 4f92672534b2ebb83616de98b40fb79761c0c2ac Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Fri, 26 Jul 2013 12:52:16 +0800 Subject: [PATCH 149/178] Improvement #41: Enable/disable redirection through D-Bus - Add "redirected_force" to D-Bus opts_get to forcefully redirect/unredirect windows. - Add D-Bus method "repaint", to, namely, repaint the screen. --- common.h | 5 ++--- compton.c | 17 +++++++++-------- dbus.c | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/common.h b/common.h index d3bc58023..e439af4a7 100644 --- a/common.h +++ b/common.h @@ -460,6 +460,8 @@ typedef struct { /// Whether to unredirect all windows if a full-screen opaque window /// is detected. bool unredir_if_possible; + /// Forced redirection setting through D-Bus. + switch_t redirected_force; /// Whether to enable D-Bus support. bool dbus; /// Path to log file. @@ -647,9 +649,6 @@ typedef struct { XserverRegion all_damage_last[CGLX_MAX_BUFFER_AGE]; /// Whether all windows are currently redirected. bool redirected; - /// Whether there's a highest full-screen window, and all windows could - /// be unredirected. - bool unredir_possible; /// Pre-generated alpha pictures. Picture *alpha_picts; /// Whether all reg_ignore of windows should expire in this paint. diff --git a/compton.c b/compton.c index 0f578791f..4cbeae7fa 100644 --- a/compton.c +++ b/compton.c @@ -1076,7 +1076,7 @@ get_alpha_pict_o(session_t *ps, opacity_t o) { static win * paint_preprocess(session_t *ps, win *list) { // Initialize unredir_possible - ps->unredir_possible = false; + bool unredir_possible = false; win *w; win *t = NULL, *next = NULL; @@ -1229,13 +1229,13 @@ paint_preprocess(session_t *ps, win *list) { last_reg_ignore = w->reg_ignore; - if (is_highest && to_paint) { + if (ps->o.unredir_if_possible && is_highest && to_paint) { is_highest = false; // Disable unredirection for multi-screen setups if (WMODE_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) && win_is_fullscreen(ps, w)) - ps->unredir_possible = true; + unredir_possible = true; } // Reset flags @@ -1259,12 +1259,13 @@ paint_preprocess(session_t *ps, win *list) { } // If possible, unredirect all windows and stop painting - if (ps->o.unredir_if_possible && ps->unredir_possible) { + if (UNSET != ps->o.redirected_force) + unredir_possible = !ps->o.redirected_force; + + if (unredir_possible) redir_stop(ps); - } - else { + else redir_start(ps); - } return t; } @@ -6365,6 +6366,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .paint_on_overlay = false, .resize_damage = 0, .unredir_if_possible = false, + .redirected_force = UNSET, .dbus = false, .benchmark = 0, .benchmark_wid = None, @@ -6435,7 +6437,6 @@ session_init(session_t *ps_old, int argc, char **argv) { .all_damage_last = { None }, .time_start = { 0, 0 }, .redirected = false, - .unredir_possible = false, .alpha_picts = NULL, .reg_ignore_expire = false, .idling = false, diff --git a/dbus.c b/dbus.c index f2b019aa3..2d9a86bd2 100644 --- a/dbus.c +++ b/dbus.c @@ -582,6 +582,12 @@ cdbus_process(session_t *ps, DBusMessage *msg) { cdbus_reply_bool(ps, msg, true); success = true; } + else if (cdbus_m_ismethod("repaint")) { + force_repaint(ps); + if (!dbus_message_get_no_reply(msg)) + cdbus_reply_bool(ps, msg, true); + success = true; + } else if (cdbus_m_ismethod("list_win")) { success = cdbus_process_list_win(ps, msg); } @@ -892,6 +898,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); + cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum); cdbus_m_opts_get_do(logpath, cdbus_reply_string); cdbus_m_opts_get_do(synchronize, cdbus_reply_bool); @@ -1064,6 +1071,16 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { return true; } + // redirected_force + if (!strcmp("redirected_force", target)) { + cdbus_enum_t val = UNSET; + if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_ENUM, &val)) + return false; + ps->o.redirected_force = val; + force_repaint(ps); + goto cdbus_process_opts_set_success; + } + #undef cdbus_m_opts_set_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); @@ -1117,6 +1134,7 @@ cdbus_process_introspect(session_t *ps, DBusMessage *msg) { " \n" " \n" " \n" + " \n" " \n" "\n"; From 8f6c2d89ea6883d5cee95cfeb83618c5c11cbf12 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 30 Jul 2013 22:24:11 +0800 Subject: [PATCH 150/178] Misc: stoppaint_force & Documentation update - Add stoppaint_force option, controlled via D-Bus, to stop painting completely, which may look better than unredirecting the screen, sometimes. (#41) - Add x2, y2 matching targets. - Update documentation. --- c2.c | 2 ++ c2.h | 4 ++++ common.h | 2 ++ compton.c | 37 ++++++++++++++++++++++++++++--------- dbus.c | 4 ++++ 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/c2.c b/c2.c index d2ca7be1f..e89638b21 100644 --- a/c2.c +++ b/c2.c @@ -1045,6 +1045,8 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, case C2_L_PID: tgt = wid; break; case C2_L_PX: tgt = w->a.x; break; case C2_L_PY: tgt = w->a.y; break; + case C2_L_PX2: tgt = w->a.x + w->widthb; break; + case C2_L_PY2: tgt = w->a.y + w->heightb; break; case C2_L_PWIDTH: tgt = w->a.width; break; case C2_L_PHEIGHT: tgt = w->a.height; break; case C2_L_PWIDTHB: tgt = w->widthb; break; diff --git a/c2.h b/c2.h index e2ab38d83..3c56e2fa7 100644 --- a/c2.h +++ b/c2.h @@ -101,6 +101,8 @@ struct _c2_l { C2_L_PID, C2_L_PX, C2_L_PY, + C2_L_PX2, + C2_L_PY2, C2_L_PWIDTH, C2_L_PHEIGHT, C2_L_PWIDTHB, @@ -187,6 +189,8 @@ const static c2_predef_t C2_PREDEFS[] = { [C2_L_PID ] = { "id" , C2_L_TCARDINAL , 0 }, [C2_L_PX ] = { "x" , C2_L_TCARDINAL , 0 }, [C2_L_PY ] = { "y" , C2_L_TCARDINAL , 0 }, + [C2_L_PX2 ] = { "x2" , C2_L_TCARDINAL , 0 }, + [C2_L_PY2 ] = { "y2" , C2_L_TCARDINAL , 0 }, [C2_L_PWIDTH ] = { "width" , C2_L_TCARDINAL , 0 }, [C2_L_PHEIGHT ] = { "height" , C2_L_TCARDINAL , 0 }, [C2_L_PWIDTHB ] = { "widthb" , C2_L_TCARDINAL , 0 }, diff --git a/common.h b/common.h index e439af4a7..bf0b4800a 100644 --- a/common.h +++ b/common.h @@ -462,6 +462,8 @@ typedef struct { bool unredir_if_possible; /// Forced redirection setting through D-Bus. switch_t redirected_force; + /// Whether to stop painting. Controlled through D-Bus. + switch_t stoppaint_force; /// Whether to enable D-Bus support. bool dbus; /// Path to log file. diff --git a/compton.c b/compton.c index 4cbeae7fa..474d2a277 100644 --- a/compton.c +++ b/compton.c @@ -4333,13 +4333,16 @@ usage(int ret) { " Only work on some drivers. Works only with GLX backend.\n" " Does not actually control paint timing, only buffer swap is\n" " affected, so it doesn't have the effect of --sw-opti unlike\n" - " other methods. Experimental." WARNING "\n" + " other methods." WARNING "\n" " opengl-mswc = Try to VSync with MESA_swap_control OpenGL\n" " extension. Basically the same as opengl-swc above, except the\n" " extension we use." WARNING "\n" + "--vsync-aggressive\n" + " Attempt to send painting request before VBlank and do XFlush()\n" + " during VBlank. This switch may be lifted out at any moment.\n" "--alpha-step val\n" - " Step for pregenerating alpha pictures. 0.01 - 1.0. Defaults to\n" - " 0.03.\n" + " X Render backend: Step for pregenerating alpha pictures. \n" + " 0.01 - 1.0. Defaults to 0.03.\n" "--dbe\n" " Enable DBE painting mode, intended to use with VSync to\n" " (hopefully) eliminate tearing.\n" @@ -4347,10 +4350,7 @@ usage(int ret) { " Painting on X Composite overlay window.\n" "--sw-opti\n" " Limit compton to repaint at most once every 1 / refresh_rate\n" - " second to boost performance. Experimental.\n" - "--vsync-aggressive\n" - " Attempt to send painting request before VBlank and do XFlush()\n" - " during VBlank. This switch may be lifted out at any moment.\n" + " second to boost performance.\n" "--use-ewmh-active-win\n" " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n" " window is focused instead of using FocusIn/Out events.\n" @@ -4360,7 +4360,6 @@ usage(int ret) { "--unredir-if-possible\n" " Unredirect all windows if a full-screen opaque window is\n" " detected, to maximize performance for full-screen windows.\n" - " Experimental.\n" "--focus-exclude condition\n" " Specify a list of conditions of windows that should always be\n" " considered focused.\n" @@ -4976,6 +4975,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { config_t cfg; int ival = 0; double dval = 0.0; + // libconfig manages string memory itself, so no need to manually free + // anything const char *sval = NULL; f = open_config_file(ps->o.config_file, &path); @@ -5068,6 +5069,10 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { config_lookup_float(&cfg, "shadow-green", &ps->o.shadow_green); // --shadow-blue config_lookup_float(&cfg, "shadow-blue", &ps->o.shadow_blue); + // --shadow-exclude-reg + if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval) + && !parse_geometry(ps, sval, &ps->o.shadow_exclude_reg_geom)) + exit(1); // --inactive-opacity-override lcfg_lookup_bool(&cfg, "inactive-opacity-override", &ps->o.inactive_opacity_override); @@ -5126,6 +5131,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { parse_cfg_condlst(ps, &cfg, &ps->o.invert_color_list, "invert-color-include"); // --blur-background-exclude parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude"); + // --opacity-rule + parse_cfg_condlst(ps, &cfg, &ps->o.opacity_rules, "opacity-rule"); // --blur-background lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background); // --blur-background-frame @@ -5134,6 +5141,12 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --blur-background-fixed lcfg_lookup_bool(&cfg, "blur-background-fixed", &ps->o.blur_background_fixed); + // --blur-kern + if (config_lookup_string(&cfg, "blur-kern", &sval) + && !parse_conv_kern_lst(ps, sval, ps->o.blur_kerns, MAX_BLUR_PASS)) + exit(1); + // --resize-damage + config_lookup_int(&cfg, "resize-damage", &ps->o.resize_damage); // --glx-no-stencil lcfg_lookup_bool(&cfg, "glx-no-stencil", &ps->o.glx_no_stencil); // --glx-copy-from-front @@ -5146,6 +5159,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { if (config_lookup_string(&cfg, "glx-swap-method", &sval) && !parse_glx_swap_method(ps, sval)) exit(1); + // --glx-use-gpushader4 + lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &ps->o.glx_use_gpushader4); // Wintype settings { wintype_t i; @@ -5563,6 +5578,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { } rebuild_shadow_exclude_reg(ps); + + if (ps->o.resize_damage < 0) + printf_errf("(): Negative --resize-damage does not work correctly."); } /** @@ -6367,6 +6385,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .resize_damage = 0, .unredir_if_possible = false, .redirected_force = UNSET, + .stoppaint_force = UNSET, .dbus = false, .benchmark = 0, .benchmark_wid = None, @@ -6995,7 +7014,7 @@ session_run(session_t *ps) { t = paint_preprocess(ps, ps->list); // If the screen is unredirected, free all_damage to stop painting - if (!ps->redirected) + if (!ps->redirected || ON == ps->o.stoppaint_force) free_region(ps, &ps->all_damage); XserverRegion all_damage_orig = None; diff --git a/dbus.c b/dbus.c index 2d9a86bd2..671ef8eef 100644 --- a/dbus.c +++ b/dbus.c @@ -899,6 +899,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum); + cdbus_m_opts_get_do(stoppaint_force, cdbus_reply_enum); cdbus_m_opts_get_do(logpath, cdbus_reply_string); cdbus_m_opts_get_do(synchronize, cdbus_reply_bool); @@ -1081,6 +1082,9 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg) { goto cdbus_process_opts_set_success; } + // stoppaint_force + cdbus_m_opts_set_do(stoppaint_force, CDBUS_TYPE_ENUM, cdbus_enum_t); + #undef cdbus_m_opts_set_do printf_errf("(): " CDBUS_ERROR_BADTGT_S, target); From cd62d55a5b62c97cff3e60ebc0b3a66b8c7fa891 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 22 Aug 2013 21:15:04 +0800 Subject: [PATCH 151/178] Improvement #137: --xinerama-shadow-crop Add --xinerama-shadow-crop to crop shadow to current Xinerama screen. Thanks to Feltzer for suggestion. --- common.h | 23 +++++++++++++++++- compton.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- compton.h | 42 +++++++++++++++++++++++++++++++++ dbus.c | 1 + 4 files changed, 134 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index bf0b4800a..f90cba714 100644 --- a/common.h +++ b/common.h @@ -85,6 +85,10 @@ #include #include +#ifdef CONFIG_XINERAMA +#include +#endif + // Workarounds for missing definitions in very old versions of X headers, // thanks to consolers for reporting #ifndef PictOpDifference @@ -506,6 +510,8 @@ typedef struct { bool shadow_ignore_shaped; /// Whether to respect _COMPTON_SHADOW. bool respect_prop_shadow; + /// Whether to crop shadow to the very Xinerama screen. + bool xinerama_shadow_crop; // === Fading === /// Enable/disable fading for specific window types. @@ -806,6 +812,16 @@ typedef struct { #endif /// Whether X DBE extension exists. bool dbe_exists; +#ifdef CONFIG_XINERAMA + /// Whether X Xinerama extension exists. + bool xinerama_exists; + /// Xinerama screen info. + XineramaScreenInfo *xinerama_scrs; + /// Xinerama screen regions. + XserverRegion *xinerama_scr_regs; + /// Number of Xinerama screens. + int xinerama_nscrs; +#endif /// Whether X Render convolution filter exists. bool xrfilter_convolution_exists; @@ -861,6 +877,10 @@ typedef struct _win { Window id; /// Window attributes. XWindowAttributes a; +#ifdef CONFIG_XINERAMA + /// Xinerama screen this window is on. + int xinerama_scr; +#endif /// Window visual pict format; XRenderPictFormat *pictfmt; /// Window painting mode. @@ -1107,12 +1127,13 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg, /** * @brief Quit if the passed-in pointer is empty. */ -static inline void +static inline void * allocchk_(const char *func_name, void *ptr) { if (!ptr) { printf_err("%s(): Failed to allocate memory.", func_name); exit(1); } + return ptr; } /// @brief Wrapper of allocchk_(). diff --git a/compton.c b/compton.c index 474d2a277..6fe927986 100644 --- a/compton.c +++ b/compton.c @@ -1817,6 +1817,12 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t if (ps->o.clear_shadow && w->border_size) XFixesSubtractRegion(ps->dpy, reg_paint, reg_paint, w->border_size); +#ifdef CONFIG_XINERAMA + if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0) + XFixesIntersectRegion(ps->dpy, reg_paint, reg_paint, + ps->xinerama_scr_regs[w->xinerama_scr]); +#endif + // Detect if the region is empty before painting { reg_data_t cache_reg = REG_DATA_INIT; @@ -2035,6 +2041,8 @@ map_win(session_t *ps, Window id) { w->a.map_state = IsViewable; + cxinerama_win_upd_scr(ps, w); + // Set focused to false bool focused_real = false; if (ps->o.track_focus && ps->o.use_ewmh_active_win @@ -2677,6 +2685,9 @@ add_win(session_t *ps, Window id, Window prev) { .id = None, .a = { }, +#ifdef CONFIG_XINERAMA + .xinerama_scr = -1, +#endif .pictfmt = NULL, .mode = WMODE_TRANS, .damaged = false, @@ -2989,8 +3000,10 @@ configure_win(session_t *ps, XConfigureEvent *ce) { add_damage(ps, damage); } - if (factor_change) + if (factor_change) { + cxinerama_win_upd_scr(ps, w); win_on_factor_change(ps, w); + } } // override_redirect flag cannot be changed after window creation, as far @@ -4417,6 +4430,15 @@ usage(int ret) { " should not be painted in, such as a dock window region.\n" " Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n" " on the bottom of the screen should not have shadows painted on.\n" +#undef WARNING +#ifndef CONFIG_XINERAMA +#define WARNING WARNING_DISABLED +#else +#define WARNING +#endif + "--xinerama-shadow-crop\n" + " Crop shadow of a window fully on a particular Xinerama screen to the\n" + " screen." WARNING "\n" "--backend backend\n" " Choose backend. Possible choices are xrender and glx" WARNING ".\n" "--glx-no-stencil\n" @@ -5245,6 +5267,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "opacity-rule", required_argument, NULL, 304 }, { "shadow-exclude-reg", required_argument, NULL, 305 }, { "paint-exclude", required_argument, NULL, 306 }, + { "xinerama-shadow-crop", no_argument, NULL, 307 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5492,6 +5515,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --paint-exclude condlst_add(ps, &ps->o.paint_blacklist, optarg); break; + P_CASEBOOL(307, xinerama_shadow_crop); default: usage(1); break; @@ -6343,6 +6367,35 @@ mainloop(session_t *ps) { return true; } +static void +cxinerama_upd_scrs(session_t *ps) { +#ifdef CONFIG_XINERAMA + free_xinerama_info(ps); + + if (!ps->o.xinerama_shadow_crop || !ps->xinerama_exists) return; + + if (!XineramaIsActive(ps->dpy)) return; + + ps->xinerama_scrs = XineramaQueryScreens(ps->dpy, &ps->xinerama_nscrs); + + // Just in case the shit hits the fan... + if (!ps->xinerama_nscrs) { + cxfree(ps->xinerama_scrs); + ps->xinerama_scrs = NULL; + return; + } + + ps->xinerama_scr_regs = allocchk(malloc(sizeof(XserverRegion *) + * ps->xinerama_nscrs)); + for (int i = 0; i < ps->xinerama_nscrs; ++i) { + const XineramaScreenInfo * const s = &ps->xinerama_scrs[i]; + XRectangle r = { .x = s->x_org, .y = s->y_org, + .width = s->width, .height = s->height }; + ps->xinerama_scr_regs[i] = XFixesCreateRegion(ps->dpy, &r, 1); + } +#endif +} + /** * Initialize a session. * @@ -6409,6 +6462,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .shadow_blacklist = NULL, .shadow_ignore_shaped = false, .respect_prop_shadow = false, + .xinerama_shadow_crop = false, .wintype_fade = { false }, .fade_in_step = 0.028 * OPAQUE, @@ -6663,6 +6717,17 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->o.dbe = false; } + // Query X Xinerama extension + if (ps->o.xinerama_shadow_crop) { +#ifdef CONFIG_XINERAMA + int xinerama_event = 0, xinerama_error = 0; + if (XineramaQueryExtension(ps->dpy, &xinerama_event, &xinerama_error)) + ps->xinerama_exists = true; +#else + printf_errf("(): Xinerama support not compiled in."); +#endif + } + rebuild_screen_reg(ps); // Overlay must be initialized before double buffer, and before creation @@ -6697,6 +6762,8 @@ session_init(session_t *ps_old, int argc, char **argv) { if (!vsync_init(ps)) exit(1); + cxinerama_upd_scrs(ps); + // Create registration window if (!ps->reg_win && !register_cm(ps)) exit(1); @@ -6923,6 +6990,7 @@ session_destroy(session_t *ps) { free(ps->pfds_read); free(ps->pfds_write); free(ps->pfds_except); + free_xinerama_info(ps); #ifdef CONFIG_VSYNC_OPENGL glx_destroy(ps); diff --git a/compton.h b/compton.h index c0773db99..f4a7ef9a2 100644 --- a/compton.h +++ b/compton.h @@ -192,6 +192,23 @@ free_wincondlst(c2_lptr_t **pcondlst) { #endif } +/** + * Free Xinerama screen info. + */ +static inline void +free_xinerama_info(session_t *ps) { +#ifdef CONFIG_XINERAMA + if (ps->xinerama_scr_regs) { + for (int i = 0; i < ps->xinerama_nscrs; ++i) + free_region(ps, &ps->xinerama_scr_regs[i]); + free(ps->xinerama_scr_regs); + } + cxfree(ps->xinerama_scrs); + ps->xinerama_scrs = NULL; + ps->xinerama_nscrs = 0; +#endif +} + /** * Check whether a paint_t contains enough data. */ @@ -1191,6 +1208,31 @@ timeout_clear(session_t *ps); static bool mainloop(session_t *ps); +#ifdef CONFIG_XINERAMA +static void +cxinerama_upd_scrs(session_t *ps); +#endif + +/** + * Get the Xinerama screen a window is on. + * + * Return an index >= 0, or -1 if not found. + */ +static inline void +cxinerama_win_upd_scr(session_t *ps, win *w) { +#ifdef CONFIG_XINERAMA + w->xinerama_scr = -1; + for (XineramaScreenInfo *s = ps->xinerama_scrs; + s < ps->xinerama_scrs + ps->xinerama_nscrs; ++s) + if (s->x_org <= w->a.x && s->y_org <= w->a.y + && s->x_org + s->width >= w->a.x + w->widthb + && s->y_org + s->height >= w->a.y + w->heightb) { + w->xinerama_scr = s - ps->xinerama_scrs; + return; + } +#endif +} + static session_t * session_init(session_t *ps_old, int argc, char **argv); diff --git a/dbus.c b/dbus.c index 671ef8eef..8f53eeefa 100644 --- a/dbus.c +++ b/dbus.c @@ -926,6 +926,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(shadow_offset_y, cdbus_reply_int32); cdbus_m_opts_get_do(shadow_opacity, cdbus_reply_double); cdbus_m_opts_get_do(clear_shadow, cdbus_reply_bool); + cdbus_m_opts_get_do(xinerama_shadow_crop, cdbus_reply_bool); cdbus_m_opts_get_do(fade_delta, cdbus_reply_int32); cdbus_m_opts_get_do(fade_in_step, cdbus_reply_int32); From 731ed301d424036e79234069b7e491484043c73c Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 26 Aug 2013 22:00:53 +0800 Subject: [PATCH 152/178] Misc: Add missing configuration file options and switches - Add "xinerama_shadow_crop" configuration option. - Add long commandline switches corresponding to the short ones. - Documentation update. --- compton.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compton.c b/compton.c index 6fe927986..e2b714e30 100644 --- a/compton.c +++ b/compton.c @@ -5111,6 +5111,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --detect-rounded-corners lcfg_lookup_bool(&cfg, "detect-rounded-corners", &ps->o.detect_rounded_corners); + // --xinerama-shadow-crop + lcfg_lookup_bool(&cfg, "xinerama-shadow-crop", + &ps->o.xinerama_shadow_crop); // --detect-client-opacity lcfg_lookup_bool(&cfg, "detect-client-opacity", &ps->o.detect_client_opacity); @@ -5217,6 +5220,20 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { const static struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "config", required_argument, NULL, 256 }, + { "shadow-radius", required_argument, NULL, 'r' }, + { "shadow-opacity", required_argument, NULL, 'o' }, + { "shadow-offset-x", required_argument, NULL, 'l' }, + { "shadow-offset-y", required_argument, NULL, 't' }, + { "fade-in-step", required_argument, NULL, 'I' }, + { "fade-out-step", required_argument, NULL, 'O' }, + { "menu-opacity", required_argument, NULL, 'm' }, + { "shadow", no_argument, NULL, 'c' }, + { "no-dock-shadow", no_argument, NULL, 'C' }, + { "clear-shadow", no_argument, NULL, 'z' }, + { "fading", no_argument, NULL, 'f' }, + { "inactive-opacity", required_argument, NULL, 'i' }, + { "frame-opacity", required_argument, NULL, 'e' }, + { "no-dnd-shadow", no_argument, NULL, 'G' }, { "shadow-red", required_argument, NULL, 257 }, { "shadow-green", required_argument, NULL, 258 }, { "shadow-blue", required_argument, NULL, 259 }, From 17c318b44a4534262499eca771ea878182769a13 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 28 Aug 2013 21:47:16 +0800 Subject: [PATCH 153/178] Bug fix #137: Auto-detect screen changes Attempt to auto-detect screen changes to address the issue reported by Feltzer. --- compton.c | 24 ++++++++++++++---------- compton.h | 3 +++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/compton.c b/compton.c index e2b714e30..ae39e2417 100644 --- a/compton.c +++ b/compton.c @@ -4114,12 +4114,15 @@ ev_shape_notify(session_t *ps, XShapeEvent *ev) { static void ev_screen_change_notify(session_t *ps, XRRScreenChangeNotifyEvent __attribute__((unused)) *ev) { - if (!ps->o.refresh_rate) { + if (ps->o.xinerama_shadow_crop) + cxinerama_upd_scrs(ps); + + if (ps->o.sw_opti && !ps->o.refresh_rate) { update_refresh_rate(ps); if (!ps->refresh_rate) { fprintf(stderr, "ev_screen_change_notify(): Refresh rate detection " - "failed, software VSync disabled."); - ps->o.vsync = VSYNC_NONE; + "failed, --sw-opti disabled."); + ps->o.sw_opti = false; } } } @@ -5714,11 +5717,6 @@ swopti_init(session_t *ps) { if (!ps->refresh_rate) return false; - // Monitor screen changes only if vsync_sw is enabled and we are using - // an auto-detected refresh rate - if (ps->randr_exists && !ps->o.refresh_rate) - XRRSelectInput(ps->dpy, ps->root, RRScreenChangeNotify); - return true; } @@ -6710,11 +6708,11 @@ session_init(session_t *ps_old, int argc, char **argv) { get_cfg(ps, argc, argv, false); // Query X RandR - if (ps->o.sw_opti && !ps->o.refresh_rate) { + if ((ps->o.sw_opti && !ps->o.refresh_rate) || ps->o.xinerama_shadow_crop) { if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) ps->randr_exists = true; else - printf_errf("(): No XRandR extension, automatic refresh rate " + printf_errf("(): No XRandR extension, automatic screen change " "detection impossible."); } @@ -6775,6 +6773,12 @@ session_init(session_t *ps_old, int argc, char **argv) { if (ps->o.sw_opti) ps->o.sw_opti = swopti_init(ps); + // Monitor screen changes if vsync_sw is enabled and we are using + // an auto-detected refresh rate, or when Xinerama features are enabled + if (ps->randr_exists && ((ps->o.sw_opti && !ps->o.refresh_rate) + || ps->o.xinerama_shadow_crop)) + XRRSelectInput(ps->dpy, ps->root, RRScreenChangeNotifyMask); + // Initialize VSync if (!vsync_init(ps)) exit(1); diff --git a/compton.h b/compton.h index f4a7ef9a2..c66739dd4 100644 --- a/compton.h +++ b/compton.h @@ -1233,6 +1233,9 @@ cxinerama_win_upd_scr(session_t *ps, win *w) { #endif } +static void +cxinerama_upd_scrs(session_t *ps); + static session_t * session_init(session_t *ps_old, int argc, char **argv); From fc117ad4f8ec65344a58308a86b48965e574004f Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 3 Sep 2013 21:14:40 +0800 Subject: [PATCH 154/178] Misc: GLX: Avoid using multi-sampling visuals Avoid using multi-sampled visuals, like the cogl patch: http://people.freedesktop.org/~glisse/0001-glx-do-not-use-multisample-visual-config-for-front-o.patch --- opengl.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/opengl.c b/opengl.c index a788cd509..a26a286f4 100644 --- a/opengl.c +++ b/opengl.c @@ -386,6 +386,14 @@ glx_update_fbconfig(session_t *ps) { int id = (int) (pcur - pfbcfgs); int depth = 0, depth_alpha = 0, val = 0; + // Skip over multi-sampled visuals + // http://people.freedesktop.org/~glisse/0001-glx-do-not-use-multisample-visual-config-for-front-o.patch +#ifdef GLX_SAMPLES + if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_SAMPLES, &val) + && val > 1) + continue; +#endif + if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth) || Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) { printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", id); @@ -405,7 +413,7 @@ glx_update_fbconfig(session_t *ps) { continue; } visualdepth = pvi->depth; - cxfree(pvi); + cxfree(pvi); } bool rgb = false; @@ -446,6 +454,12 @@ glx_update_fbconfig(session_t *ps) { printf_errf("(): No FBConfig found for depth 32. Expect crazy things."); } +#ifdef DEBUG_GLX + printf_dbgf("(): %d-bit: %#3x, 32-bit: %#3x\n", + ps->depth, (int) ps->glx_fbconfigs[ps->depth]->cfg, + (int) ps->glx_fbconfigs[32]->cfg); +#endif + return true; } From d41c7507250be2349080fcea44b5f33f84dd65aa Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 4 Sep 2013 22:00:51 +0800 Subject: [PATCH 155/178] Improvement: --unredir-if-possible-exclude & --unredir-if-possible-delay - Add --unredir-if-possible-exclude, to exclude certain windows when evaluating --unredir-if-possible. (#140) - Add --unredir-if-possible-delay, to add some delay before unredirecting screen. (#138, #140) - Code clean-up. --- common.h | 15 +++++ compton.c | 189 +++++++++++++++++++++++++++++++++++++----------------- compton.h | 11 ++++ dbus.c | 1 + 4 files changed, 158 insertions(+), 58 deletions(-) diff --git a/common.h b/common.h index f90cba714..ea95235ae 100644 --- a/common.h +++ b/common.h @@ -464,6 +464,11 @@ typedef struct { /// Whether to unredirect all windows if a full-screen opaque window /// is detected. bool unredir_if_possible; + /// List of conditions of windows to ignore as a full-screen window + /// when determining if a window could be unredirected. + c2_lptr_t *unredir_if_possible_blacklist; + /// Delay before unredirecting screen. + time_ms_t unredir_if_possible_delay; /// Forced redirection setting through D-Bus. switch_t redirected_force; /// Whether to stop painting. Controlled through D-Bus. @@ -644,6 +649,10 @@ typedef struct { int nfds_max; /// Linked list of all timeouts. struct _timeout_t *tmout_lst; + /// Timeout for delayed unredirection. + struct _timeout_t *tmout_unredir; + /// Whether we have hit unredirection timeout. + bool tmout_unredir_hit; /// Whether we have received an event in this cycle. bool ev_received; /// Whether the program is idling. I.e. no fading, no potential window @@ -921,6 +930,8 @@ typedef struct _win { bool to_paint; /// Whether the window is painting excluded. bool paint_excluded; + /// Whether the window is unredirect-if-possible excluded. + bool unredir_if_possible_excluded; /// Whether this window is in open/close state. bool in_openclose; @@ -962,6 +973,7 @@ typedef struct _win { const c2_lptr_t *cache_bbblst; const c2_lptr_t *cache_oparule; const c2_lptr_t *cache_pblst; + const c2_lptr_t *cache_uipblst; // Opacity-related members /// Current window opacity. @@ -1527,6 +1539,9 @@ timeout_invoke(session_t *ps, timeout_t *ptmout); bool timeout_drop(session_t *ps, timeout_t *prm); +void +timeout_reset(session_t *ps, timeout_t *ptmout); + /** * Add a file descriptor to a select() fd_set. */ diff --git a/compton.c b/compton.c index ae39e2417..b8f1fc6d5 100644 --- a/compton.c +++ b/compton.c @@ -1075,13 +1075,7 @@ get_alpha_pict_o(session_t *ps, opacity_t o) { static win * paint_preprocess(session_t *ps, win *list) { - // Initialize unredir_possible - bool unredir_possible = false; - - win *w; win *t = NULL, *next = NULL; - // Trace whether it's the highest window to paint - bool is_highest = true; // Fading step calculation time_ms_t steps = 0L; @@ -1097,7 +1091,10 @@ paint_preprocess(session_t *ps, win *list) { XserverRegion last_reg_ignore = None; - for (w = list; w; w = next) { + bool unredir_possible = false; + // Trace whether it's the highest window to paint + bool is_highest = true; + for (win *w = list; w; w = next) { bool to_paint = true; const winmode_t mode_old = w->mode; @@ -1229,12 +1226,17 @@ paint_preprocess(session_t *ps, win *list) { last_reg_ignore = w->reg_ignore; + // (Un)redirect screen + // We could definitely unredirect the screen when there's no window to + // paint, but this is typically unnecessary, may cause flickering when + // fading is enabled, and could create inconsistency when the wallpaper + // is not correctly set. if (ps->o.unredir_if_possible && is_highest && to_paint) { is_highest = false; - // Disable unredirection for multi-screen setups if (WMODE_SOLID == w->mode && (!w->frame_opacity || !win_has_frame(w)) - && win_is_fullscreen(ps, w)) + && win_is_fullscreen(ps, w) + && !w->unredir_if_possible_excluded) unredir_possible = true; } @@ -1242,7 +1244,6 @@ paint_preprocess(session_t *ps, win *list) { w->flags = 0; } - // Avoid setting w->to_paint if w is to be freed bool destroyed = (w->opacity_tgt == w->opacity && w->destroyed); @@ -1258,14 +1259,25 @@ paint_preprocess(session_t *ps, win *list) { w->to_paint = to_paint; } + // If possible, unredirect all windows and stop painting if (UNSET != ps->o.redirected_force) unredir_possible = !ps->o.redirected_force; - if (unredir_possible) - redir_stop(ps); - else + if (unredir_possible) { + if (ps->redirected) { + if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) + redir_stop(ps); + else if (!ps->tmout_unredir->enabled) { + timeout_reset(ps, ps->tmout_unredir); + ps->tmout_unredir->enabled = true; + } + } + } + else { + ps->tmout_unredir->enabled = false; redir_start(ps); + } return t; } @@ -1691,7 +1703,6 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t #ifdef DEBUG_REPAINT static struct timespec last_paint = { 0 }; #endif - win *w = NULL; XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; #ifdef CONFIG_VSYNC_OPENGL @@ -1771,7 +1782,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t reg_tmp = XFixesCreateRegion(ps->dpy, NULL, 0); reg_tmp2 = XFixesCreateRegion(ps->dpy, NULL, 0); - for (w = t; w; w = w->prev_trans) { + for (win *w = t; w; w = w->prev_trans) { // Painting shadow if (w->shadow) { // Shadow is to be painted based on the ignore region of current @@ -1953,7 +1964,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t printf("[ %5ld:%09ld ] ", diff.tv_sec, diff.tv_nsec); last_paint = now; printf("paint:"); - for (w = t; w; w = w->prev_trans) + for (win *w = t; w; w = w->prev_trans) printf(" %#010lx", w->id); putchar('\n'); fflush(stdout); @@ -1962,7 +1973,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t // Check if fading is finished on all painted windows { win *pprev = NULL; - for (w = t; w; w = pprev) { + for (win *w = t; w; w = pprev) { pprev = w->prev_trans; check_fade_fin(ps, w); } @@ -1971,6 +1982,11 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t static void add_damage(session_t *ps, XserverRegion damage) { + // Ignore damage when screen isn't redirected + if (!ps->redirected) + free_region(ps, &damage); + + if (!damage) return; if (ps->all_damage) { XFixesUnionRegion(ps->dpy, ps->all_damage, ps->all_damage, damage); XFixesDestroyRegion(ps->dpy, damage); @@ -1999,13 +2015,18 @@ repair_win(session_t *ps, win *w) { w->a.y + w->a.border_width); } + w->damaged = true; + w->pixmap_damaged = true; + + // Why care about damage when screen is unredirected? + // We will force full-screen repaint on redirection. + if (!ps->redirected) return; + // Remove the part in the damage area that could be ignored if (!ps->reg_ignore_expire && w->prev_trans && w->prev_trans->reg_ignore) XFixesSubtractRegion(ps->dpy, parts, parts, w->prev_trans->reg_ignore); add_damage(ps, parts); - w->damaged = true; - w->pixmap_damaged = true; } static wintype_t @@ -2507,6 +2528,9 @@ win_on_factor_change(session_t *ps, win *w) { if (ps->o.paint_blacklist) w->paint_excluded = win_match(ps, w, ps->o.paint_blacklist, &w->cache_pblst); + if (ps->o.unredir_if_possible_blacklist) + w->unredir_if_possible_excluded = win_match(ps, w, + ps->o.unredir_if_possible_blacklist, &w->cache_uipblst); } /** @@ -3677,9 +3701,8 @@ ev_name(session_t *ps, XEvent *ev) { CASESTRRET(PropertyNotify); CASESTRRET(ClientMessage); default: - if (ev->type == ps->damage_event + XDamageNotify) { + if (isdamagenotify(ps, ev)) return "Damage"; - } if (ps->shape_exists && ev->type == ps->shape_event) { return "ShapeNotify"; @@ -3718,7 +3741,7 @@ ev_window(session_t *ps, XEvent *ev) { case ClientMessage: return ev->xclient.window; default: - if (ev->type == ps->damage_event + XDamageNotify) { + if (isdamagenotify(ps, ev)) { return ((XDamageNotifyEvent *)ev)->drawable; } @@ -4166,7 +4189,7 @@ ev_handle(session_t *ps, XEvent *ev) { } #ifdef DEBUG_EVENTS - if (ev->type != ps->damage_event + XDamageNotify) { + if (!isdamagenotify(ps, ev)) { Window wid = ev_window(ps, ev); char *window_name = NULL; bool to_free = false; @@ -4228,10 +4251,10 @@ ev_handle(session_t *ps, XEvent *ev) { ev_screen_change_notify(ps, (XRRScreenChangeNotifyEvent *) ev); break; } - if (ev->type == ps->damage_event + XDamageNotify) { - ev_damage_notify(ps, (XDamageNotifyEvent *)ev); + if (isdamagenotify(ps, ev)) { + ev_damage_notify(ps, (XDamageNotifyEvent *) ev); + break; } - break; } } @@ -4376,6 +4399,12 @@ usage(int ret) { "--unredir-if-possible\n" " Unredirect all windows if a full-screen opaque window is\n" " detected, to maximize performance for full-screen windows.\n" + "--unredir-if-possible-delay ms\n" + " Delay before unredirecting the window, in milliseconds.\n" + " Defaults to 0.\n" + "--unredir-if-possible-exclude condition\n" + " Conditions of windows that shouldn't be considered full-screen\n" + " for unredirecting screen.\n" "--focus-exclude condition\n" " Specify a list of conditions of windows that should always be\n" " considered focused.\n" @@ -4602,6 +4631,27 @@ fork_after(session_t *ps) { return success; } +/** + * Parse a long number. + */ +static inline bool +parse_long(const char *s, long *dest) { + const char *endptr = NULL; + long val = strtol(s, (char **) &endptr, 0); + if (!endptr || endptr == s) { + printf_errf("(\"%s\"): Invalid number.", s); + return false; + } + while (isspace(*endptr)) + ++endptr; + if (*endptr) { + printf_errf("(\"%s\"): Trailing characters.", s); + return false; + } + *dest = val; + return true; +} + /** * Parse a floating-point number in matrix. */ @@ -5288,6 +5338,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "shadow-exclude-reg", required_argument, NULL, 305 }, { "paint-exclude", required_argument, NULL, 306 }, { "xinerama-shadow-crop", no_argument, NULL, 307 }, + { "unredir-if-possible-exclude", required_argument, NULL, 308 }, + { "unredir-if-possible-delay", required_argument, NULL, 309 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5344,17 +5396,22 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { optind = 1; while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { + long val = 0; switch (o) { #define P_CASEBOOL(idx, option) case idx: ps->o.option = true; break +#define P_CASELONG(idx, option) \ + case idx: \ + if (!parse_long(optarg, &val)) exit(1); \ + ps->o.option = val; \ + break + // Short options case 'h': usage(0); break; case 'd': break; - case 'D': - ps->o.fade_delta = atoi(optarg); - break; + P_CASELONG('D', fade_delta); case 'I': ps->o.fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; @@ -5378,18 +5435,12 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { fading_enable = true; break; P_CASEBOOL('S', synchronize); - case 'r': - ps->o.shadow_radius = atoi(optarg); - break; + P_CASELONG('r', shadow_radius); case 'o': ps->o.shadow_opacity = atof(optarg); break; - case 'l': - ps->o.shadow_offset_x = atoi(optarg); - break; - case 't': - ps->o.shadow_offset_y = atoi(optarg); - break; + P_CASELONG('l', shadow_offset_x); + P_CASELONG('t', shadow_offset_y); case 'i': ps->o.inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE); break; @@ -5434,10 +5485,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { P_CASEBOOL(266, shadow_ignore_shaped); P_CASEBOOL(267, detect_rounded_corners); P_CASEBOOL(268, detect_client_opacity); - case 269: - // --refresh-rate - ps->o.refresh_rate = atoi(optarg); - break; + P_CASELONG(269, refresh_rate); case 270: // --vsync if (!parse_vsync(ps, optarg)) @@ -5484,10 +5532,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { break; P_CASEBOOL(291, glx_no_stencil); P_CASEBOOL(292, glx_copy_from_front); - case 293: - // --benchmark - ps->o.benchmark = atoi(optarg); - break; + P_CASELONG(293, benchmark); case 294: // --benchmark-wid ps->o.benchmark_wid = strtol(optarg, NULL, 0); @@ -5516,10 +5561,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (!parse_conv_kern_lst(ps, optarg, ps->o.blur_kerns, MAX_BLUR_PASS)) exit(1); break; - case 302: - // --resize-damage - ps->o.resize_damage = atoi(optarg); - break; + P_CASELONG(302, resize_damage); P_CASEBOOL(303, glx_use_gpushader4); case 304: // --opacity-rule @@ -5536,6 +5578,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { condlst_add(ps, &ps->o.paint_blacklist, optarg); break; P_CASEBOOL(307, xinerama_shadow_crop); + case 308: + // --unredir-if-possible-exclude + condlst_add(ps, &ps->o.unredir_if_possible_blacklist, optarg); + break; + P_CASELONG(309, unredir_if_possible_delay); default: usage(1); break; @@ -5583,10 +5630,6 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (ps->o.blur_background_frame) ps->o.blur_background = true; - // Free background blur blacklist if background blur is not actually enabled - if (!ps->o.blur_background) - free_wincondlst(&ps->o.blur_background_blacklist); - // Other variables determined by options // Determine whether we need to track focus changes @@ -6110,7 +6153,8 @@ static void redir_start(session_t *ps) { if (!ps->redirected) { #ifdef DEBUG_REDIR - printf("redir_start(): Screen redirected.\n"); + print_timestamp(ps); + printf_dbgf("(): Screen redirected.\n"); #endif // Map overlay window. Done firstly according to this: @@ -6133,6 +6177,9 @@ redir_start(session_t *ps) { XSync(ps->dpy, False); ps->redirected = true; + + // Repaint the whole screen + force_repaint(ps); } } @@ -6272,6 +6319,14 @@ timeout_invoke(session_t *ps, timeout_t *ptmout) { ptmout->callback(ps, ptmout); } +/** + * Reset a timeout to initial state. + */ +void +timeout_reset(session_t *ps, timeout_t *ptmout) { + ptmout->firstrun = ptmout->lastrun = get_time_ms(); +} + /** * Unredirect all windows. */ @@ -6279,7 +6334,8 @@ static void redir_stop(session_t *ps) { if (ps->redirected) { #ifdef DEBUG_REDIR - printf("redir_stop(): Screen unredirected.\n"); + print_timestamp(ps); + printf_dbgf("(): Screen unredirected.\n"); #endif // Destroy all Pictures as they expire once windows are unredirected // If we don't destroy them here, looks like the resources are just @@ -6299,6 +6355,17 @@ redir_stop(session_t *ps) { } } +/** + * Unredirection timeout callback. + */ +static bool +tmout_unredir_callback(session_t *ps, timeout_t *tmout) { + ps->tmout_unredir_hit = true; + tmout->enabled = false; + + return true; +} + /** * Main loop. */ @@ -6452,6 +6519,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .paint_on_overlay = false, .resize_damage = 0, .unredir_if_possible = false, + .unredir_if_possible_blacklist = NULL, + .unredir_if_possible_delay = 0, .redirected_force = UNSET, .stoppaint_force = UNSET, .dbus = false, @@ -6829,11 +6898,12 @@ session_init(session_t *ps_old, int argc, char **argv) { } fds_insert(ps, ConnectionNumber(ps->dpy), POLLIN); + ps->tmout_unredir = timeout_insert(ps, ps->o.unredir_if_possible_delay, + tmout_unredir_callback, NULL); + ps->tmout_unredir->enabled = false; XGrabServer(ps->dpy); - redir_start(ps); - { Window root_return, parent_return; Window *children; @@ -6942,6 +7012,7 @@ session_destroy(session_t *ps) { free_wincondlst(&ps->o.blur_background_blacklist); free_wincondlst(&ps->o.opacity_rules); free_wincondlst(&ps->o.paint_blacklist); + free_wincondlst(&ps->o.unredir_if_possible_blacklist); #endif // Free tracked atom list @@ -7047,6 +7118,7 @@ session_destroy(session_t *ps) { XSync(ps->dpy, True); // Free timeouts + ps->tmout_unredir = NULL; timeout_clear(ps); if (ps == ps_g) @@ -7101,6 +7173,7 @@ session_run(session_t *ps) { ps->idling = true; t = paint_preprocess(ps, ps->list); + ps->tmout_unredir_hit = false; // If the screen is unredirected, free all_damage to stop painting if (!ps->redirected || ON == ps->o.stoppaint_force) diff --git a/compton.h b/compton.h index c66739dd4..d44544633 100644 --- a/compton.h +++ b/compton.h @@ -324,6 +324,14 @@ ms_to_tv(int timeout) { }; } +/** + * Whether an event is DamageNotify. + */ +static inline bool +isdamagenotify(session_t *ps, const XEvent *ev) { + return ps->damage_event + XDamageNotify == ev->type; +} + /** * Create a XTextProperty of a single string. */ @@ -1205,6 +1213,9 @@ timeout_get_poll_time(session_t *ps); static void timeout_clear(session_t *ps); +static bool +tmout_unredir_callback(session_t *ps, timeout_t *tmout); + static bool mainloop(session_t *ps); diff --git a/dbus.c b/dbus.c index 8f53eeefa..299c5ac0b 100644 --- a/dbus.c +++ b/dbus.c @@ -898,6 +898,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { cdbus_m_opts_get_do(detect_rounded_corners, cdbus_reply_bool); cdbus_m_opts_get_do(paint_on_overlay, cdbus_reply_bool); cdbus_m_opts_get_do(unredir_if_possible, cdbus_reply_bool); + cdbus_m_opts_get_do(unredir_if_possible_delay, cdbus_reply_int32); cdbus_m_opts_get_do(redirected_force, cdbus_reply_enum); cdbus_m_opts_get_do(stoppaint_force, cdbus_reply_enum); cdbus_m_opts_get_do(logpath, cdbus_reply_string); From 7e488d813c7d32e9226097bb1c519a9fd1e60333 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 12 Sep 2013 21:23:20 +0800 Subject: [PATCH 156/178] Bug fix #140: Possible fix for CPU usage with --unredir-possible-delay - Possible fix for high CPU usage with a low --unredir-possible-delay. Thanks to Feltzer for reporting. (#140) - Rewrite some parts to (hopefully) increase performance, especially with --unredir-if-possible. - Known issue: With GTX 670 and x11-drivers/nvidia-drivers-325.15, and compton --unredir-if-possible --config /dev/null, if you send a SIGUSR1 to compton when a full-screen solid window is there, in which case compton either redirects then immediately unredirects the screen, or just don't redirect it altogether, X freezes after compton unredirects the screen. Requests sent by other X clients are not responded until compton is killed, which indicates the possibility of a bug in X. Attaching to X process shows X is on ./os/waitFor.c. Backend does not matter. --paint-on-overlay fixes the issue somehow. compton-git-v0.1_beta1-5-g4600f43-2013-08-28 doesn't exhibit the issue, but it's probably timing-related. --- compton.c | 106 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/compton.c b/compton.c index b8f1fc6d5..92378b047 100644 --- a/compton.c +++ b/compton.c @@ -1102,9 +1102,16 @@ paint_preprocess(session_t *ps, win *list) { next = w->next; opacity_t opacity_old = w->opacity; - // Destroy reg_ignore on all windows if they should expire - if (ps->reg_ignore_expire) - free_region(ps, &w->reg_ignore); + // Data expiration + { + // Remove built shadow if needed + if (w->flags & WFLAG_SIZE_CHANGE) + free_paint(ps, &w->shadow_paint); + + // Destroy reg_ignore on all windows if they should expire + if (ps->reg_ignore_expire) + free_region(ps, &w->reg_ignore); + } // Update window opacity target and dim state if asked if (WFLAG_OPCT_CHANGE & w->flags) { @@ -1115,43 +1122,33 @@ paint_preprocess(session_t *ps, win *list) { // Run fading run_fade(ps, w, steps); + // Opacity will not change, from now on. + // Give up if it's not damaged or invisible, or it's unmapped and its - // pixmap is gone (for example due to a ConfigureNotify) + // pixmap is gone (for example due to a ConfigureNotify), or when it's + // excluded if (!w->damaged || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= ps->root_width || w->a.y >= ps->root_height - || ((IsUnmapped == w->a.map_state || w->destroyed) - && !w->paint.pixmap)) { + || ((IsUnmapped == w->a.map_state || w->destroyed) && !w->paint.pixmap) + || get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0] + || w->paint_excluded) to_paint = false; - } - to_paint = to_paint && !w->paint_excluded; + // to_paint will never change afterward - if (to_paint) { - // If opacity changes - if (w->opacity != opacity_old) { - win_determine_mode(ps, w); - add_damage_win(ps, w); - } - - if (get_alpha_pict_o(ps, w->opacity) == ps->alpha_picts[0]) - to_paint = false; - } + // Determine mode as early as possible + if (to_paint && (!w->to_paint || w->opacity != opacity_old)) + win_determine_mode(ps, w); if (to_paint) { // Fetch bounding region - if (!w->border_size) { + if (!w->border_size) w->border_size = border_size(ps, w, true); - } // Fetch window extents - if (!w->extents) { + if (!w->extents) w->extents = win_extents(ps, w); - // If w->extents does not exist, the previous add_damage_win() - // call when opacity changes has no effect, so redo it here. - if (w->opacity != opacity_old) - add_damage_win(ps, w); - } // Calculate frame_opacity { @@ -1164,6 +1161,8 @@ paint_preprocess(session_t *ps, win *list) { else w->frame_opacity = 0.0; + // Destroy all reg_ignore above when frame opaque state changes on + // SOLID mode if (w->to_paint && WMODE_SOLID == mode_old && (0.0 == frame_opacity_old) != (0.0 == w->frame_opacity)) ps->reg_ignore_expire = true; @@ -1174,24 +1173,18 @@ paint_preprocess(session_t *ps, win *list) { w->shadow_opacity = ps->o.shadow_opacity * w->frame_opacity; else w->shadow_opacity = ps->o.shadow_opacity * get_opacity_percent(w); - - // Rebuild shadow if necessary - if (w->flags & WFLAG_SIZE_CHANGE) { - free_paint(ps, &w->shadow_paint); - } - - if (w->shadow && !paint_isvalid(ps, &w->shadow_paint)) - win_build_shadow(ps, w, 1); } + // Add window to damaged area if its painting status changes + // or opacity changes + if (to_paint != w->to_paint || w->opacity != opacity_old) + add_damage_win(ps, w); + + // Destroy all reg_ignore above when window mode changes if ((to_paint && WMODE_SOLID == w->mode) != (w->to_paint && WMODE_SOLID == mode_old)) ps->reg_ignore_expire = true; - // Add window to damaged area if its painting status changes - if (to_paint != w->to_paint) - add_damage_win(ps, w); - if (to_paint) { // Generate ignore region for painting to reduce GPU load if (ps->reg_ignore_expire || !w->to_paint) { @@ -1264,6 +1257,10 @@ paint_preprocess(session_t *ps, win *list) { if (UNSET != ps->o.redirected_force) unredir_possible = !ps->o.redirected_force; + // If there's no window to paint, and the screen isn't redirected, + // don't redirect it. + if (ps->o.unredir_if_possible && is_highest && !ps->redirected) + unredir_possible = true; if (unredir_possible) { if (ps->redirected) { if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) @@ -1785,6 +1782,10 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t for (win *w = t; w; w = w->prev_trans) { // Painting shadow if (w->shadow) { + // Lazy shadow building + if (!paint_isvalid(ps, &w->shadow_paint)) + win_build_shadow(ps, w, 1); + // Shadow is to be painted based on the ignore region of current // window if (w->reg_ignore) { @@ -2050,6 +2051,13 @@ wid_get_prop_wintype(session_t *ps, Window wid) { static void map_win(session_t *ps, Window id) { + // Unmap overlay window if it got mapped but we are currently not + // in redirected state. + if (ps->overlay && id == ps->overlay && !ps->redirected) { + XUnmapWindow(ps->dpy, ps->overlay); + XFlush(ps->dpy); + } + win *w = find_win(ps, id); // Don't care about window mapping if it's an InputOnly window @@ -2831,14 +2839,13 @@ add_win(session_t *ps, Window id, Window prev) { assert(IsViewable == map_state || IsUnmapped == map_state); new->a.map_state = IsUnmapped; - // Get window picture format - if (InputOutput == new->a.class) + if (InputOutput == new->a.class) { + // Get window picture format new->pictfmt = XRenderFindVisualFormat(ps->dpy, new->a.visual); - // Create Damage for window - if (InputOutput == new->a.class) { - set_ignore_next(ps); - new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); + // Create Damage for window + set_ignore_next(ps); + new->damage = XDamageCreate(ps->dpy, id, XDamageReportNonEmpty); } calc_win_size(ps, new); @@ -6097,6 +6104,11 @@ init_overlay(session_t *ps) { // Retrieve DamageNotify on root window if we are painting on an // overlay // root_damage = XDamageCreate(ps->dpy, root, XDamageReportNonEmpty); + + // Unmap overlay, firstly. But this typically does not work because + // the window isn't created yet. + // XUnmapWindow(ps->dpy, ps->overlay); + // XFlush(ps->dpy); } else { fprintf(stderr, "Cannot get X Composite overlay window. Falling " @@ -6371,6 +6383,9 @@ tmout_unredir_callback(session_t *ps, timeout_t *tmout) { */ static bool mainloop(session_t *ps) { + // Don't miss timeouts even when we have a LOT of other events! + timeout_run(ps); + // Process existing events // Sometimes poll() returns 1 but no events are actually read, // causing XNextEvent() to block, I have no idea what's wrong, so we @@ -6444,8 +6459,6 @@ mainloop(session_t *ps) { free(ptv); ptv = NULL; - timeout_run(ps); - return true; } @@ -6732,6 +6745,7 @@ session_init(session_t *ps_old, int argc, char **argv) { | ExposureMask | StructureNotifyMask | PropertyChangeMask); + XFlush(ps->dpy); ps->root_width = DisplayWidth(ps->dpy, ps->scr); ps->root_height = DisplayHeight(ps->dpy, ps->scr); From 631f50d665057a034d4cd22b49e030d1913ab79a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 14 Sep 2013 10:04:38 +0800 Subject: [PATCH 157/178] Bug fix #143: Move setlocale(LC_NUMERIC, "C") Move setlocale(LC_NUMERIC, "C") to a better place, in hope to fix #143. Thanks to hiciu for reporting! --- compton.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compton.c b/compton.c index 92378b047..23e86dd32 100644 --- a/compton.c +++ b/compton.c @@ -5390,16 +5390,16 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.wintype_opacity[i] = 1.0; } + // Enforce LC_NUMERIC locale "C" here to make sure dots are recognized + // instead of commas in atof(). + setlocale(LC_NUMERIC, "C"); + #ifdef CONFIG_LIBCONFIG parse_config(ps, &cfgtmp); #endif // Parse commandline arguments. Range checking will be done later. - // Enforce LC_NUMERIC locale "C" here to make sure dots are recognized - // instead of commas in atof(). - setlocale(LC_NUMERIC, "C"); - optind = 1; while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { From 9c609b9b97eee09e975d96082ffab32543258251 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 15 Sep 2013 08:56:53 +0800 Subject: [PATCH 158/178] Bug fix #143: GLSL shader error on European locales Set LC_NUMERIC=C when generating GLSL shader strings to avoid decimal point issues on European locales. Thanks to hiciu for reporting. --- opengl.c | 9 +++++++++ opengl.h | 1 + 2 files changed, 10 insertions(+) diff --git a/opengl.c b/opengl.c index a26a286f4..33a2fc724 100644 --- a/opengl.c +++ b/opengl.c @@ -225,6 +225,11 @@ glx_init_blur(session_t *ps) { #ifdef CONFIG_VSYNC_OPENGL_GLSL { + char *lc_numeric_old = mstrcpy(setlocale(LC_NUMERIC, NULL)); + // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane + // Thanks to hiciu for reporting. + setlocale(LC_NUMERIC, "C"); + static const char *FRAG_SHADER_BLUR_PREFIX = "#version 110\n" "%s" @@ -331,6 +336,10 @@ glx_init_blur(session_t *ps) { #undef P_GET_UNIFM_LOC } free(extension); + + // Restore LC_NUMERIC + setlocale(LC_NUMERIC, lc_numeric_old); + free(lc_numeric_old); } diff --git a/opengl.h b/opengl.h index eae8bcf86..564b7e20a 100644 --- a/opengl.h +++ b/opengl.h @@ -11,6 +11,7 @@ #include "common.h" #include +#include #ifdef DEBUG_GLX_ERR From 6e82deb05b7e8d4c183d33c5a4acb978cf465b46 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 15 Sep 2013 11:07:49 +0800 Subject: [PATCH 159/178] Bug fix #144: Workaround for insane window type changes Dynamically detect window type as a workaround to insane applications. Thanks to Feltzer for reporting. (#144) --- compton.c | 62 +++++++++++++++++++++++++++++++++---------------------- compton.h | 3 +++ 2 files changed, 40 insertions(+), 25 deletions(-) diff --git a/compton.c b/compton.c index 23e86dd32..55e638fca 100644 --- a/compton.c +++ b/compton.c @@ -2586,6 +2586,31 @@ calc_shadow_geometry(session_t *ps, win *w) { w->shadow_height = w->heightb + ps->gaussian_map->size; } +/** + * Update window type. + */ +static void +win_upd_wintype(session_t *ps, win *w) { + const wintype_t wtype_old = w->window_type; + + // Detect window type here + w->window_type = wid_get_prop_wintype(ps, w->client_win); + + // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take + // override-redirect windows or windows without WM_TRANSIENT_FOR as + // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. + if (WINTYPE_UNKNOWN == w->window_type) { + if (w->a.override_redirect + || !wid_has_prop(ps, w->client_win, ps->atom_transient)) + w->window_type = WINTYPE_NORMAL; + else + w->window_type = WINTYPE_DIALOG; + } + + if (w->window_type != wtype_old) + win_on_wtype_change(ps, w); +} + /** * Mark a window as the client window of another. * @@ -2608,32 +2633,11 @@ win_mark_client(session_t *ps, win *w, Window client) { // Make sure the XSelectInput() requests are sent XSync(ps->dpy, False); - // Get frame widths if needed - if (ps->o.frame_opacity) { - get_frame_extents(ps, w, client); - } + win_upd_wintype(ps, w); - { - wintype_t wtype_old = w->window_type; - - // Detect window type here - if (WINTYPE_UNKNOWN == w->window_type) - w->window_type = wid_get_prop_wintype(ps, w->client_win); - - // Conform to EWMH standard, if _NET_WM_WINDOW_TYPE is not present, take - // override-redirect windows or windows without WM_TRANSIENT_FOR as - // _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG. - if (WINTYPE_UNKNOWN == w->window_type) { - if (w->a.override_redirect - || !wid_has_prop(ps, client, ps->atom_transient)) - w->window_type = WINTYPE_NORMAL; - else - w->window_type = WINTYPE_DIALOG; - } - - if (w->window_type != wtype_old) - win_on_wtype_change(ps, w); - } + // Get frame widths. The window is in damaged area already. + if (ps->o.frame_opacity) + get_frame_extents(ps, w, client); // Get window group if (ps->o.track_leader) @@ -4027,6 +4031,14 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { } } + // If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but + // there are always some stupid applications. (#144) + if (ev->atom == ps->atom_win_type) { + win *w = NULL; + if ((w = find_toplevel(ps, ev->window))) + win_upd_wintype(ps, w); + } + // If _NET_WM_OPACITY changes if (ev->atom == ps->atom_opacity) { win *w = NULL; diff --git a/compton.h b/compton.h index d44544633..1180ce32c 100644 --- a/compton.h +++ b/compton.h @@ -803,6 +803,9 @@ calc_win_size(session_t *ps, win *w); static void calc_shadow_geometry(session_t *ps, win *w); +static void +win_upd_wintype(session_t *ps, win *w); + static void win_mark_client(session_t *ps, win *w, Window client); From 2b534cd86445fcd770948bbb79f4732361259ab9 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 18 Sep 2013 21:50:57 +0800 Subject: [PATCH 160/178] Bug fix #99: Rewrite focus detection logic - Rewrite focus detection logic. Remove w->focused_real and use ps->active_win to identify focused window uniformly. Use a more expensive way to filter FocusIn/Out events to improve reliability. Only limited tests are done, and bugs are likely to be introduced. (#99) - Known issue: Under fvwm, compton sometimes does not consistently report the window input gets sent to. But there's something wrong in that case: XGetInputFocus() shows the root window is focused but another window is receiving input. --- c2.c | 2 +- common.h | 22 ++++---- compton.c | 161 ++++++++++++++++++++++++------------------------------ compton.h | 19 ++++++- dbus.c | 6 +- 5 files changed, 107 insertions(+), 103 deletions(-) diff --git a/c2.c b/c2.c index e89638b21..de221c01d 100644 --- a/c2.c +++ b/c2.c @@ -1055,7 +1055,7 @@ c2_match_once_leaf(session_t *ps, win *w, const c2_l_t *pleaf, case C2_L_PFULLSCREEN: tgt = win_is_fullscreen(ps, w); break; case C2_L_POVREDIR: tgt = w->a.override_redirect; break; case C2_L_PARGB: tgt = (WMODE_ARGB == w->mode); break; - case C2_L_PFOCUSED: tgt = w->focused_real; break; + case C2_L_PFOCUSED: tgt = win_is_focused_real(ps, w); break; case C2_L_PWMWIN: tgt = w->wmwin; break; case C2_L_PCLIENT: tgt = w->client_win; break; case C2_L_PLEADER: tgt = w->leader; break; diff --git a/common.h b/common.h index ea95235ae..eadaafd95 100644 --- a/common.h +++ b/common.h @@ -954,8 +954,6 @@ typedef struct _win { bool focused; /// Override value of window focus state. Set by D-Bus method calls. switch_t focused_force; - /// Whether the window is actually focused. - bool focused_real; // Blacklist related members /// Name of the window. @@ -1695,6 +1693,14 @@ find_toplevel(session_t *ps, Window id) { return NULL; } +/** + * Check if a window is really focused. + */ +static inline bool +win_is_focused_real(session_t *ps, const win *w) { + return IsViewable == w->a.map_state && ps->active_win == w; +} + /** * Find out the currently focused window. * @@ -1702,19 +1708,15 @@ find_toplevel(session_t *ps, Window id) { */ static inline win * find_focused(session_t *ps) { - if (!ps->o.track_focus) - return NULL; - - for (win *w = ps->list; w; w = w->next) { - if (w->focused_real && !w->destroyed) - return w; - } + if (!ps->o.track_focus) return NULL; + if (ps->active_win && win_is_focused_real(ps, ps->active_win)) + return ps->active_win; return NULL; } /** - * Copies a region + * Copies a region. */ static inline XserverRegion copy_region(const session_t *ps, XserverRegion oldregion) { diff --git a/compton.c b/compton.c index 55e638fca..511083c1a 100644 --- a/compton.c +++ b/compton.c @@ -776,19 +776,12 @@ recheck_focus(session_t *ps) { // opacity on it Window wid = 0; int revert_to; - win *w = NULL; XGetInputFocus(ps->dpy, &wid, &revert_to); - if (!wid || PointerRoot == wid) - return NULL; - - // Fallback to the old method if find_toplevel() fails - if (!(w = find_toplevel(ps, wid))) { - w = find_toplevel2(ps, wid); - } + win *w = find_win_all(ps, wid); - // And we set the focus state and opacity here + // And we set the focus state here if (w) { win_set_focused(ps, w, true); return w; @@ -2066,19 +2059,12 @@ map_win(session_t *ps, Window id) { || IsViewable == w->a.map_state) return; - assert(!w->focused_real); + assert(!win_is_focused_real(ps, w)); w->a.map_state = IsViewable; cxinerama_win_upd_scr(ps, w); - // Set focused to false - bool focused_real = false; - if (ps->o.track_focus && ps->o.use_ewmh_active_win - && w == ps->active_win) - focused_real = true; - win_set_focused(ps, w, focused_real); - // Call XSelectInput() before reading properties so that no property // changes are lost XSelectInput(ps->dpy, id, determine_evmask(ps, id, WIN_EVMODE_FRAME)); @@ -2113,14 +2099,10 @@ map_win(session_t *ps, Window id) { // Detect if the window is shaped or has rounded corners win_update_shape_raw(ps, w); - // Occasionally compton does not seem able to get a FocusIn event from - // a window just mapped. I suspect it's a timing issue again when the - // XSelectInput() is called too late. We have to recheck the focused - // window here. It makes no sense if we are using EWMH - // _NET_ACTIVE_WINDOW. - if (ps->o.track_focus && !ps->o.use_ewmh_active_win) { + // FocusIn/Out may be ignored when the window is unmapped, so we must + // recheck focus here + if (ps->o.track_focus) recheck_focus(ps); - } // Update window focus state win_update_focused(ps, w); @@ -2296,7 +2278,7 @@ calc_opacity(session_t *ps, win *w) { } // Respect active_opacity only when the window is physically focused - if (OPAQUE == opacity && ps->o.active_opacity && w->focused_real) + if (OPAQUE == opacity && ps->o.active_opacity && win_is_focused_real(ps, w)) opacity = ps->o.active_opacity; } @@ -2752,7 +2734,6 @@ add_win(session_t *ps, Window id, Window prev) { .focused = false, .focused_force = UNSET, - .focused_real = false, .name = NULL, .class_instance = NULL, @@ -3270,7 +3251,7 @@ win_update_focused(session_t *ps, win *w) { w->focused = w->focused_force; } else { - w->focused = w->focused_real; + w->focused = win_is_focused_real(ps, w); // Use wintype_focus, and treat WM windows and override-redirected // windows specially @@ -3302,51 +3283,68 @@ win_set_focused(session_t *ps, win *w, bool focused) { if (IsUnmapped == w->a.map_state) return; - if (w->focused_real != focused) { - w->focused_real = focused; + if (win_is_focused_real(ps, w) == focused) return; - // If window grouping detection is enabled - if (ps->o.track_leader) { - Window leader = win_get_leader(ps, w); + if (focused) { + if (ps->active_win) + win_set_focused(ps, ps->active_win, false); + ps->active_win = w; + } + else if (w == ps->active_win) + ps->active_win = NULL; - // If the window gets focused, replace the old active_leader - if (w->focused_real && leader != ps->active_leader) { - Window active_leader_old = ps->active_leader; + assert(win_is_focused_real(ps, w) == focused); - ps->active_leader = leader; + win_on_focus_change(ps, w); +} - group_update_focused(ps, active_leader_old); - group_update_focused(ps, leader); - } - // If the group get unfocused, remove it from active_leader - else if (!w->focused_real && leader && leader == ps->active_leader - && !group_is_focused(ps, leader)) { - ps->active_leader = None; - group_update_focused(ps, leader); - } +/** + * Handle window focus change. + */ +static void +win_on_focus_change(session_t *ps, win *w) { + // If window grouping detection is enabled + if (ps->o.track_leader) { + Window leader = win_get_leader(ps, w); - // The window itself must be updated anyway - win_update_focused(ps, w); + // If the window gets focused, replace the old active_leader + if (win_is_focused_real(ps, w) && leader != ps->active_leader) { + Window active_leader_old = ps->active_leader; + + ps->active_leader = leader; + + group_update_focused(ps, active_leader_old); + group_update_focused(ps, leader); } - // Otherwise, only update the window itself - else { - win_update_focused(ps, w); + // If the group get unfocused, remove it from active_leader + else if (!win_is_focused_real(ps, w) && leader && leader == ps->active_leader + && !group_is_focused(ps, leader)) { + ps->active_leader = None; + group_update_focused(ps, leader); } - // Update everything related to conditions - win_on_factor_change(ps, w); + // The window itself must be updated anyway + win_update_focused(ps, w); + } + // Otherwise, only update the window itself + else { + win_update_focused(ps, w); + } + + // Update everything related to conditions + win_on_factor_change(ps, w); #ifdef CONFIG_DBUS - // Send D-Bus signal - if (ps->o.dbus) { - if (w->focused_real) - cdbus_ev_win_focusin(ps, w); - else - cdbus_ev_win_focusout(ps, w); - } -#endif + // Send D-Bus signal + if (ps->o.dbus) { + if (win_is_focused_real(ps, w)) + cdbus_ev_win_focusin(ps, w); + else + cdbus_ev_win_focusout(ps, w); } +#endif } + /** * Update leader of a window. */ @@ -3386,7 +3384,7 @@ win_set_leader(session_t *ps, win *w, Window nleader) { // Update the old and new window group and active_leader if the window // could affect their state. Window cache_leader = win_get_leader(ps, w); - if (w->focused_real && cache_leader_old != cache_leader) { + if (win_is_focused_real(ps, w) && cache_leader_old != cache_leader) { ps->active_leader = cache_leader; group_update_focused(ps, cache_leader_old); @@ -3808,11 +3806,10 @@ ev_focus_report(XFocusChangeEvent* ev) { */ inline static bool ev_focus_accept(XFocusChangeEvent *ev) { - return ev->detail == NotifyNonlinear - || ev->detail == NotifyNonlinearVirtual; + return NotifyNormal == ev->mode || NotifyUngrab == ev->mode; } -inline static void +static inline void ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { #ifdef DEBUG_EVENTS ev_focus_report(ev); @@ -3821,12 +3818,9 @@ ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { if (!ev_focus_accept(ev)) return; - win *w = find_win(ps, ev->window); - - // To deal with events sent from windows just destroyed - if (!w) return; - - win_set_focused(ps, w, true); + win *w = find_win_all(ps, ev->window); + if (w) + win_set_focused(ps, w, true); } inline static void @@ -3838,12 +3832,9 @@ ev_focus_out(session_t *ps, XFocusChangeEvent *ev) { if (!ev_focus_accept(ev)) return; - win *w = find_win(ps, ev->window); - - // To deal with events sent from windows just destroyed - if (!w) return; - - win_set_focused(ps, w, false); + win *w = find_win_all(ps, ev->window); + if (w) + win_set_focused(ps, w, false); } inline static void @@ -3965,21 +3956,11 @@ ev_expose(session_t *ps, XExposeEvent *ev) { static void update_ewmh_active_win(session_t *ps) { // Search for the window - Window wid = - wid_get_prop_window(ps, ps->root, ps->atom_ewmh_active_win); - win *w = NULL; - - if (wid && !(w = find_toplevel(ps, wid))) - if (!(w = find_win(ps, wid))) - w = find_toplevel2(ps, wid); + Window wid = wid_get_prop_window(ps, ps->root, ps->atom_ewmh_active_win); + win *w = find_win_all(ps, wid); - // Mark the window focused - if (w) { - if (ps->active_win && w != ps->active_win) - win_set_focused(ps, ps->active_win, false); - ps->active_win = w; - win_set_focused(ps, w, true); - } + // Mark the window focused. No need to unfocus the previous one. + if (w) win_set_focused(ps, w, true); } inline static void diff --git a/compton.h b/compton.h index 1180ce32c..11325b75c 100644 --- a/compton.h +++ b/compton.h @@ -563,6 +563,20 @@ clear_cache_win_leaders(session_t *ps) { static win * find_toplevel2(session_t *ps, Window wid); +/** + * Find matched window. + */ +static inline win * +find_win_all(session_t *ps, const Window wid) { + if (!wid || PointerRoot == wid || wid == ps->root || wid == ps->overlay) + return NULL; + + win *w = find_win(ps, wid); + if (!w) w = find_toplevel(ps, wid); + if (!w) w = find_toplevel2(ps, wid); + return w; +} + static Window win_get_leader_raw(session_t *ps, win *w, int recursions); @@ -589,7 +603,7 @@ group_is_focused(session_t *ps, Window leader) { for (win *w = ps->list; w; w = w->next) { if (win_get_leader(ps, w) == leader && !w->destroyed - && w->focused_real) + && win_is_focused_real(ps, w)) return true; } @@ -764,6 +778,9 @@ group_update_focused(session_t *ps, Window leader) { static inline void win_set_focused(session_t *ps, win *w, bool focused); +static void +win_on_focus_change(session_t *ps, win *w); + static void win_determine_fade(session_t *ps, win *w); diff --git a/dbus.c b/dbus.c index 299c5ac0b..1d20a061a 100644 --- a/dbus.c +++ b/dbus.c @@ -722,7 +722,11 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(window_type, cdbus_reply_enum); cdbus_m_win_get_do(wmwin, cdbus_reply_bool); cdbus_m_win_get_do(leader, cdbus_reply_wid); - cdbus_m_win_get_do(focused_real, cdbus_reply_bool); + // focused_real + if (!strcmp("focused_real", target)) { + cdbus_reply_bool(ps, msg, win_is_focused_real(ps, w)); + return true; + } cdbus_m_win_get_do(fade_force, cdbus_reply_enum); cdbus_m_win_get_do(shadow_force, cdbus_reply_enum); cdbus_m_win_get_do(focused_force, cdbus_reply_enum); From e0e726c4246f234b216ed79f22aa77e70560d178 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 24 Sep 2013 20:41:50 +0800 Subject: [PATCH 161/178] Misc: Add cfg file options for --unredir-if-possible-* Add configuration file options for --unredir-if-possible-{delay,exclude}. (#140) --- compton.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compton.c b/compton.c index 511083c1a..76a1c75cf 100644 --- a/compton.c +++ b/compton.c @@ -5192,6 +5192,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --unredir-if-possible lcfg_lookup_bool(&cfg, "unredir-if-possible", &ps->o.unredir_if_possible); + // --unredir-if-possible-delay + if (lcfg_lookup_int(&cfg, "unredir-if-possible-delay", &ival)) + ps->o.unredir_if_possible_delay = ival; // --inactive-dim-fixed lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &ps->o.inactive_dim_fixed); // --detect-transient @@ -5211,6 +5214,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude"); // --opacity-rule parse_cfg_condlst(ps, &cfg, &ps->o.opacity_rules, "opacity-rule"); + // --unredir-if-possible-exclude + parse_cfg_condlst(ps, &cfg, &ps->o.unredir_if_possible_blacklist, "unredir-if-possible-exclude"); // --blur-background lcfg_lookup_bool(&cfg, "blur-background", &ps->o.blur_background); // --blur-background-frame From 7f97d55da55d44a018de2fb065ec3d4f63ab0307 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 26 Sep 2013 07:41:11 +0800 Subject: [PATCH 162/178] Misc: Typo in vsync_opengl_init() I typed ps->glXGetVideoSyncSGI as ps->glXWaitVideoSyncSGI... --- compton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 76a1c75cf..dd9341cbc 100644 --- a/compton.c +++ b/compton.c @@ -5868,7 +5868,7 @@ vsync_opengl_init(session_t *ps) { return false; // Get video sync functions - if (!ps->glXWaitVideoSyncSGI) + if (!ps->glXGetVideoSyncSGI) ps->glXGetVideoSyncSGI = (f_GetVideoSync) glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI"); if (!ps->glXWaitVideoSyncSGI) From 70dfd1f58e94ae63eb8d035a35c4002dcd5cc14a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 1 Oct 2013 23:20:22 +0800 Subject: [PATCH 163/178] Bug fix #149: --opacity-rule misbehaves on 32-bit systems & others - Fix a bug that --opacity-rule misbehaves with a value higher than 50% on 32-bit systems. Thanks to mrinx for reporting. (#149) - Fix a bug that opacity-rule in configuration file does not work. --- compton.c | 27 +++++++++++++++++++++++---- compton.h | 5 +++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/compton.c b/compton.c index dd9341cbc..68577f1ec 100644 --- a/compton.c +++ b/compton.c @@ -2469,7 +2469,7 @@ static void win_update_opacity_rule(session_t *ps, win *w) { // If long is 32-bit, unfortunately there's no way could we express "unset", // so we just entirely don't distinguish "unset" and OPAQUE - long opacity = OPAQUE; + opacity_t opacity = OPAQUE; void *val = NULL; if (c2_matchd(ps, w, ps->o.opacity_rules, &w->cache_oparule, &val)) opacity = ((double) (long) val) / 100.0 * OPAQUE; @@ -5029,9 +5029,8 @@ parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, // Parse an array of options if (config_setting_is_array(setting)) { int i = config_setting_length(setting); - while (i--) { + while (i--) condlst_add(ps, pcondlst, config_setting_get_string_elem(setting, i)); - } } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { @@ -5040,6 +5039,26 @@ parse_cfg_condlst(session_t *ps, const config_t *pcfg, c2_lptr_t **pcondlst, } } +/** + * Parse an opacity rule list in configuration file. + */ +static inline void +parse_cfg_condlst_opct(session_t *ps, const config_t *pcfg, const char *name) { + config_setting_t *setting = config_lookup(pcfg, name); + if (setting) { + // Parse an array of options + if (config_setting_is_array(setting)) { + int i = config_setting_length(setting); + while (i--) + parse_rule_opacity(ps, config_setting_get_string_elem(setting, i)); + } + // Treat it as a single pattern if it's a string + else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { + parse_rule_opacity(ps, config_setting_get_string(setting)); + } + } +} + /** * Parse a configuration file from default location. */ @@ -5213,7 +5232,7 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { // --blur-background-exclude parse_cfg_condlst(ps, &cfg, &ps->o.blur_background_blacklist, "blur-background-exclude"); // --opacity-rule - parse_cfg_condlst(ps, &cfg, &ps->o.opacity_rules, "opacity-rule"); + parse_cfg_condlst_opct(ps, &cfg, "opacity-rule"); // --unredir-if-possible-exclude parse_cfg_condlst(ps, &cfg, &ps->o.unredir_if_possible_blacklist, "unredir-if-possible-exclude"); // --blur-background diff --git a/compton.h b/compton.h index 11325b75c..cb16111a8 100644 --- a/compton.h +++ b/compton.h @@ -476,9 +476,10 @@ win_has_frame(const win *w) { } static inline void -wid_set_opacity_prop(session_t *ps, Window wid, long val) { +wid_set_opacity_prop(session_t *ps, Window wid, opacity_t val) { + const unsigned long v = val; XChangeProperty(ps->dpy, wid, ps->atom_opacity, XA_CARDINAL, 32, - PropModeReplace, (unsigned char *) &val, 1); + PropModeReplace, (unsigned char *) &v, 1); } static inline void From 522923dbc7f99fe0ef4bd44c6e9f761addec9de2 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 21 Oct 2013 22:17:01 +0800 Subject: [PATCH 164/178] Bug fix #153: Possible fix for a rare timing issue Possible fix for a very rare timing issue in focus detection. Compton may fail to detect the currently focused window, when a window newly mapped gets focused, we failed to listen to events and get FocusIn from it in time, and a series of focus change events before it happens stay in the event queue and puzzled compton. My choice is to force focus recheck on all focus-related events. More roundtrips to X, but not necessarily worse performance, due to the high cost of focus flipping especially when there's a lot of conditions. Thanks to SlackBox for reporting. (#153) --- common.h | 1 + compton.c | 20 ++++++++------------ 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/common.h b/common.h index eadaafd95..f8531bcbd 100644 --- a/common.h +++ b/common.h @@ -30,6 +30,7 @@ // #define DEBUG_GLX_GLSL 1 // #define DEBUG_GLX_ERR 1 // #define DEBUG_GLX_MARK 1 +// #define DEBUG_GLX_PAINTREG 1 // #define MONITOR_REPAINT 1 // Whether to enable PCRE regular expression support in blacklists, enabled diff --git a/compton.c b/compton.c index 68577f1ec..0c9855281 100644 --- a/compton.c +++ b/compton.c @@ -781,6 +781,12 @@ recheck_focus(session_t *ps) { win *w = find_win_all(ps, wid); +#ifdef DEBUG_EVENTS + print_timestamp(ps); + printf_dbgf("(): %#010lx (%#010lx \"%s\") focused.\n", wid, + (w ? w->id: None), (w ? w->name: NULL)); +#endif + // And we set the focus state here if (w) { win_set_focused(ps, w, true); @@ -3815,12 +3821,7 @@ ev_focus_in(session_t *ps, XFocusChangeEvent *ev) { ev_focus_report(ev); #endif - if (!ev_focus_accept(ev)) - return; - - win *w = find_win_all(ps, ev->window); - if (w) - win_set_focused(ps, w, true); + recheck_focus(ps); } inline static void @@ -3829,12 +3830,7 @@ ev_focus_out(session_t *ps, XFocusChangeEvent *ev) { ev_focus_report(ev); #endif - if (!ev_focus_accept(ev)) - return; - - win *w = find_win_all(ps, ev->window); - if (w) - win_set_focused(ps, w, false); + recheck_focus(ps); } inline static void From ed99f1b2c9fe5c2ff5efe0af204b381aa9fdcc3e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sat, 9 Nov 2013 21:38:31 +0800 Subject: [PATCH 165/178] Misc: --write-pid-path & small changes - Add --write-pid-path to write process ID to a file, to help determining compton's process ID when -b is used. - Add a few extra targets to query through D-Bus opts_get (version, pid, etc.) and through win_get, primarily for debugging. - Add helper macros likely(), unlikely(), cmalloc(), ccalloc(), crealloc(). - Change w->opacity_set to opacity_t. Determine display_repr in session_init(), etc. --- common.h | 44 +++++++++++++++++++++++++++++------------ compton.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- dbus.c | 42 +++++++++++++++++++-------------------- dbus.h | 2 ++ 4 files changed, 110 insertions(+), 37 deletions(-) diff --git a/common.h b/common.h index f8531bcbd..469f8f1ae 100644 --- a/common.h +++ b/common.h @@ -60,6 +60,10 @@ #error Cannot enable c2 debugging without c2 support. #endif +#ifndef COMPTON_VERSION +#define COMPTON_VERSION "unknown" +#endif + // === Includes === // For some special functions @@ -132,6 +136,12 @@ #define MSTR_(s) #s #define MSTR(s) MSTR_(s) +/// @brief Wrapper for gcc branch prediction builtin, for likely branch. +#define likely(x) __builtin_expect(!!(x), 1) + +/// @brief Wrapper for gcc branch prediction builtin, for unlikely branch. +#define unlikely(x) __builtin_expect(!!(x), 0) + /// Print out an error message. #define printf_err(format, ...) \ fprintf(stderr, format "\n", ## __VA_ARGS__) @@ -431,9 +441,13 @@ typedef struct { // === General === /// The configuration file we used. char *config_file; + /// Path to write PID to. + char *write_pid_path; /// The display name we used. NULL means we are using the value of the /// DISPLAY environment variable. char *display; + /// Safe representation of display name. + char *display_repr; /// The backend in use. enum backend backend; /// Whether to avoid using stencil buffer under GLX backend. Might be @@ -986,7 +1000,7 @@ typedef struct _win { /// _NET_WM_OPACITY value opacity_t opacity_prop_client; /// Last window opacity value we set. - long opacity_set; + opacity_t opacity_set; // Fading-related members /// Do not fade if it's false. Change on window type change. @@ -1150,6 +1164,15 @@ allocchk_(const char *func_name, void *ptr) { /// @brief Wrapper of allocchk_(). #define allocchk(ptr) allocchk_(__func__, ptr) +/// @brief Wrapper of malloc(). +#define cmalloc(nmemb, type) ((type *) allocchk(malloc((nmemb) * sizeof(type)))) + +/// @brief Wrapper of calloc(). +#define ccalloc(nmemb, type) ((type *) allocchk(calloc((nmemb), sizeof(type)))) + +/// @brief Wrapper of ealloc(). +#define crealloc(ptr, nmemb, type) ((type *) allocchk(realloc((ptr), (nmemb) * sizeof(type)))) + /** * Return whether a struct timeval value is empty. */ @@ -1310,10 +1333,7 @@ print_timestamp(session_t *ps) { */ static inline char * mstrcpy(const char *src) { - char *str = malloc(sizeof(char) * (strlen(src) + 1)); - - if (!str) - printf_errfq(1, "(): Failed to allocate memory."); + char *str = cmalloc(strlen(src) + 1, char); strcpy(str, src); @@ -1325,10 +1345,7 @@ mstrcpy(const char *src) { */ static inline char * mstrncpy(const char *src, unsigned len) { - char *str = malloc(sizeof(char) * (len + 1)); - - if (!str) - printf_errfq(1, "(): Failed to allocate memory."); + char *str = cmalloc(len + 1, char); strncpy(str, src, len); str[len] = '\0'; @@ -1341,7 +1358,7 @@ mstrncpy(const char *src, unsigned len) { */ static inline char * mstrjoin(const char *src1, const char *src2) { - char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) + 1)); + char *str = cmalloc(strlen(src1) + strlen(src2) + 1, char); strcpy(str, src1); strcat(str, src2); @@ -1354,8 +1371,8 @@ mstrjoin(const char *src1, const char *src2) { */ static inline char * mstrjoin3(const char *src1, const char *src2, const char *src3) { - char *str = malloc(sizeof(char) * (strlen(src1) + strlen(src2) - + strlen(src3) + 1)); + char *str = cmalloc(strlen(src1) + strlen(src2) + + strlen(src3) + 1, char); strcpy(str, src1); strcat(str, src2); @@ -1369,7 +1386,8 @@ mstrjoin3(const char *src1, const char *src2, const char *src3) { */ static inline void mstrextend(char **psrc1, const char *src2) { - *psrc1 = realloc(*psrc1, (*psrc1 ? strlen(*psrc1): 0) + strlen(src2) + 1); + *psrc1 = crealloc(*psrc1, (*psrc1 ? strlen(*psrc1): 0) + strlen(src2) + 1, + char); strcat(*psrc1, src2); } diff --git a/compton.c b/compton.c index 0c9855281..770721596 100644 --- a/compton.c +++ b/compton.c @@ -1571,7 +1571,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, } } - double dopacity = get_opacity_percent(w);; + double dopacity = get_opacity_percent(w); if (!w->frame_opacity) { win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pcache_reg, pict); @@ -4309,6 +4309,8 @@ usage(int ret) { " Enable synchronous operation (for debugging).\n" "--config path\n" " Look for configuration file at the path.\n" + "--write-pid-path path\n" + " Write process ID to a file.\n" "--shadow-red value\n" " Red color value of shadow (0.0 - 1.0, defaults to 0).\n" "--shadow-green value\n" @@ -4584,7 +4586,7 @@ ostream_reopen(session_t *ps, const char *path) { /** * Fork program to background and disable all I/O streams. */ -static bool +static inline bool fork_after(session_t *ps) { if (getppid() == 1) return true; @@ -4627,6 +4629,26 @@ fork_after(session_t *ps) { return success; } +/** + * Write PID to a file. + */ +static inline bool +write_pid(session_t *ps) { + if (!ps->o.write_pid_path) + return true; + + FILE *f = fopen(ps->o.write_pid_path, "w"); + if (unlikely(!f)) { + printf_errf("(): Failed to write PID to \"%s\".", ps->o.write_pid_path); + return false; + } + + fprintf(f, "%ld\n", (long) getpid()); + fclose(f); + + return true; +} + /** * Parse a long number. */ @@ -5360,6 +5382,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "xinerama-shadow-crop", no_argument, NULL, 307 }, { "unredir-if-possible-exclude", required_argument, NULL, 308 }, { "unredir-if-possible-delay", required_argument, NULL, 309 }, + { "write-pid-path", required_argument, NULL, 310 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5603,6 +5626,10 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { condlst_add(ps, &ps->o.unredir_if_possible_blacklist, optarg); break; P_CASELONG(309, unredir_if_possible_delay); + case 310: + // --write-pid-path + ps->o.write_pid_path = mstrcpy(optarg); + break; default: usage(1); break; @@ -6800,6 +6827,27 @@ session_init(session_t *ps_old, int argc, char **argv) { ps->shape_exists = true; } + // Build a safe representation of display name + { + char *display_repr = DisplayString(ps->dpy); + if (!display_repr) + display_repr = "unknown"; + display_repr = mstrcpy(display_repr); + + // Convert all special characters in display_repr name to underscore + { + char *pdisp = display_repr; + + while (*pdisp) { + if (!isalnum(*pdisp)) + *pdisp = '_'; + ++pdisp; + } + } + + ps->o.display_repr = display_repr; + } + // Second pass get_cfg(ps, argc, argv, false); @@ -6974,6 +7022,8 @@ session_init(session_t *ps_old, int argc, char **argv) { } } + write_pid(ps); + // Free the old session if (ps_old) free(ps_old); @@ -7099,9 +7149,12 @@ session_destroy(session_t *ps) { free(ps->shadow_corner); free(ps->shadow_top); free(ps->gaussian_map); + + free(ps->o.config_file); + free(ps->o.write_pid_path); free(ps->o.display); + free(ps->o.display_repr); free(ps->o.logpath); - free(ps->o.config_file); for (int i = 0; i < MAX_BLUR_PASS; ++i) { free(ps->o.blur_kerns[i]); free(ps->blur_kerns_cache[i]); diff --git a/dbus.c b/dbus.c index 1d20a061a..8aec9ea82 100644 --- a/dbus.c +++ b/dbus.c @@ -39,30 +39,10 @@ cdbus_init(session_t *ps) { // Request service name { - // Get display name - char *display = DisplayString(ps->dpy); - if (!display) - display = "unknown"; - display = mstrcpy(display); - - // Convert all special characters in display name to underscore - { - char *pdisp = display; - - while (*pdisp) { - if (!isalnum(*pdisp)) - *pdisp = '_'; - ++pdisp; - } - } - // Build service name - char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", display); + char *service = mstrjoin3(CDBUS_SERVICE_NAME, ".", ps->o.display_repr); ps->dbus_service = service; - free(display); - display = NULL; - // Request for the name int ret = dbus_bus_request_name(ps->dbus_conn, service, DBUS_NAME_FLAG_DO_NOT_QUEUE, &err); @@ -735,7 +715,13 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg) { cdbus_m_win_get_do(class_instance, cdbus_reply_string); cdbus_m_win_get_do(class_general, cdbus_reply_string); cdbus_m_win_get_do(role, cdbus_reply_string); + cdbus_m_win_get_do(opacity, cdbus_reply_uint32); + cdbus_m_win_get_do(opacity_tgt, cdbus_reply_uint32); + cdbus_m_win_get_do(opacity_prop, cdbus_reply_uint32); + cdbus_m_win_get_do(opacity_prop_client, cdbus_reply_uint32); + cdbus_m_win_get_do(opacity_set, cdbus_reply_uint32); + cdbus_m_win_get_do(frame_opacity, cdbus_reply_double); cdbus_m_win_get_do(left_width, cdbus_reply_uint32); cdbus_m_win_get_do(right_width, cdbus_reply_uint32); @@ -889,6 +875,18 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { return true; \ } + // version + if (!strcmp("version", target)) { + cdbus_reply_string(ps, msg, COMPTON_VERSION); + return true; + } + + // pid + if (!strcmp("pid", target)) { + cdbus_reply_int32(ps, msg, getpid()); + return true; + } + // display if (!strcmp("display", target)) { cdbus_reply_string(ps, msg, DisplayString(ps->dpy)); @@ -896,6 +894,8 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg) { } cdbus_m_opts_get_do(config_file, cdbus_reply_string); + cdbus_m_opts_get_do(display_repr, cdbus_reply_string); + cdbus_m_opts_get_do(write_pid_path, cdbus_reply_string); cdbus_m_opts_get_do(mark_wmwin_focused, cdbus_reply_bool); cdbus_m_opts_get_do(mark_ovredir_focused, cdbus_reply_bool); cdbus_m_opts_get_do(fork_after_register, cdbus_reply_bool); diff --git a/dbus.h b/dbus.h index 50770d63c..a806c2de9 100644 --- a/dbus.h +++ b/dbus.h @@ -10,6 +10,8 @@ #include "common.h" #include +#include +#include #define CDBUS_SERVICE_NAME "com.github.chjj.compton" #define CDBUS_INTERFACE_NAME CDBUS_SERVICE_NAME From 640ef0437814dbc4df57a275cbadecb368ad0950 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 10 Nov 2013 10:13:18 +0800 Subject: [PATCH 166/178] Misc: Add properties to the registration window Add WM_CLASS, COMPTON_VERSION, and _NET_WM_PID properties to the registration window, to ease the development of pcman's compton-conf. --- compton.c | 27 +++++++++++++++++++++++++-- compton.h | 23 ++++++++++++++++++++--- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/compton.c b/compton.c index 770721596..7adfba179 100644 --- a/compton.c +++ b/compton.c @@ -4543,8 +4543,31 @@ register_cm(session_t *ps) { if (ps->redirected) XCompositeUnredirectWindow(ps->dpy, ps->reg_win, CompositeRedirectManual); - Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", - NULL, 0, NULL, NULL, NULL); + { + XClassHint *h = XAllocClassHint(); + if (h) { + h->res_name = "compton"; + h->res_class = "xcompmgr"; + } + Xutf8SetWMProperties(ps->dpy, ps->reg_win, "xcompmgr", "xcompmgr", + NULL, 0, NULL, NULL, h); + cxfree(h); + } + + // Set _NET_WM_PID + { + long pid = getpid(); + if (!XChangeProperty(ps->dpy, ps->reg_win, + get_atom(ps, "_NET_WM_PID"), XA_CARDINAL, 32, PropModeReplace, + (unsigned char *) &pid, 1)) { + printf_errf("(): Failed to set _NET_WM_PID."); + } + } + + // Set COMPTON_VERSION + if (!wid_set_text_prop(ps, ps->reg_win, get_atom(ps, "COMPTON_VERSION"), COMPTON_VERSION)) { + printf_errf("(): Failed to set COMPTON_VERSION."); + } { unsigned len = strlen(REGISTER_PROP) + 2; diff --git a/compton.h b/compton.h index cb16111a8..42ce5e7f2 100644 --- a/compton.h +++ b/compton.h @@ -337,9 +337,7 @@ isdamagenotify(session_t *ps, const XEvent *ev) { */ static inline XTextProperty * make_text_prop(session_t *ps, char *str) { - XTextProperty *pprop = malloc(sizeof(XTextProperty)); - if (!pprop) - printf_errfq(1, "(): Failed to allocate memory."); + XTextProperty *pprop = cmalloc(1, XTextProperty); if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) { cxfree(pprop->value); @@ -350,6 +348,25 @@ make_text_prop(session_t *ps, char *str) { return pprop; } + +/** + * Set a single-string text property on a window. + */ +static inline bool +wid_set_text_prop(session_t *ps, Window wid, Atom prop_atom, char *str) { + XTextProperty *pprop = make_text_prop(ps, str); + if (!pprop) { + printf_errf("(\"%s\"): Failed to make text property.", str); + return false; + } + + XSetTextProperty(ps->dpy, wid, pprop, prop_atom); + cxfree(pprop->value); + cxfree(pprop); + + return true; +} + static void run_fade(session_t *ps, win *w, unsigned steps); From 44a13b4e767eef1985a4a34723b7cc279e1dc160 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 10 Dec 2013 22:06:02 +0800 Subject: [PATCH 167/178] Feature: Add XRender-GLX hybird backend - Add new backend "xr_glx_hybird", which uses X Render for all compositing but GLX on the last step of rendering to screen. This makes GLX-backend-specific VSync methods usable while may avoid certain bugs with GLX backend. The idea comes from ali1234. Experimental. - GLX backend: Stop using or rendering to depth buffer. - Use glFinish() instead of glFlush() before VSync. It probably uses more CPU but could be more reliable than glFlush(). --- common.h | 26 ++++++++++++++-- compton.c | 89 ++++++++++++++++++++++++++++++++----------------------- compton.h | 26 ++++++++++------ opengl.c | 5 ++-- 4 files changed, 95 insertions(+), 51 deletions(-) diff --git a/common.h b/common.h index 469f8f1ae..6003cbf16 100644 --- a/common.h +++ b/common.h @@ -323,6 +323,7 @@ typedef enum { enum backend { BKEND_XRENDER, BKEND_GLX, + BKEND_XR_GLX_HYBIRD, NUM_BKEND, }; @@ -645,7 +646,7 @@ typedef struct { /// A Picture acting as the painting target. Picture tgt_picture; /// Temporary buffer to paint to before sending to display. - Picture tgt_buffer; + paint_t tgt_buffer; /// DBE back buffer for root window. Used in DBE painting mode. XdbeBackBuffer root_dbe; /// Window ID of the window we register as a symbol. @@ -1712,6 +1713,25 @@ find_toplevel(session_t *ps, Window id) { return NULL; } + +/** + * Check if current backend uses XRender for rendering. + */ +static inline bool +bkend_use_xrender(session_t *ps) { + return BKEND_XRENDER == ps->o.backend + || BKEND_XR_GLX_HYBIRD == ps->o.backend; +} + +/** + * Check if current backend uses GLX. + */ +static inline bool +bkend_use_glx(session_t *ps) { + return BKEND_GLX == ps->o.backend + || BKEND_XR_GLX_HYBIRD == ps->o.backend; +} + /** * Check if a window is really focused. */ @@ -2026,7 +2046,7 @@ free_texture(session_t *ps, glx_texture_t **pptex) { static inline void glx_mark_(session_t *ps, const char *func, XID xid, bool start) { #ifdef DEBUG_GLX_MARK - if (BKEND_GLX == ps->o.backend && ps->glStringMarkerGREMEDY) { + if (bkend_use_glx(ps) && ps->glStringMarkerGREMEDY) { if (!func) func = "(unknown)"; const char *postfix = (start ? " (start)": " (end)"); char *str = malloc((strlen(func) + 12 + 2 @@ -2047,7 +2067,7 @@ glx_mark_(session_t *ps, const char *func, XID xid, bool start) { static inline void glx_mark_frame(session_t *ps) { #ifdef DEBUG_GLX_MARK - if (BKEND_GLX == ps->o.backend && ps->glFrameTerminatorGREMEDY) + if (bkend_use_glx(ps) && ps->glFrameTerminatorGREMEDY) ps->glFrameTerminatorGREMEDY(); #endif } diff --git a/compton.c b/compton.c index 7adfba179..1281c02b7 100644 --- a/compton.c +++ b/compton.c @@ -47,6 +47,7 @@ const char * const VSYNC_STRS[NUM_VSYNC + 1] = { const char * const BACKEND_STRS[NUM_BKEND + 1] = { "xrender", // BKEND_XRENDER "glx", // BKEND_GLX + "xr_glx_hybird",// BKEND_XR_GLX_HYBIRD NULL }; @@ -1404,6 +1405,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, switch (ps->o.backend) { case BKEND_XRENDER: + case BKEND_XR_GLX_HYBIRD: { // Normalize blur kernels for (int i = 0; i < MAX_BLUR_PASS; ++i) { @@ -1476,12 +1478,13 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, XserverRegion reg_paint, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: + case BKEND_XR_GLX_HYBIRD: { Picture alpha_pict = get_alpha_pict_d(ps, opacity); if (alpha_pict != ps->alpha_picts[0]) { int op = ((!argb && !alpha_pict) ? PictOpSrc: PictOpOver); XRenderComposite(ps->dpy, op, pict, alpha_pict, - ps->tgt_buffer, x, y, 0, 0, dx, dy, wid, hei); + ps->tgt_buffer.pict, x, y, 0, 0, dx, dy, wid, hei); } break; } @@ -1511,7 +1514,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); } // XRender: Build picture - if (BKEND_XRENDER == ps->o.backend && !w->paint.pict) { + if (bkend_use_xrender(ps) && !w->paint.pict) { Drawable draw = w->paint.pixmap; if (!draw) draw = w->id; @@ -1547,7 +1550,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, Picture pict = w->paint.pict; // Invert window color, if required - if (BKEND_XRENDER == ps->o.backend && w->invert_color) { + if (bkend_use_xrender(ps) && w->invert_color) { Picture newpict = xr_build_picture(ps, wid, hei, w->pictfmt); if (newpict) { // Apply clipping region to save some CPU @@ -1571,7 +1574,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, } } - double dopacity = get_opacity_percent(w); + const double dopacity = get_opacity_percent(w); if (!w->frame_opacity) { win_render(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pcache_reg, pict); @@ -1640,6 +1643,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, switch (ps->o.backend) { case BKEND_XRENDER: + case BKEND_XR_GLX_HYBIRD: { unsigned short cval = 0xffff * dim_opacity; @@ -1655,8 +1659,8 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, .height = hei, }; - XRenderFillRectangles(ps->dpy, PictOpOver, ps->tgt_buffer, &color, - &rect, 1); + XRenderFillRectangles(ps->dpy, PictOpOver, ps->tgt_buffer.pict, + &color, &rect, 1); } break; #ifdef CONFIG_VSYNC_OPENGL @@ -1702,7 +1706,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XserverRegion reg_paint = None, reg_tmp = None, reg_tmp2 = None; #ifdef CONFIG_VSYNC_OPENGL - if (BKEND_GLX == ps->o.backend) { + if (bkend_use_glx(ps)) { glx_paint_pre(ps, ®ion); } #endif @@ -1717,27 +1721,29 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t #ifdef MONITOR_REPAINT // Note: MONITOR_REPAINT cannot work with DBE right now. - ps->tgt_buffer = ps->tgt_picture; + // Picture old_tgt_buffer = ps->tgt_buffer.pict; + ps->tgt_buffer.pict = ps->tgt_picture; #else - if (!ps->tgt_buffer) { + if (!paint_isvalid(ps, &ps->tgt_buffer)) { // DBE painting mode: Directly paint to a Picture of the back buffer - if (ps->o.dbe) { - ps->tgt_buffer = XRenderCreatePicture(ps->dpy, ps->root_dbe, + if (BKEND_XRENDER == ps->o.backend && ps->o.dbe) { + ps->tgt_buffer.pict = XRenderCreatePicture(ps->dpy, ps->root_dbe, XRenderFindVisualFormat(ps->dpy, ps->vis), 0, 0); } // No-DBE painting mode: Paint to an intermediate Picture then paint // the Picture to root window else { - Pixmap root_pixmap = XCreatePixmap( - ps->dpy, ps->root, ps->root_width, ps->root_height, - ps->depth); - - ps->tgt_buffer = XRenderCreatePicture(ps->dpy, root_pixmap, - XRenderFindVisualFormat(ps->dpy, ps->vis), - 0, 0); + if (!ps->tgt_buffer.pixmap) { + free_paint(ps, &ps->tgt_buffer); + ps->tgt_buffer.pixmap = XCreatePixmap(ps->dpy, ps->root, + ps->root_width, ps->root_height, ps->depth); + } - XFreePixmap(ps->dpy, root_pixmap); + if (BKEND_GLX != ps->o.backend) + ps->tgt_buffer.pict = XRenderCreatePicture(ps->dpy, + ps->tgt_buffer.pixmap, XRenderFindVisualFormat(ps->dpy, ps->vis), + 0, 0); } } #endif @@ -1753,6 +1759,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t ps->root_width, ps->root_height); break; case BKEND_GLX: + case BKEND_XR_GLX_HYBIRD: glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); @@ -1874,7 +1881,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t // Blur window background if (w->blur_background && (WMODE_SOLID != w->mode || (ps->o.blur_background_frame && w->frame_opacity))) { - win_blur_background(ps, w, ps->tgt_buffer, reg_paint, &cache_reg); + win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg); } // Painting the window @@ -1898,7 +1905,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XSync(ps->dpy, False); #ifdef CONFIG_VSYNC_OPENGL if (ps->glx_context) { - glFlush(); + glFinish(); glXWaitX(); } #endif @@ -1922,14 +1929,24 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XdbeSwapBuffers(ps->dpy, &swap_info, 1); } // No-DBE painting mode - else if (ps->tgt_buffer != ps->tgt_picture) { + else if (ps->tgt_buffer.pict != ps->tgt_picture) { XRenderComposite( - ps->dpy, PictOpSrc, ps->tgt_buffer, None, + ps->dpy, PictOpSrc, ps->tgt_buffer.pict, None, ps->tgt_picture, 0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height); } break; #ifdef CONFIG_VSYNC_OPENGL + case BKEND_XR_GLX_HYBIRD: + XSync(ps->dpy, False); + glFinish(); + glXWaitX(); + paint_bind_tex_real(ps, &ps->tgt_buffer, + ps->root_width, ps->root_height, ps->depth, + !ps->o.glx_no_rebind_pixmap); + glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, + ps->root_width, ps->root_height, 0, 1.0, false, region_real, NULL); + // No break here! case BKEND_GLX: if (ps->o.glx_use_copysubbuffermesa) glx_swap_copysubbuffermesa(ps, region_real); @@ -2937,10 +2954,7 @@ static void configure_win(session_t *ps, XConfigureEvent *ce) { // On root window changes if (ce->window == ps->root) { - if (ps->tgt_buffer) { - XRenderFreePicture(ps->dpy, ps->tgt_buffer); - ps->tgt_buffer = None; - } + free_paint(ps, &ps->tgt_buffer); ps->root_width = ce->width; ps->root_height = ce->height; @@ -4470,7 +4484,8 @@ usage(int ret) { " Crop shadow of a window fully on a particular Xinerama screen to the\n" " screen." WARNING "\n" "--backend backend\n" - " Choose backend. Possible choices are xrender and glx" WARNING ".\n" + " Choose backend. Possible choices are xrender, glx, and\n" + " xr_glx_hybird" WARNING ".\n" "--glx-no-stencil\n" " GLX backend: Avoid using stencil buffer. Might cause issues\n" " when rendering transparent content. My tests show a 15% performance\n" @@ -5982,7 +5997,7 @@ vsync_opengl_swc_init(session_t *ps) { if (!ensure_glx_context(ps)) return false; - if (BKEND_GLX != ps->o.backend) { + if (!bkend_use_glx(ps)) { printf_errf("(): I'm afraid glXSwapIntervalSGI wouldn't help if you are " "not using GLX backend. You could try, nonetheless."); } @@ -6010,7 +6025,7 @@ vsync_opengl_mswc_init(session_t *ps) { if (!ensure_glx_context(ps)) return false; - if (BKEND_GLX != ps->o.backend) { + if (!bkend_use_glx(ps)) { printf_errf("(): I'm afraid glXSwapIntervalMESA wouldn't help if you are " "not using GLX backend. You could try, nonetheless."); } @@ -6189,6 +6204,7 @@ init_filters(session_t *ps) { if (ps->o.blur_background || ps->o.blur_background_frame) { switch (ps->o.backend) { case BKEND_XRENDER: + case BKEND_XR_GLX_HYBIRD: { // Query filters XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps)); @@ -6578,7 +6594,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .root_tile_paint = PAINT_INIT, .screen_reg = None, .tgt_picture = None, - .tgt_buffer = None, + .tgt_buffer = PAINT_INIT, .root_dbe = None, .reg_win = None, .o = { @@ -6918,7 +6934,7 @@ session_init(session_t *ps_old, int argc, char **argv) { init_overlay(ps); // Initialize DBE - if (ps->o.dbe && BKEND_GLX == ps->o.backend) { + if (ps->o.dbe && BKEND_XRENDER != ps->o.backend) { printf_errf("(): DBE couldn't be used on GLX backend."); ps->o.dbe = false; } @@ -6927,7 +6943,7 @@ session_init(session_t *ps_old, int argc, char **argv) { exit(1); // Initialize OpenGL as early as possible - if (BKEND_GLX == ps->o.backend) { + if (bkend_use_glx(ps)) { #ifdef CONFIG_VSYNC_OPENGL if (!glx_init(ps, true)) exit(1); @@ -7150,10 +7166,8 @@ session_destroy(session_t *ps) { free_picture(ps, &ps->white_picture); // Free tgt_{buffer,picture} and root_picture - if (ps->tgt_buffer == ps->tgt_picture) - ps->tgt_buffer = None; - else - free_picture(ps, &ps->tgt_buffer); + if (ps->tgt_buffer.pict == ps->tgt_picture) + ps->tgt_buffer.pict = None; if (ps->tgt_picture == ps->root_picture) ps->tgt_picture = None; @@ -7161,6 +7175,7 @@ session_destroy(session_t *ps) { free_picture(ps, &ps->tgt_picture); free_picture(ps, &ps->root_picture); + free_paint(ps, &ps->tgt_buffer); // Free other X resources free_root_tile(ps); diff --git a/compton.h b/compton.h index 42ce5e7f2..8d1e90e21 100644 --- a/compton.h +++ b/compton.h @@ -219,7 +219,7 @@ paint_isvalid(session_t *ps, const paint_t *ppaint) { if (!ppaint) return false; - if (BKEND_XRENDER == ps->o.backend && !ppaint->pict) + if (bkend_use_xrender(ps) && !ppaint->pict) return false; #ifdef CONFIG_VSYNC_OPENGL @@ -229,25 +229,32 @@ paint_isvalid(session_t *ps, const paint_t *ppaint) { return true; } + /** * Bind texture in paint_t if we are using GLX backend. */ static inline bool -paint_bind_tex(session_t *ps, paint_t *ppaint, +paint_bind_tex_real(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, unsigned depth, bool force) { #ifdef CONFIG_VSYNC_OPENGL - if (BKEND_GLX == ps->o.backend) { - if (!ppaint->pixmap) - return false; + if (!ppaint->pixmap) + return false; - if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) - return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); - } + if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) + return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth); #endif return true; } +static inline bool +paint_bind_tex(session_t *ps, paint_t *ppaint, + unsigned wid, unsigned hei, unsigned depth, bool force) { + if (BKEND_GLX == ps->o.backend) + return paint_bind_tex_real(ps, ppaint, wid, hei, depth, force); + return true; +} + /** * Free data in a reg_data_t. */ @@ -679,7 +686,8 @@ static inline void set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: - XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer, 0, 0, reg); + case BKEND_XR_GLX_HYBIRD: + XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer.pict, 0, 0, reg); break; #ifdef CONFIG_VSYNC_OPENGL case BKEND_GLX: diff --git a/opengl.c b/opengl.c index 33a2fc724..d557aea63 100644 --- a/opengl.c +++ b/opengl.c @@ -124,7 +124,8 @@ glx_init(session_t *ps, bool need_render) { if (need_render) { glx_on_root_change(ps); - // glEnable(GL_DEPTH_TEST); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); @@ -880,7 +881,7 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDepthMask(GL_TRUE); + // glDepthMask(GL_TRUE); } cxfree(rects_free); From 43b47ec45346c895c5c555d9771fbdfbaca387ff Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 24 Dec 2013 07:46:48 +0800 Subject: [PATCH 168/178] Misc #152: Fix a spelling mistake Fix a spelling mistake (xr_glx_hybird -> xr_glx_hybrid). Thanks to cju for reporting. --- common.h | 11 ++++++++--- compton.c | 16 ++++++++-------- compton.h | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/common.h b/common.h index 6003cbf16..71ea8975b 100644 --- a/common.h +++ b/common.h @@ -323,7 +323,7 @@ typedef enum { enum backend { BKEND_XRENDER, BKEND_GLX, - BKEND_XR_GLX_HYBIRD, + BKEND_XR_GLX_HYBRID, NUM_BKEND, }; @@ -1491,6 +1491,11 @@ parse_backend(session_t *ps, const char *str) { ps->o.backend = i; return true; } + // Keep compatibility with an old revision containing a spelling mistake... + if (!strcasecmp(str, "xr_glx_hybird")) { + ps->o.backend = BKEND_XR_GLX_HYBRID; + return true; + } printf_errf("(\"%s\"): Invalid backend argument.", str); return false; } @@ -1720,7 +1725,7 @@ find_toplevel(session_t *ps, Window id) { static inline bool bkend_use_xrender(session_t *ps) { return BKEND_XRENDER == ps->o.backend - || BKEND_XR_GLX_HYBIRD == ps->o.backend; + || BKEND_XR_GLX_HYBRID == ps->o.backend; } /** @@ -1729,7 +1734,7 @@ bkend_use_xrender(session_t *ps) { static inline bool bkend_use_glx(session_t *ps) { return BKEND_GLX == ps->o.backend - || BKEND_XR_GLX_HYBIRD == ps->o.backend; + || BKEND_XR_GLX_HYBRID == ps->o.backend; } /** diff --git a/compton.c b/compton.c index 1281c02b7..95f6b77ce 100644 --- a/compton.c +++ b/compton.c @@ -47,7 +47,7 @@ const char * const VSYNC_STRS[NUM_VSYNC + 1] = { const char * const BACKEND_STRS[NUM_BKEND + 1] = { "xrender", // BKEND_XRENDER "glx", // BKEND_GLX - "xr_glx_hybird",// BKEND_XR_GLX_HYBIRD + "xr_glx_hybrid",// BKEND_XR_GLX_HYBRID NULL }; @@ -1405,7 +1405,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { // Normalize blur kernels for (int i = 0; i < MAX_BLUR_PASS; ++i) { @@ -1478,7 +1478,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, XserverRegion reg_paint, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { Picture alpha_pict = get_alpha_pict_d(ps, opacity); if (alpha_pict != ps->alpha_picts[0]) { @@ -1643,7 +1643,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { unsigned short cval = 0xffff * dim_opacity; @@ -1759,7 +1759,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t ps->root_width, ps->root_height); break; case BKEND_GLX: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); @@ -1937,7 +1937,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t } break; #ifdef CONFIG_VSYNC_OPENGL - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: XSync(ps->dpy, False); glFinish(); glXWaitX(); @@ -4485,7 +4485,7 @@ usage(int ret) { " screen." WARNING "\n" "--backend backend\n" " Choose backend. Possible choices are xrender, glx, and\n" - " xr_glx_hybird" WARNING ".\n" + " xr_glx_hybrid" WARNING ".\n" "--glx-no-stencil\n" " GLX backend: Avoid using stencil buffer. Might cause issues\n" " when rendering transparent content. My tests show a 15% performance\n" @@ -6204,7 +6204,7 @@ init_filters(session_t *ps) { if (ps->o.blur_background || ps->o.blur_background_frame) { switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { // Query filters XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps)); diff --git a/compton.h b/compton.h index 8d1e90e21..22ee195dd 100644 --- a/compton.h +++ b/compton.h @@ -686,7 +686,7 @@ static inline void set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer.pict, 0, 0, reg); break; #ifdef CONFIG_VSYNC_OPENGL From 0969d7d525ce8e053f9e8e7dd79540570307f477 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 26 Dec 2013 20:43:06 +0800 Subject: [PATCH 169/178] Misc #163: Make usage of glFinish() optional Make usage of glFinish() optional to avoid high CPU usage. (#163) --- common.h | 3 +++ compton.c | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index 71ea8975b..7786f82b8 100644 --- a/common.h +++ b/common.h @@ -513,6 +513,9 @@ typedef struct { bool dbe; /// Whether to do VSync aggressively. bool vsync_aggressive; + /// Whether to use glFinish() instead of glFlush() for (possibly) better + /// VSync yet probably higher CPU usage. + bool vsync_use_glfinish; // === Shadow === /// Enable/disable shadow for specific window types. diff --git a/compton.c b/compton.c index 95f6b77ce..3ddf46f4c 100644 --- a/compton.c +++ b/compton.c @@ -1905,7 +1905,10 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t XSync(ps->dpy, False); #ifdef CONFIG_VSYNC_OPENGL if (ps->glx_context) { - glFinish(); + if (ps->o.vsync_use_glfinish) + glFinish(); + else + glFlush(); glXWaitX(); } #endif @@ -1939,7 +1942,10 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t #ifdef CONFIG_VSYNC_OPENGL case BKEND_XR_GLX_HYBRID: XSync(ps->dpy, False); - glFinish(); + if (ps->o.vsync_use_glfinish) + glFinish(); + else + glFlush(); glXWaitX(); paint_bind_tex_real(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, ps->depth, @@ -5421,6 +5427,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "unredir-if-possible-exclude", required_argument, NULL, 308 }, { "unredir-if-possible-delay", required_argument, NULL, 309 }, { "write-pid-path", required_argument, NULL, 310 }, + { "vsync-use-glfinish", no_argument, NULL, 311 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5668,6 +5675,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { // --write-pid-path ps->o.write_pid_path = mstrcpy(optarg); break; + P_CASEBOOL(311, vsync_use_glfinish); default: usage(1); break; From 978dde64920e0e2357225cf0ca6e8865a44c7d7e Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 2 Jan 2014 12:33:57 -0600 Subject: [PATCH 170/178] Fix typo: s/hybird/hybrid/ --- common.h | 6 +++--- compton.c | 16 ++++++++-------- compton.h | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/common.h b/common.h index 6003cbf16..5ac37acba 100644 --- a/common.h +++ b/common.h @@ -323,7 +323,7 @@ typedef enum { enum backend { BKEND_XRENDER, BKEND_GLX, - BKEND_XR_GLX_HYBIRD, + BKEND_XR_GLX_HYBRID, NUM_BKEND, }; @@ -1720,7 +1720,7 @@ find_toplevel(session_t *ps, Window id) { static inline bool bkend_use_xrender(session_t *ps) { return BKEND_XRENDER == ps->o.backend - || BKEND_XR_GLX_HYBIRD == ps->o.backend; + || BKEND_XR_GLX_HYBRID == ps->o.backend; } /** @@ -1729,7 +1729,7 @@ bkend_use_xrender(session_t *ps) { static inline bool bkend_use_glx(session_t *ps) { return BKEND_GLX == ps->o.backend - || BKEND_XR_GLX_HYBIRD == ps->o.backend; + || BKEND_XR_GLX_HYBRID == ps->o.backend; } /** diff --git a/compton.c b/compton.c index 1281c02b7..95f6b77ce 100644 --- a/compton.c +++ b/compton.c @@ -47,7 +47,7 @@ const char * const VSYNC_STRS[NUM_VSYNC + 1] = { const char * const BACKEND_STRS[NUM_BKEND + 1] = { "xrender", // BKEND_XRENDER "glx", // BKEND_GLX - "xr_glx_hybird",// BKEND_XR_GLX_HYBIRD + "xr_glx_hybrid",// BKEND_XR_GLX_HYBRID NULL }; @@ -1405,7 +1405,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer, switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { // Normalize blur kernels for (int i = 0; i < MAX_BLUR_PASS; ++i) { @@ -1478,7 +1478,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, XserverRegion reg_paint, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { Picture alpha_pict = get_alpha_pict_d(ps, opacity); if (alpha_pict != ps->alpha_picts[0]) { @@ -1643,7 +1643,7 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { unsigned short cval = 0xffff * dim_opacity; @@ -1759,7 +1759,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t ps->root_width, ps->root_height); break; case BKEND_GLX: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); @@ -1937,7 +1937,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t } break; #ifdef CONFIG_VSYNC_OPENGL - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: XSync(ps->dpy, False); glFinish(); glXWaitX(); @@ -4485,7 +4485,7 @@ usage(int ret) { " screen." WARNING "\n" "--backend backend\n" " Choose backend. Possible choices are xrender, glx, and\n" - " xr_glx_hybird" WARNING ".\n" + " xr_glx_hybrid" WARNING ".\n" "--glx-no-stencil\n" " GLX backend: Avoid using stencil buffer. Might cause issues\n" " when rendering transparent content. My tests show a 15% performance\n" @@ -6204,7 +6204,7 @@ init_filters(session_t *ps) { if (ps->o.blur_background || ps->o.blur_background_frame) { switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: { // Query filters XFilters *pf = XRenderQueryFilters(ps->dpy, get_tgt_window(ps)); diff --git a/compton.h b/compton.h index 8d1e90e21..22ee195dd 100644 --- a/compton.h +++ b/compton.h @@ -686,7 +686,7 @@ static inline void set_tgt_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { switch (ps->o.backend) { case BKEND_XRENDER: - case BKEND_XR_GLX_HYBIRD: + case BKEND_XR_GLX_HYBRID: XFixesSetPictureClipRegion(ps->dpy, ps->tgt_buffer.pict, 0, 0, reg); break; #ifdef CONFIG_VSYNC_OPENGL From 96ea32f1792776c20041a2485719a9caa927f5de Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Sun, 19 Jan 2014 08:04:14 +0800 Subject: [PATCH 171/178] Bug fix #163: xr_glx_hybrid: Flickering issue xr_glx_hybrid backend: Attempt to fix flickering issue. Thanks to cju for testing. --- compton.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compton.c b/compton.c index 3ddf46f4c..f539daa2c 100644 --- a/compton.c +++ b/compton.c @@ -1950,6 +1950,13 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t paint_bind_tex_real(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, ps->depth, !ps->o.glx_no_rebind_pixmap); + // See #163 + XSync(ps->dpy, False); + if (ps->o.vsync_use_glfinish) + glFinish(); + else + glFlush(); + glXWaitX(); glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width, ps->root_height, 0, 1.0, false, region_real, NULL); // No break here! From 6078937c2c159c9e457cd8ebe0f964498b3a3476 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 21 Jan 2014 22:13:06 +0800 Subject: [PATCH 172/178] Bug fix: Fix access to freed memory due to invalid w->prev_trans - Fix a bug that w->prev_trans sometimes points to freed memory. Probably related to #165. - Add some more debugging printf()-s under DEBUG_EVENTS. --- compton.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index f539daa2c..3a14d82b7 100644 --- a/compton.c +++ b/compton.c @@ -1245,6 +1245,7 @@ paint_preprocess(session_t *ps, win *list) { t = w; } else { + assert(w->destroyed == (w->fade_callback == destroy_callback)); check_fade_fin(ps, w); } @@ -2089,6 +2090,10 @@ map_win(session_t *ps, Window id) { win *w = find_win(ps, id); +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx \"%s\"): %p\n", id, (w ? w->name: NULL), w); +#endif + // Don't care about window mapping if it's an InputOnly window // Try avoiding mapping a window twice if (!w || InputOnly == w->a.class @@ -2824,6 +2829,10 @@ add_win(session_t *ps, Window id, Window prev) { // Allocate and initialize the new win structure win *new = malloc(sizeof(win)); +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx): %p\n", id, new); +#endif + if (!new) { printf_errf("(%#010lx): Failed to allocate memory for the new window.", id); return false; @@ -3078,10 +3087,18 @@ circulate_win(session_t *ps, XCirculateEvent *ce) { static void finish_destroy_win(session_t *ps, Window id) { - win **prev, *w; + win **prev = NULL, *w = NULL; + +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx): Starting...\n", id); +#endif for (prev = &ps->list; (w = *prev); prev = &w->next) { if (w->id == id && w->destroyed) { +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx \"%s\"): %p\n", id, w->name, w); +#endif + finish_unmap_win(ps, w); *prev = w->next; @@ -3091,6 +3108,12 @@ finish_destroy_win(session_t *ps, Window id) { free_win_res(ps, w); + // Drop w from all prev_trans to avoid accessing freed memory in + // repair_win() + for (win *w2 = ps->list; w2; w2 = w2->next) + if (w == w2->prev_trans) + w2->prev_trans = NULL; + free(w); break; } @@ -3106,6 +3129,10 @@ static void destroy_win(session_t *ps, Window id) { win *w = find_win(ps, id); +#ifdef DEBUG_EVENTS + printf_dbgf("(%#010lx \"%s\"): %p\n", id, (w ? w->name: NULL), w); +#endif + if (w) { unmap_win(ps, w); From ecd5b3393f0e9af9a230f06294cb6131cebf69d6 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Thu, 27 Feb 2014 22:08:30 +0800 Subject: [PATCH 173/178] Bug fix: Fix -S Fix the broken -S. --- compton.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compton.c b/compton.c index 3a14d82b7..9d058767c 100644 --- a/compton.c +++ b/compton.c @@ -5480,6 +5480,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.config_file = mstrcpy(optarg); else if ('d' == o) ps->o.display = mstrcpy(optarg); + else if ('S' == o) + ps->o.synchronize = true; else if ('?' == o || ':' == o) usage(1); } @@ -5532,6 +5534,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { usage(0); break; case 'd': + case 'S': break; P_CASELONG('D', fade_delta); case 'I': @@ -5556,7 +5559,6 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'F': fading_enable = true; break; - P_CASEBOOL('S', synchronize); P_CASELONG('r', shadow_radius); case 'o': ps->o.shadow_opacity = atof(optarg); From f01576deb4f747f72e85795dcca8c43b43d22e5e Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Tue, 11 Mar 2014 07:22:23 +0800 Subject: [PATCH 174/178] Misc: xr-glx-hybrid alias & minor fixes - Add "xr-glx-hybrid" as an alias of "xr_glx_hybrid". (#163) - Clear damage history in expose_root() and when root window size changes. Unfortunately this doesn't fix #181. --- common.h | 14 ++++++++++++++ compton.c | 2 ++ 2 files changed, 16 insertions(+) diff --git a/common.h b/common.h index 7786f82b8..e87651c9e 100644 --- a/common.h +++ b/common.h @@ -1499,6 +1499,11 @@ parse_backend(session_t *ps, const char *str) { ps->o.backend = BKEND_XR_GLX_HYBRID; return true; } + // cju wants to use dashes + if (!strcasecmp(str, "xr-glx-hybrid")) { + ps->o.backend = BKEND_XR_GLX_HYBRID; + return true; + } printf_errf("(\"%s\"): Invalid backend argument.", str); return false; } @@ -1788,6 +1793,15 @@ free_region(session_t *ps, XserverRegion *p) { } } +/** + * Free all regions in ps->all_damage_last . + */ +static inline void +free_all_damage_last(session_t *ps) { + for (int i = 0; i < CGLX_MAX_BUFFER_AGE; ++i) + free_region(ps, &ps->all_damage_last[i]); +} + /** * Crop a rectangle by another rectangle. * diff --git a/compton.c b/compton.c index 9d058767c..645aa5c57 100644 --- a/compton.c +++ b/compton.c @@ -2983,6 +2983,7 @@ configure_win(session_t *ps, XConfigureEvent *ce) { rebuild_screen_reg(ps); rebuild_shadow_exclude_reg(ps); + free_all_damage_last(ps); #ifdef CONFIG_VSYNC_OPENGL if (BKEND_GLX == ps->o.backend) @@ -3275,6 +3276,7 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { static void expose_root(session_t *ps, XRectangle *rects, int nrects) { + free_all_damage_last(ps); XserverRegion region = XFixesCreateRegion(ps->dpy, rects, nrects); add_damage(ps, region); } From 224dcd29cc383a61c85140897424b07f905f2c95 Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Mon, 17 Mar 2014 23:25:34 +0800 Subject: [PATCH 175/178] Bug fix #181: Add --xrender-sync{,-fence} - Add --xrender-sync{,-fence} to deal with redraw lag issue on GLX backend. --xrender-sync-fence requires a sufficiently new xorg-server and libXext. NO_XSYNC=1 may be used to disable it at compile time. Thanks to tchebb for reporting and everybody else for testing. (#181) - A bit code clean-up. Replace a few XSync() with XFlush() to minimize the latency. --- common.h | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++- compton.c | 119 ++++++++++++++++++++++++++++--------- compton.h | 11 +++- opengl.c | 127 +++++++++++++++++++++++++++++++++------- opengl.h | 2 + 5 files changed, 379 insertions(+), 51 deletions(-) diff --git a/common.h b/common.h index e87651c9e..d243dc077 100644 --- a/common.h +++ b/common.h @@ -55,11 +55,19 @@ // #define CONFIG_DBUS 1 // Whether to enable condition support. // #define CONFIG_C2 1 +// Whether to enable X Sync support. +// #define CONFIG_XSYNC 1 +// Whether to enable GLX Sync support. +// #define CONFIG_GLX_XSYNC 1 #if !defined(CONFIG_C2) && defined(DEBUG_C2) #error Cannot enable c2 debugging without c2 support. #endif +#if (!defined(CONFIG_XSYNC) || !defined(CONFIG_VSYNC_OPENGL)) && defined(CONFIG_GLX_SYNC) +#error Cannot enable GL sync without X Sync / OpenGL support. +#endif + #ifndef COMPTON_VERSION #define COMPTON_VERSION "unknown" #endif @@ -89,6 +97,9 @@ #include #include #include +#ifdef CONFIG_XSYNC +#include +#endif #ifdef CONFIG_XINERAMA #include @@ -338,6 +349,16 @@ enum { typedef struct _glx_texture glx_texture_t; #ifdef CONFIG_VSYNC_OPENGL +#ifdef DEBUG_GLX_DEBUG_CONTEXT +typedef GLXContext (*f_glXCreateContextAttribsARB) (Display *dpy, + GLXFBConfig config, GLXContext share_context, Bool direct, + const int *attrib_list); +typedef void (*GLDEBUGPROC) (GLenum source, GLenum type, + GLuint id, GLenum severity, GLsizei length, const GLchar* message, + GLvoid* userParam); +typedef void (*f_DebugMessageCallback) (GLDEBUGPROC, void *userParam); +#endif + typedef int (*f_WaitVideoSync) (int, int, unsigned *); typedef int (*f_GetVideoSync) (unsigned *); @@ -352,6 +373,47 @@ typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, in typedef void (*f_CopySubBuffer) (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height); +#ifdef CONFIG_GLX_SYNC +// Looks like duplicate typedef of the same type is safe? +typedef int64_t GLint64; +typedef uint64_t GLuint64; +typedef struct __GLsync *GLsync; + +#ifndef GL_SYNC_FLUSH_COMMANDS_BIT +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#endif + +#ifndef GL_TIMEOUT_IGNORED +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +#ifndef GL_ALREADY_SIGNALED +#define GL_ALREADY_SIGNALED 0x911A +#endif + +#ifndef GL_TIMEOUT_EXPIRED +#define GL_TIMEOUT_EXPIRED 0x911B +#endif + +#ifndef GL_CONDITION_SATISFIED +#define GL_CONDITION_SATISFIED 0x911C +#endif + +#ifndef GL_WAIT_FAILED +#define GL_WAIT_FAILED 0x911D +#endif + +typedef GLsync (*f_FenceSync) (GLenum condition, GLbitfield flags); +typedef GLboolean (*f_IsSync) (GLsync sync); +typedef void (*f_DeleteSync) (GLsync sync); +typedef GLenum (*f_ClientWaitSync) (GLsync sync, GLbitfield flags, + GLuint64 timeout); +typedef void (*f_WaitSync) (GLsync sync, GLbitfield flags, + GLuint64 timeout); +typedef GLsync (*f_ImportSyncEXT) (GLenum external_sync_type, + GLintptr external_sync, GLbitfield flags); +#endif + #ifdef DEBUG_GLX_MARK typedef void (*f_StringMarkerGREMEDY) (GLsizei len, const void *string); typedef void (*f_FrameTerminatorGREMEDY) (void); @@ -438,7 +500,7 @@ struct _win; typedef struct _c2_lptr c2_lptr_t; /// Structure representing all options. -typedef struct { +typedef struct _options_t { // === General === /// The configuration file we used. char *config_file; @@ -451,6 +513,11 @@ typedef struct { char *display_repr; /// The backend in use. enum backend backend; + /// Whether to sync X drawing to avoid certain delay issues with + /// GLX backend. + bool xrender_sync; + /// Whether to sync X drawing with X Sync fence. + bool xrender_sync_fence; /// Whether to avoid using stencil buffer under GLX backend. Might be /// unsafe. bool glx_no_stencil; @@ -617,7 +684,7 @@ typedef struct { } options_t; /// Structure containing all necessary data for a compton session. -typedef struct { +typedef struct _session_t { // === Display related === /// Display in use. Display *dpy; @@ -650,6 +717,9 @@ typedef struct { Picture tgt_picture; /// Temporary buffer to paint to before sending to display. paint_t tgt_buffer; +#ifdef CONFIG_XSYNC + XSyncFence tgt_buffer_fence; +#endif /// DBE back buffer for root window. Used in DBE painting mode. XdbeBackBuffer root_dbe; /// Window ID of the window we register as a symbol. @@ -783,6 +853,20 @@ typedef struct { f_ReleaseTexImageEXT glXReleaseTexImageProc; /// Pointer to glXCopySubBufferMESA function. f_CopySubBuffer glXCopySubBufferProc; +#ifdef CONFIG_GLX_SYNC + /// Pointer to the glFenceSync() function. + f_FenceSync glFenceSyncProc; + /// Pointer to the glIsSync() function. + f_IsSync glIsSyncProc; + /// Pointer to the glDeleteSync() function. + f_DeleteSync glDeleteSyncProc; + /// Pointer to the glClientWaitSync() function. + f_ClientWaitSync glClientWaitSyncProc; + /// Pointer to the glWaitSync() function. + f_WaitSync glWaitSyncProc; + /// Pointer to the glImportSyncEXT() function. + f_ImportSyncEXT glImportSyncEXT; +#endif #ifdef DEBUG_GLX_MARK /// Pointer to StringMarkerGREMEDY function. f_StringMarkerGREMEDY glStringMarkerGREMEDY; @@ -849,6 +933,14 @@ typedef struct { XserverRegion *xinerama_scr_regs; /// Number of Xinerama screens. int xinerama_nscrs; +#endif +#ifdef CONFIG_XSYNC + /// Whether X Sync extension exists. + bool xsync_exists; + /// Event base number for X Sync extension. + int xsync_event; + /// Error base number for X Sync extension. + int xsync_error; #endif /// Whether X Render convolution filter exists. bool xrfilter_convolution_exists; @@ -915,6 +1007,10 @@ typedef struct _win { winmode_t mode; /// Whether the window has been damaged at least once. bool damaged; +#ifdef CONFIG_XSYNC + /// X Sync fence of drawable. + XSyncFence fence; +#endif /// Whether the window was damaged after last paint. bool pixmap_damaged; /// Damage of the window. @@ -1802,6 +1898,20 @@ free_all_damage_last(session_t *ps) { free_region(ps, &ps->all_damage_last[i]); } +#ifdef CONFIG_XSYNC +/** + * Free a XSync fence. + */ +static inline void +free_fence(session_t *ps, XSyncFence *pfence) { + if (*pfence) + XSyncDestroyFence(ps->dpy, *pfence); + *pfence = None; +} +#else +#define free_fence(ps, pfence) ((void) 0) +#endif + /** * Crop a rectangle by another rectangle. * @@ -1927,6 +2037,11 @@ vsync_deinit(session_t *ps); */ ///@{ +#ifdef CONFIG_GLX_SYNC +void +xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence); +#endif + bool glx_init(session_t *ps, bool need_render); @@ -2096,6 +2211,58 @@ glx_mark_frame(session_t *ps) { ///@} +#ifdef CONFIG_XSYNC +#define xr_sync(ps, d, pfence) xr_sync_(ps, d, pfence) +#else +#define xr_sync(ps, d, pfence) xr_sync_(ps, d) +#endif + +/** + * Synchronizes a X Render drawable to ensure all pending painting requests + * are completed. + */ +static inline void +xr_sync_(session_t *ps, Drawable d +#ifdef CONFIG_XSYNC + , XSyncFence *pfence +#endif + ) { + if (!ps->o.xrender_sync) + return; + +#ifdef CONFIG_XSYNC + if (ps->o.xrender_sync_fence && ps->xsync_exists) { + // TODO: If everybody just follows the rules stated in X Sync prototype, + // we need only one fence per screen, but let's stay a bit cautious right + // now + XSyncFence tmp_fence = None; + if (!pfence) + pfence = &tmp_fence; + assert(pfence); + if (!*pfence) + *pfence = XSyncCreateFence(ps->dpy, d, False); + if (*pfence) { + Bool triggered = False; + // The fence may fail to be created (e.g. because of died drawable) + assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || !triggered); + XSyncTriggerFence(ps->dpy, *pfence); + XSyncAwaitFence(ps->dpy, pfence, 1); + assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || triggered); + } + else { + printf_errf("(%#010lx): Failed to create X Sync fence.", d); + } + free_fence(ps, &tmp_fence); + if (*pfence) + XSyncResetFence(ps->dpy, *pfence); + } +#endif + XSync(ps->dpy, False); +#ifdef CONFIG_GLX_SYNC + xr_glx_sync(ps, d, pfence); +#endif +} + /** @name DBus handling */ ///@{ diff --git a/compton.c b/compton.c index 645aa5c57..1efb05d62 100644 --- a/compton.c +++ b/compton.c @@ -489,6 +489,9 @@ win_build_shadow(session_t *ps, win *w, double opacity) { w->shadow_paint.pixmap = shadow_pixmap_argb; w->shadow_paint.pict = shadow_picture_argb; + // Sync it once and only once + xr_sync(ps, w->shadow_paint.pixmap, NULL); + bool success = paint_bind_tex(ps, &w->shadow_paint, shadow_image->width, shadow_image->height, 32, true); XFreeGC(ps->dpy, gc); @@ -1513,12 +1516,16 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, if (!w->paint.pixmap && ps->has_name_pixmap) { set_ignore_next(ps); w->paint.pixmap = XCompositeNameWindowPixmap(ps->dpy, w->id); + if (w->paint.pixmap) + free_fence(ps, &w->fence); } + + Drawable draw = w->paint.pixmap; + if (!draw) + draw = w->id; + // XRender: Build picture if (bkend_use_xrender(ps) && !w->paint.pict) { - Drawable draw = w->paint.pixmap; - if (!draw) - draw = w->id; { XRenderPictureAttributes pa = { .subwindow_mode = IncludeInferiors, @@ -1528,6 +1535,10 @@ win_paint_win(session_t *ps, win *w, XserverRegion reg_paint, CPSubwindowMode, &pa); } } + + if (IsViewable == w->a.map_state) + xr_sync(ps, draw, &w->fence); + // GLX: Build texture // Let glx_bind_pixmap() determine pixmap size, because if the user // is resizing windows, the width and height we get may not be up-to-date, @@ -1948,11 +1959,13 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t else glFlush(); glXWaitX(); + assert(ps->tgt_buffer.pixmap); + xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence); paint_bind_tex_real(ps, &ps->tgt_buffer, ps->root_width, ps->root_height, ps->depth, !ps->o.glx_no_rebind_pixmap); // See #163 - XSync(ps->dpy, False); + xr_sync(ps, ps->tgt_buffer.pixmap, &ps->tgt_buffer_fence); if (ps->o.vsync_use_glfinish) glFinish(); else @@ -2116,7 +2129,7 @@ map_win(session_t *ps, Window id) { } // Make sure the XSelectInput() requests are sent - XSync(ps->dpy, False); + XFlush(ps->dpy); // Update window mode here to check for ARGB windows win_determine_mode(ps, w); @@ -2205,7 +2218,7 @@ finish_unmap_win(session_t *ps, win *w) { w->extents = None; } - free_paint(ps, &w->paint); + free_wpaint(ps, w); free_region(ps, &w->border_size); free_paint(ps, &w->shadow_paint); } @@ -2219,6 +2232,11 @@ static void unmap_win(session_t *ps, win *w) { if (!w || IsUnmapped == w->a.map_state) return; + // One last synchronization + if (w->paint.pixmap) + xr_sync(ps, w->paint.pixmap, &w->fence); + free_fence(ps, &w->fence); + // Set focus out win_set_focused(ps, w, false); @@ -2654,7 +2672,7 @@ win_mark_client(session_t *ps, win *w, Window client) { determine_evmask(ps, client, WIN_EVMODE_CLIENT)); // Make sure the XSelectInput() requests are sent - XSync(ps->dpy, False); + XFlush(ps->dpy); win_upd_wintype(ps, w); @@ -3037,7 +3055,7 @@ configure_win(session_t *ps, XConfigureEvent *ce) { if (w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) - free_paint(ps, &w->paint); + free_wpaint(ps, w); if (w->a.width != ce->width || w->a.height != ce->height || w->a.border_width != ce->border_width) { @@ -3097,7 +3115,7 @@ finish_destroy_win(session_t *ps, Window id) { for (prev = &ps->list; (w = *prev); prev = &w->next) { if (w->id == id && w->destroyed) { #ifdef DEBUG_EVENTS - printf_dbgf("(%#010lx \"%s\"): %p\n", id, w->name, w); + printf_dbgf("(%#010lx \"%s\"): %p\n", id, w->name, w); #endif finish_unmap_win(ps, w); @@ -3188,10 +3206,10 @@ damage_win(session_t *ps, XDamageNotifyEvent *de) { * Xlib error handler function. */ static int -error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { +xerror(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { session_t * const ps = ps_g; - int o; + int o = 0; const char *name = "Unknown"; if (should_ignore(ps, ev->serial)) { @@ -3240,6 +3258,17 @@ error(Display __attribute__((unused)) *dpy, XErrorEvent *ev) { } #endif +#ifdef CONFIG_XSYNC + if (ps->xsync_exists) { + o = ev->error_code - ps->xsync_error; + switch (o) { + CASESTRRET2(XSyncBadCounter); + CASESTRRET2(XSyncBadAlarm); + CASESTRRET2(XSyncBadFence); + } + } +#endif + switch (ev->error_code) { CASESTRRET2(BadAccess); CASESTRRET2(BadAlloc); @@ -3771,18 +3800,27 @@ ev_name(session_t *ps, XEvent *ev) { CASESTRRET(Expose); CASESTRRET(PropertyNotify); CASESTRRET(ClientMessage); - default: - if (isdamagenotify(ps, ev)) - return "Damage"; + } - if (ps->shape_exists && ev->type == ps->shape_event) { - return "ShapeNotify"; - } + if (isdamagenotify(ps, ev)) + return "Damage"; - sprintf(buf, "Event %d", ev->type); + if (ps->shape_exists && ev->type == ps->shape_event) + return "ShapeNotify"; - return buf; +#ifdef CONFIG_XSYNC + if (ps->xsync_exists) { + int o = ev->type - ps->xsync_event; + switch (o) { + CASESTRRET(CounterNotify); + CASESTRRET(AlarmNotify); + } } +#endif + + sprintf(buf, "Event %d", ev->type); + + return buf; } static Window @@ -5464,6 +5502,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "unredir-if-possible-delay", required_argument, NULL, 309 }, { "write-pid-path", required_argument, NULL, 310 }, { "vsync-use-glfinish", no_argument, NULL, 311 }, + { "xrender-sync", no_argument, NULL, 312 }, + { "xrender-sync-fence", no_argument, NULL, 313 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5714,6 +5754,8 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { ps->o.write_pid_path = mstrcpy(optarg); break; P_CASEBOOL(311, vsync_use_glfinish); + P_CASEBOOL(312, xrender_sync); + P_CASEBOOL(313, xrender_sync_fence); default: usage(1); break; @@ -5761,6 +5803,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { if (ps->o.blur_background_frame) ps->o.blur_background = true; + if (ps->o.xrender_sync_fence) + ps->o.xrender_sync = true; + // Other variables determined by options // Determine whether we need to track focus changes @@ -6478,7 +6523,7 @@ redir_stop(session_t *ps) { // If we don't destroy them here, looks like the resources are just // kept inaccessible somehow for (win *w = ps->list; w; w = w->next) - free_paint(ps, &w->paint); + free_wpaint(ps, w); XCompositeUnredirectSubwindows(ps->dpy, ps->root, CompositeRedirectManual); // Unmap overlay window @@ -6852,7 +6897,7 @@ session_init(session_t *ps_old, int argc, char **argv) { } } - XSetErrorHandler(error); + XSetErrorHandler(xerror); if (ps->o.synchronize) { XSynchronize(ps->dpy, 1); } @@ -6907,11 +6952,6 @@ session_init(session_t *ps_old, int argc, char **argv) { exit(1); } - // Query X Shape - if (XShapeQueryExtension(ps->dpy, &ps->shape_event, &ps->shape_error)) { - ps->shape_exists = true; - } - // Build a safe representation of display name { char *display_repr = DisplayString(ps->dpy); @@ -6936,6 +6976,32 @@ session_init(session_t *ps_old, int argc, char **argv) { // Second pass get_cfg(ps, argc, argv, false); + // Query X Shape + if (XShapeQueryExtension(ps->dpy, &ps->shape_event, &ps->shape_error)) { + ps->shape_exists = true; + } + + if (ps->o.xrender_sync_fence) { +#ifdef CONFIG_XSYNC + // Query X Sync + if (XSyncQueryExtension(ps->dpy, &ps->xsync_event, &ps->xsync_error)) { + // TODO: Fencing may require version >= 3.0? + int major_version_return = 0, minor_version_return = 0; + if (XSyncInitialize(ps->dpy, &major_version_return, &minor_version_return)) + ps->xsync_exists = true; + } + if (!ps->xsync_exists) { + printf_errf("(): X Sync extension not found. No X Sync fence sync is " + "possible."); + exit(1); + } +#else + printf_errf("(): X Sync support not compiled in. --xrender-sync-fence" + "can't work."); + exit(1); +#endif + } + // Query X RandR if ((ps->o.sw_opti && !ps->o.refresh_rate) || ps->o.xinerama_shadow_crop) { if (XRRQueryExtension(ps->dpy, &ps->randr_event, &ps->randr_error)) @@ -7219,6 +7285,7 @@ session_destroy(session_t *ps) { ps->tgt_picture = None; else free_picture(ps, &ps->tgt_picture); + free_fence(ps, &ps->tgt_buffer_fence); free_picture(ps, &ps->root_picture); free_paint(ps, &ps->tgt_buffer); diff --git a/compton.h b/compton.h index 22ee195dd..a4e838efb 100644 --- a/compton.h +++ b/compton.h @@ -275,6 +275,15 @@ free_paint(session_t *ps, paint_t *ppaint) { free_pixmap(ps, &ppaint->pixmap); } +/** + * Free w->paint. + */ +static inline void +free_wpaint(session_t *ps, win *w) { + free_paint(ps, &w->paint); + free_fence(ps, &w->fence); +} + /** * Destroy all resources in a struct _win. */ @@ -883,7 +892,7 @@ static void damage_win(session_t *ps, XDamageNotifyEvent *de); static int -error(Display *dpy, XErrorEvent *ev); +xerror(Display *dpy, XErrorEvent *ev); static void expose_root(session_t *ps, XRectangle *rects, int nrects); diff --git a/opengl.c b/opengl.c index d557aea63..39aac33c2 100644 --- a/opengl.c +++ b/opengl.c @@ -10,6 +10,50 @@ #include "opengl.h" +#ifdef CONFIG_GLX_SYNC +void +xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence) { + if (*pfence) { + // GLsync sync = ps->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + GLsync sync = ps->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); + XSync(ps->dpy, False); + glx_check_err(ps); + /* GLenum ret = ps->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, + 1000); + assert(GL_CONDITION_SATISFIED == ret); */ + ps->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); + // ps->glDeleteSyncProc(sync); + // XSyncResetFence(ps->dpy, *pfence); + } + glx_check_err(ps); +} +#endif + +static inline GLXFBConfig +get_fbconfig_from_visualinfo(session_t *ps, const XVisualInfo *visualinfo) { + int nelements = 0; + GLXFBConfig *fbconfigs = glXGetFBConfigs(ps->dpy, visualinfo->screen, + &nelements); + for (int i = 0; i < nelements; ++i) { + int visual_id = 0; + if (Success == glXGetFBConfigAttrib(ps->dpy, fbconfigs[i], GLX_VISUAL_ID, &visual_id) + && visual_id == visualinfo->visualid) + return fbconfigs[i]; + } + + return NULL; +} + +#ifdef DEBUG_GLX_DEBUG_CONTEXT +static void +glx_debug_msg_callback(GLenum source, GLenum type, + GLuint id, GLenum severity, GLsizei length, const GLchar *message, + GLvoid *userParam) { + printf_dbgf("(): source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"\n", + source, type, id, severity, message); +} +#endif + /** * Initialize OpenGL. */ @@ -56,7 +100,33 @@ glx_init(session_t *ps, bool need_render) { if (!ps->glx_context) { // Get GLX context +#ifndef DEBUG_GLX_DEBUG_CONTEXT ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); +#else + { + GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); + if (!fbconfig) { + printf_errf("(): Failed to get GLXFBConfig for root visual %#lx.", + pvis->visualid); + goto glx_init_end; + } + + f_glXCreateContextAttribsARB p_glXCreateContextAttribsARB = + (f_glXCreateContextAttribsARB) + glXGetProcAddress((const GLubyte *) "glXCreateContextAttribsARB"); + if (!p_glXCreateContextAttribsARB) { + printf_errf("(): Failed to get glXCreateContextAttribsARB()."); + goto glx_init_end; + } + + static const int attrib_list[] = { + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, + None + }; + ps->glx_context = p_glXCreateContextAttribsARB(ps->dpy, fbconfig, NULL, + GL_TRUE, attrib_list); + } +#endif if (!ps->glx_context) { printf_errf("(): Failed to get GLX context."); @@ -68,6 +138,20 @@ glx_init(session_t *ps, bool need_render) { printf_errf("(): Failed to attach GLX context."); goto glx_init_end; } + +#ifdef DEBUG_GLX_DEBUG_CONTEXT + { + f_DebugMessageCallback p_DebugMessageCallback = + (f_DebugMessageCallback) + glXGetProcAddress((const GLubyte *) "glDebugMessageCallback"); + if (!p_DebugMessageCallback) { + printf_errf("(): Failed to get glDebugMessageCallback(0."); + goto glx_init_end; + } + p_DebugMessageCallback(glx_debug_msg_callback, ps); + } +#endif + } // Ensure we have a stencil buffer. X Fixes does not guarantee rectangles @@ -114,6 +198,27 @@ glx_init(session_t *ps, bool need_render) { goto glx_init_end; } } + +#ifdef CONFIG_GLX_SYNC + ps->glFenceSyncProc = (f_FenceSync) + glXGetProcAddress((const GLubyte *) "glFenceSync"); + ps->glIsSyncProc = (f_IsSync) + glXGetProcAddress((const GLubyte *) "glIsSync"); + ps->glDeleteSyncProc = (f_DeleteSync) + glXGetProcAddress((const GLubyte *) "glDeleteSync"); + ps->glClientWaitSyncProc = (f_ClientWaitSync) + glXGetProcAddress((const GLubyte *) "glClientWaitSync"); + ps->glWaitSyncProc = (f_WaitSync) + glXGetProcAddress((const GLubyte *) "glWaitSync"); + ps->glImportSyncEXT = (f_ImportSyncEXT) + glXGetProcAddress((const GLubyte *) "glImportSyncEXT"); + if (!ps->glFenceSyncProc || !ps->glIsSyncProc || !ps->glDeleteSyncProc + || !ps->glClientWaitSyncProc || !ps->glWaitSyncProc + || !ps->glImportSyncEXT) { + printf_errf("(): Failed to acquire GLX sync functions."); + goto glx_init_end; + } +#endif } // Acquire FBConfigs @@ -344,9 +449,7 @@ glx_init_blur(session_t *ps) { } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; #else @@ -655,9 +758,7 @@ glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, glBindTexture(ptex->target, 0); glDisable(ptex->target); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; } @@ -680,9 +781,7 @@ glx_release_pixmap(session_t *ps, glx_texture_t *ptex) { ptex->glpixmap = 0; } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -803,9 +902,7 @@ glx_paint_pre(session_t *ps, XserverRegion *preg) { glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL); #endif -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -886,9 +983,7 @@ glx_set_clip(session_t *ps, XserverRegion reg, const reg_data_t *pcache_reg) { cxfree(rects_free); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } #define P_PAINTREG_START() \ @@ -1174,9 +1269,7 @@ glx_blur_dst_end: free_glx_bc(ps, pbc); } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return ret; } @@ -1212,9 +1305,7 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z, glColor4f(0.0f, 0.0f, 0.0f, 0.0f); glDisable(GL_BLEND); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; } @@ -1412,9 +1503,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glActiveTexture(GL_TEXTURE0); } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif return true; } @@ -1452,9 +1541,7 @@ glx_render_color(session_t *ps, int dx, int dy, int width, int height, int z, } glColor4f(0.0f, 0.0f, 0.0f, 0.0f); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -1492,9 +1579,7 @@ glx_render_dots(session_t *ps, int dx, int dy, int width, int height, int z, } glColor4f(0.0f, 0.0f, 0.0f, 0.0f); -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif } /** @@ -1524,9 +1609,7 @@ glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg) { } } -#ifdef DEBUG_GLX_ERR glx_check_err(ps); -#endif cxfree(rects); } diff --git a/opengl.h b/opengl.h index 564b7e20a..8628e36d3 100644 --- a/opengl.h +++ b/opengl.h @@ -59,6 +59,8 @@ glx_check_err_(session_t *ps, const char *func, int line) { } #define glx_check_err(ps) glx_check_err_(ps, __func__, __LINE__) +#else +#define glx_check_err(ps) ((void) 0) #endif /** From 6e4f45f875ccc0f29b93b0b36ef34dfdc8f8394a Mon Sep 17 00:00:00 2001 From: Richard Grenville Date: Wed, 26 Mar 2014 22:27:25 +0800 Subject: [PATCH 176/178] Misc: Add --xrender-sync{,-fence} as configuration file option - Add --xrender-sync{,-fence} as configuration file option. - Quit on encountering invalid opacity rule. - Other small changes. --- common.h | 4 +++- compton.c | 22 ++++++++++++++++++++-- opengl.c | 4 ++-- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/common.h b/common.h index d243dc077..525b9a96b 100644 --- a/common.h +++ b/common.h @@ -2230,6 +2230,7 @@ xr_sync_(session_t *ps, Drawable d if (!ps->o.xrender_sync) return; + XSync(ps->dpy, False); #ifdef CONFIG_XSYNC if (ps->o.xrender_sync_fence && ps->xsync_exists) { // TODO: If everybody just follows the rules stated in X Sync prototype, @@ -2243,6 +2244,8 @@ xr_sync_(session_t *ps, Drawable d *pfence = XSyncCreateFence(ps->dpy, d, False); if (*pfence) { Bool triggered = False; + /* if (XSyncQueryFence(ps->dpy, *pfence, &triggered) && triggered) + XSyncResetFence(ps->dpy, *pfence); */ // The fence may fail to be created (e.g. because of died drawable) assert(!XSyncQueryFence(ps->dpy, *pfence, &triggered) || !triggered); XSyncTriggerFence(ps->dpy, *pfence); @@ -2257,7 +2260,6 @@ xr_sync_(session_t *ps, Drawable d XSyncResetFence(ps->dpy, *pfence); } #endif - XSync(ps->dpy, False); #ifdef CONFIG_GLX_SYNC xr_glx_sync(ps, d, pfence); #endif diff --git a/compton.c b/compton.c index 1efb05d62..1da926b5d 100644 --- a/compton.c +++ b/compton.c @@ -4596,6 +4596,18 @@ usage(int ret) { "--glx-use-gpushader4\n" " GLX backend: Use GL_EXT_gpu_shader4 for some optimization on blur\n" " GLSL code. My tests on GTX 670 show no noticeable effect.\n" + "--xrender-sync\n" + " Attempt to synchronize client applications' draw calls with XSync(),\n" + " used on GLX backend to ensure up-to-date window content is painted.\n" +#undef WARNING +#ifndef CONFIG_XSYNC +#define WARNING WARNING_DISABLED +#else +#define WARNING +#endif + "--xrender-sync-fence\n" + " Additionally use X Sync fence to sync clients' draw calls. Needed\n" + " on nvidia-drivers with GLX backend for some users." WARNING "\n" #undef WARNING #ifndef CONFIG_DBUS #define WARNING WARNING_DISABLED @@ -5186,7 +5198,9 @@ parse_cfg_condlst_opct(session_t *ps, const config_t *pcfg, const char *name) { if (config_setting_is_array(setting)) { int i = config_setting_length(setting); while (i--) - parse_rule_opacity(ps, config_setting_get_string_elem(setting, i)); + if (!parse_rule_opacity(ps, config_setting_get_string_elem(setting, + i))) + exit(1); } // Treat it as a single pattern if it's a string else if (CONFIG_TYPE_STRING == config_setting_type(setting)) { @@ -5399,6 +5413,10 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { exit(1); // --glx-use-gpushader4 lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &ps->o.glx_use_gpushader4); + // --xrender-sync + lcfg_lookup_bool(&cfg, "xrender-sync", &ps->o.xrender_sync); + // --xrender-sync-fence + lcfg_lookup_bool(&cfg, "xrender-sync-fence", &ps->o.xrender_sync_fence); // Wintype settings { wintype_t i; @@ -6996,7 +7014,7 @@ session_init(session_t *ps_old, int argc, char **argv) { exit(1); } #else - printf_errf("(): X Sync support not compiled in. --xrender-sync-fence" + printf_errf("(): X Sync support not compiled in. --xrender-sync-fence " "can't work."); exit(1); #endif diff --git a/opengl.c b/opengl.c index 39aac33c2..951d8e50a 100644 --- a/opengl.c +++ b/opengl.c @@ -16,11 +16,11 @@ xr_glx_sync(session_t *ps, Drawable d, XSyncFence *pfence) { if (*pfence) { // GLsync sync = ps->glFenceSyncProc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); GLsync sync = ps->glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, *pfence, 0); - XSync(ps->dpy, False); - glx_check_err(ps); /* GLenum ret = ps->glClientWaitSyncProc(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000); assert(GL_CONDITION_SATISFIED == ret); */ + XSyncTriggerFence(ps->dpy, *pfence); + XFlush(ps->dpy); ps->glWaitSyncProc(sync, 0, GL_TIMEOUT_IGNORED); // ps->glDeleteSyncProc(sync); // XSyncResetFence(ps->dpy, *pfence); From fbd3a27d6e0998f6052e93ce60f68c5261b38d2d Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Mon, 31 Mar 2014 23:12:37 -0500 Subject: [PATCH 177/178] Add required functionality for use with TDE --- c2.h | 4 +- common.h | 33 ++++- compton.c | 405 ++++++++++++++++++++++++++++++++++++++++++++++++++---- compton.h | 6 + opengl.c | 38 ++++- 5 files changed, 454 insertions(+), 32 deletions(-) diff --git a/c2.h b/c2.h index 3c56e2fa7..129a5e739 100644 --- a/c2.h +++ b/c2.h @@ -35,10 +35,10 @@ typedef struct _c2_l c2_l_t; /// Pointer to a condition tree. typedef struct { bool isbranch : 1; - union { +// union { c2_b_t *b; c2_l_t *l; - }; +// }; } c2_ptr_t; /// Initializer for c2_ptr_t. diff --git a/common.h b/common.h index 525b9a96b..275b7b671 100644 --- a/common.h +++ b/common.h @@ -4,6 +4,7 @@ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey + * Copyright (c) 2014 Timothy Pearson * See LICENSE for more information. * */ @@ -60,6 +61,11 @@ // Whether to enable GLX Sync support. // #define CONFIG_GLX_XSYNC 1 +// TDE specific options +// #define USE_ENV_HOME 1 +#define WRITE_PID_FILE 1 +#define _TDE_COMP_MGR_VERSION_ 3.00 + #if !defined(CONFIG_C2) && defined(DEBUG_C2) #error Cannot enable c2 debugging without c2 support. #endif @@ -599,7 +605,7 @@ typedef struct _options_t { c2_lptr_t *shadow_blacklist; /// Whether bounding-shaped window should be ignored. bool shadow_ignore_shaped; - /// Whether to respect _COMPTON_SHADOW. + /// Whether to respect _TDE_WM_WINDOW_SHADOW. bool respect_prop_shadow; /// Whether to crop shadow to the very Xinerama screen. bool xinerama_shadow_crop; @@ -615,6 +621,8 @@ typedef struct _options_t { time_ms_t fade_delta; /// Whether to disable fading on window open/close. bool no_fading_openclose; + /// Whether to disable fading on opacity change + bool no_fading_opacitychange; /// Fading blacklist. A linked list of conditions. c2_lptr_t *fade_blacklist; @@ -967,10 +975,14 @@ typedef struct _session_t { Atom atom_client_leader; /// Atom of property _NET_ACTIVE_WINDOW. Atom atom_ewmh_active_win; - /// Atom of property _COMPTON_SHADOW. + /// Atom of property _TDE_WM_WINDOW_SHADOW. Atom atom_compton_shadow; /// Atom of property _NET_WM_WINDOW_TYPE. Atom atom_win_type; + /// Atom of property _KDE_TRANSPARENT_TO_BLACK. + Atom atom_win_type_tde_transparent_to_black; + /// Atom of property _KDE_TRANSPARENT_TO_DESKTOP. + Atom atom_win_type_tde_transparent_to_desktop; /// Array of atoms of all possible window types. Atom atoms_wintypes[NUM_WINTYPES]; /// Linked list of additional atoms to track. @@ -1132,9 +1144,11 @@ typedef struct _win { int shadow_width; /// Height of shadow. Affected by window size and commandline argument. int shadow_height; + /// Relative size of shadow. + int shadow_size; /// Picture to render shadow. Affected by window size. paint_t shadow_paint; - /// The value of _COMPTON_SHADOW attribute of the window. Below 0 for + /// The value of _TDE_WM_WINDOW_SHADOW attribute of the window. Below 0 for /// none. long prop_shadow; @@ -1151,6 +1165,12 @@ typedef struct _win { /// Whether to blur window background. bool blur_background; + /// Whether to show black background + bool show_black_background; + + /// Whether to show desktop background + bool show_root_tile; + #ifdef CONFIG_VSYNC_OPENGL_GLSL /// Textures and FBO background blur use. glx_blur_cache_t glx_blur_cache; @@ -2094,6 +2114,10 @@ glx_render(session_t *ps, const glx_texture_t *ptex, double opacity, bool neg, XserverRegion reg_tgt, const reg_data_t *pcache_reg); +bool +glx_render_specified_color(session_t *ps, int color, int dx, int dy, int width, int height, int z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg); + void glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); @@ -2320,6 +2344,9 @@ opts_init_track_focus(session_t *ps); void opts_set_no_fading_openclose(session_t *ps, bool newval); + +void +opts_set_no_fading_opacitychange(session_t *ps, bool newval); //!@} #endif diff --git a/compton.c b/compton.c index 1da926b5d..123703a19 100644 --- a/compton.c +++ b/compton.c @@ -4,6 +4,7 @@ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey + * Copyright (c) 2014 Timothy Pearson * See LICENSE for more information. * */ @@ -94,6 +95,140 @@ const static char *background_props_str[] = { /// have a pointer to current session passed in. session_t *ps_g = NULL; +// === Execution control === + +struct sigaction usr_action; +sigset_t block_mask; + +int my_exit_code = 3; + +void write_pid_file(pid_t pid) +{ +#ifdef WRITE_PID_FILE +#ifdef USE_ENV_HOME + const char *home = getenv("HOME"); +#else + const char *home; + struct passwd *p; + p = getpwuid(getuid()); + if (p) + home = p->pw_dir; + else + home = getenv("HOME"); +#endif + const char *filename; + const char *configfile = "/.compton-tde.pid"; + int n = strlen(home)+strlen(configfile)+1; + filename = (char*)malloc(n*sizeof(char)); + memset(filename,0,n); + strcat(filename, home); + strcat(filename, configfile); + + printf("writing '%s' as pidfile\n\n", filename); + + /* now that we did all that by way of introduction...write the file! */ + FILE *pFile; + char buffer[255]; + sprintf(buffer, "%d", pid); + pFile = fopen(filename, "w"); + if (pFile) { + fwrite(buffer,1,strlen(buffer), pFile); + fclose(pFile); + } + + free(filename); + filename = NULL; +#endif +} + +void delete_pid_file() +{ +#ifdef WRITE_PID_FILE +#ifdef USE_ENV_HOME + const char *home = getenv("HOME"); +#else + const char *home; + struct passwd *p; + p = getpwuid(getuid()); + if (p) + home = p->pw_dir; + else + home = getenv("HOME"); +#endif + const char *filename; + const char *configfile = "/.compton-tde.pid"; + int n = strlen(home)+strlen(configfile)+1; + filename = (char*)malloc(n*sizeof(char)); + memset(filename,0,n); + strcat(filename, home); + strcat(filename, configfile); + + printf("deleting '%s' as pidfile\n\n", filename); + + /* now that we did all that by way of introduction...delete the file! */ + unlink(filename); + + free(filename); + filename = NULL; +#endif + +#if WORK_AROUND_FGLRX + if ((my_exit_code == 3) && (restartOnSigterm)) { + printf("compton-tde lost connection to X server, restarting...\n"); fflush(stdout); + sleep(1); + char me[2048]; + int chars = readlink("/proc/self/exe", me, sizeof(me)); + me[chars] = 0; + me[2047] = 0; + execl(me, basename(me), (char*)NULL); + } +#endif +} + +void handle_siguser (int sig) +{ + int uidnum; + if (sig == SIGTERM) { + my_exit_code=0; + delete_pid_file(); + exit(0); + } + if (sig == SIGUSR1) { + char newuid[1024]; +#ifndef NDEBUG + printf("Enter the new user ID:\n"); fflush(stdout); +#endif + char *eof; + newuid[0] = '\0'; + newuid[sizeof(newuid)-1] = '\0'; + eof = fgets(newuid, sizeof(newuid), stdin); + uidnum = atoi(newuid); +#ifndef NDEBUG + printf("Setting compton-tde process uid to %d...\n", uidnum); fflush(stdout); +#endif + + my_exit_code=4; + delete_pid_file(); + my_exit_code=3; + setuid(uidnum); + write_pid_file(getpid()); + + } + else { + uidnum = getuid(); + } + if ((sig == SIGUSR1) || (sig == SIGUSR2)) { + get_cfg(ps_g, 0, 0, false); /* reload the configuration file */ + + /* set background/shadow picture using the new settings */ + ps_g->cshadow_picture = solid_picture(ps_g, true, 1, ps_g->o.shadow_red, ps_g->o.shadow_green, ps_g->o.shadow_blue); + + /* regenerate shadows using the new settings */ + init_alpha_picts(ps_g); + init_filters(ps_g); + } +} + // === Fading === /** @@ -800,6 +935,12 @@ recheck_focus(session_t *ps) { return NULL; } +static Bool +determine_window_transparent_to_black(const session_t *ps, Window w); + +static Bool +determine_window_transparent_to_desktop(const session_t *ps, Window w); + static bool get_root_tile(session_t *ps) { /* @@ -1890,6 +2031,45 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t reg_data_t cache_reg = REG_DATA_INIT; if (!is_region_empty(ps, reg_paint, &cache_reg)) { set_tgt_clip(ps, reg_paint, &cache_reg); + + /* Here we redraw the background of the transparent window if we want + to do anything special (i.e. anything other than showing the + windows and desktop prestacked behind of the window). + For example, if you want to blur the background or show another + background pixmap entirely here is the place to do it; simply + draw the new background onto ps->tgt_buffer.pict before continuing! */ + switch (ps->o.backend) { + case BKEND_XRENDER: + case BKEND_XR_GLX_HYBRID: + { + if (w->show_root_tile) { + XRenderComposite (ps->dpy, PictOpSrc, ps->root_tile_paint.pict, None, ps->tgt_buffer.pict, + w->a.x, w->a.y, w->a.x, w->a.y, + w->a.x, w->a.y, w->widthb, w->heightb); + } + else if (w->show_black_background) { + XRenderComposite (ps->dpy, PictOpSrc, ps->black_picture, None, ps->tgt_buffer.pict, + w->a.x, w->a.y, w->a.x, w->a.y, + w->a.x, w->a.y, w->widthb, w->heightb); + } + break; + } +#ifdef CONFIG_VSYNC_OPENGL + case BKEND_GLX: + { + if (w->show_root_tile) { + glx_render(ps, ps->root_tile_paint.ptex, w->a.x, w->a.y, w->a.x, w->a.y, w->widthb, w->heightb, + ps->glx_z, 1.0, false, reg_paint, &cache_reg); + } + else if (w->show_black_background) { + glx_render_specified_color(ps, 0, w->a.x, w->a.y, w->widthb, w->heightb, + ps->glx_z, reg_paint, &cache_reg); + } + break; + } +#endif + } + // Blur window background if (w->blur_background && (WMODE_SOLID != w->mode || (ps->o.blur_background_frame && w->frame_opacity))) { @@ -2131,6 +2311,11 @@ map_win(session_t *ps, Window id) { // Make sure the XSelectInput() requests are sent XFlush(ps->dpy); + /* This needs to be here since we don't get PropertyNotify when unmapped */ + w->opacity = wid_get_opacity_prop(ps, w->id, OPAQUE); + w->show_root_tile = determine_window_transparent_to_desktop(ps, id); + w->show_black_background = determine_window_transparent_to_black(ps, id); + // Update window mode here to check for ARGB windows win_determine_mode(ps, w); @@ -2165,7 +2350,7 @@ map_win(session_t *ps, Window id) { win_update_opacity_prop(ps, w); w->flags |= WFLAG_OPCT_CHANGE; - // Check for _COMPTON_SHADOW + // Check for _TDE_WM_WINDOW_SHADOW if (ps->o.respect_prop_shadow) win_update_prop_shadow_raw(ps, w); @@ -2283,6 +2468,125 @@ get_opacity_percent(win *w) { return ((double) w->opacity) / OPAQUE; } +static Bool +get_window_transparent_to_desktop(const session_t *ps, Window w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data; + int result = XGetWindowProperty (ps->dpy, w, ps->atom_win_type_tde_transparent_to_desktop, 0L, 1L, False, + XA_ATOM, &actual, &format, + &n, &left, &data); + + if (result == Success && data != None && format == 32 ) + { + Atom a; + a = *(long*)data; + XFree ( (void *) data); + return True; + } + return False; +} + +static Bool +get_window_transparent_to_black(const session_t *ps, Window w) +{ + Atom actual; + int format; + unsigned long n, left; + + unsigned char *data; + int result = XGetWindowProperty (ps->dpy, w, ps->atom_win_type_tde_transparent_to_black, 0L, 1L, False, + XA_ATOM, &actual, &format, + &n, &left, &data); + + if (result == Success && data != None && format == 32 ) + { + Atom a; + a = *(long*)data; + XFree ( (void *) data); + return True; + } + return False; +} + +static Bool +determine_window_transparent_to_desktop (const session_t *ps, Window w) +{ + Window root_return, parent_return; + Window *children = NULL; + unsigned int nchildren, i; + Bool type; + + type = get_window_transparent_to_desktop (ps, w); + if (type == True) { + return True; + } + + if (!XQueryTree (ps->dpy, w, &root_return, &parent_return, &children, + &nchildren)) + { + /* XQueryTree failed. */ + if (children) + XFree ((void *)children); + return False; + } + + for (i = 0;i < nchildren;i++) + { + type = determine_window_transparent_to_desktop (ps, children[i]); + if (type == True) + return True; + } + + if (children) + XFree ((void *)children); + + return False; +} + +static Bool +determine_window_transparent_to_black (const session_t *ps, Window w) +{ + Window root_return, parent_return; + Window *children = NULL; + unsigned int nchildren, i; + Bool type; + Bool ret = False; + + type = get_window_transparent_to_black (ps, w); + if (type == True) { + return True; + } + + if (!XQueryTree (ps->dpy, w, &root_return, &parent_return, &children, + &nchildren)) + { + /* XQueryTree failed. */ + if (children) { + XFree ((void *)children); + } + return False; + } + + for (i = 0;i < nchildren;i++) + { + type = determine_window_transparent_to_black (ps, children[i]); + if (type == True) { + ret = True; + break; + } + } + + if (children) { + XFree ((void *)children); + } + + return ret; +} + static void win_determine_mode(session_t *ps, win *w) { winmode_t mode = WMODE_SOLID; @@ -2375,7 +2679,8 @@ win_determine_fade(session_t *ps, win *w) { if (UNSET != w->fade_force) w->fade = w->fade_force; else if ((ps->o.no_fading_openclose && w->in_openclose) - || win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst)) + || win_match(ps, w, ps->o.fade_blacklist, &w->cache_fblst) + || (ps->o.no_fading_opacitychange && (!w->in_openclose))) w->fade = false; else w->fade = ps->o.wintype_fade[w->window_type]; @@ -2416,7 +2721,7 @@ win_update_shape(session_t *ps, win *w) { } /** - * Reread _COMPTON_SHADOW property from a window. + * Reread _TDE_WM_WINDOW_SHADOW property from a window. * * The property must be set on the outermost window, usually the WM frame. */ @@ -2436,7 +2741,7 @@ win_update_prop_shadow_raw(session_t *ps, win *w) { } /** - * Reread _COMPTON_SHADOW property from a window and update related + * Reread _TDE_WM_WINDOW_SHADOW property from a window and update related * things. */ static void @@ -2497,7 +2802,7 @@ win_determine_invert_color(session_t *ps, win *w) { if (UNSET != w->invert_color_force) w->invert_color = w->invert_color_force; - else + else w->invert_color = win_match(ps, w, ps->o.invert_color_list, &w->cache_ivclst); if (w->invert_color != invert_color_old) @@ -2621,8 +2926,8 @@ calc_win_size(session_t *ps, win *w) { */ static void calc_shadow_geometry(session_t *ps, win *w) { - w->shadow_dx = ps->o.shadow_offset_x; - w->shadow_dy = ps->o.shadow_offset_y; + w->shadow_dx = ps->o.shadow_offset_x * w->shadow_size; + w->shadow_dy = ps->o.shadow_offset_y * w->shadow_size; w->shadow_width = w->widthb + ps->gaussian_map->size; w->shadow_height = w->heightb + ps->gaussian_map->size; } @@ -2828,6 +3133,7 @@ add_win(session_t *ps, Window id, Window prev) { .shadow_dy = 0, .shadow_width = 0, .shadow_height = 0, + .shadow_size = 100, .shadow_paint = PAINT_INIT, .prop_shadow = -1, @@ -2837,6 +3143,9 @@ add_win(session_t *ps, Window id, Window prev) { .invert_color_force = UNSET, .blur_background = false, + + .show_black_background = false, + .show_root_tile = false, }; // Reject overlay window and already added windows @@ -3772,6 +4081,19 @@ opts_set_no_fading_openclose(session_t *ps, bool newval) { } } +/** + * Set no_fading_opacitychange option. + */ +void +opts_set_no_fading_opacitychange(session_t *ps, bool newval) { + if (newval != ps->o.no_fading_opacitychange) { + ps->o.no_fading_opacitychange = newval; + for (win *w = ps->list; w; w = w->next) + win_determine_fade(ps, w); + ps->ev_received = true; + } +} + //!@} #endif @@ -4160,7 +4482,7 @@ ev_property_notify(session_t *ps, XPropertyEvent *ev) { } } - // If _COMPTON_SHADOW changes + // If _TDE_WM_WINDOW_SHADOW changes if (ps->o.respect_prop_shadow && ps->atom_compton_shadow == ev->atom) { win *w = find_win(ps, ev->window); if (w) @@ -4401,6 +4723,8 @@ usage(int ret) { " Daemonize process.\n" "-S\n" " Enable synchronous operation (for debugging).\n" + "-v\n" + " Print version Number and exit\\n" "--config path\n" " Look for configuration file at the path.\n" "--write-pid-path path\n" @@ -4427,6 +4751,8 @@ usage(int ret) { " Mark windows that have no WM frame as active.\n" "--no-fading-openclose\n" " Do not fade on window open/close.\n" + "--no-fading-opacitychange\n" + " Do not fade on window opacity change.\n" "--shadow-ignore-shaped\n" " Do not paint shadows on shaped windows.\n" "--detect-rounded-corners\n" @@ -4486,7 +4812,7 @@ usage(int ret) { " Use _NET_WM_ACTIVE_WINDOW on the root window to determine which\n" " window is focused instead of using FocusIn/Out events.\n" "--respect-prop-shadow\n" - " Respect _COMPTON_SHADOW. This a prototype-level feature, which\n" + " Respect _TDE_WM_WINDOW_SHADOW. This a prototype-level feature, which\n" " you must not rely on.\n" "--unredir-if-possible\n" " Unredirect all windows if a full-screen opaque window is\n" @@ -4685,10 +5011,18 @@ register_cm(session_t *ps) { s /= 10; } + Window w; + Atom a; + static char net_wm_cm[] = "_NET_WM_CM_Sxx"; + + snprintf (net_wm_cm, sizeof (net_wm_cm), "_NET_WM_CM_S%d", ps->scr); + a = XInternAtom (ps->dpy, net_wm_cm, False); + char *buf = malloc(len); snprintf(buf, len, REGISTER_PROP "%d", ps->scr); buf[len - 1] = '\0'; - XSetSelectionOwner(ps->dpy, get_atom(ps, buf), ps->reg_win, 0); + // setting this causes compton to abort on TDE login + // XSetSelectionOwner(ps->dpy, get_atom(ps, buf), ps->reg_win, 0); free(buf); } @@ -4828,7 +5162,7 @@ parse_matrix(session_t *ps, const char *src, const char **endptr) { int wid = 0, hei = 0; const char *pc = NULL; XFixed *matrix = NULL; - + // Get matrix width and height { double val = 0.0; @@ -5090,8 +5424,8 @@ parse_rule_opacity(session_t *ps, const char *src) { */ static FILE * open_config_file(char *cpath, char **ppath) { - const static char *config_filename = "/compton.conf"; - const static char *config_filename_legacy = "/.compton.conf"; + const static char *config_filename = "/compton-tde.conf"; + const static char *config_filename_legacy = "/.compton-tde.conf"; const static char *config_home_suffix = "/.config"; const static char *config_system_dir = "/etc/xdg"; @@ -5307,6 +5641,8 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { wintype_arr_enable(ps->o.wintype_fade); // --no-fading-open-close lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose); + // --no-fading-opacitychange + lcfg_lookup_bool(&cfg, "no-fading-opacitychange", &ps->o.no_fading_opacitychange); // --shadow-red config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red); // --shadow-green @@ -5439,6 +5775,10 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { } config_destroy(&cfg); + + // Adjust shadow offsets + ps->o.shadow_offset_x = ((-ps->o.shadow_radius * 7 / 5) - ps->o.shadow_offset_x * ps->o.shadow_radius / 100); + ps->o.shadow_offset_y = ((-ps->o.shadow_radius * 7 / 5) - ps->o.shadow_offset_y * ps->o.shadow_radius / 100); } #endif @@ -5447,7 +5787,7 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) { */ static void get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { - const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hscnfFCaSzGb"; + const static char *shortopts = "D:I:O:d:r:o:m:l:t:i:e:hvscnfFCaSzGb"; const static struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "config", required_argument, NULL, 256 }, @@ -5522,6 +5862,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { { "vsync-use-glfinish", no_argument, NULL, 311 }, { "xrender-sync", no_argument, NULL, 312 }, { "xrender-sync-fence", no_argument, NULL, 313 }, + { "no-fading-opacitychange", no_argument, NULL, 314 }, // Must terminate with a NULL entry { NULL, 0, NULL, 0 }, }; @@ -5593,6 +5934,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { case 'h': usage(0); break; + case 'v': fprintf (stderr, "%s v%-3.2f\n", argv[0], _TDE_COMP_MGR_VERSION_); my_exit_code=0; exit (0); case 'd': case 'S': break; @@ -5774,6 +6116,7 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) { P_CASEBOOL(311, vsync_use_glfinish); P_CASEBOOL(312, xrender_sync); P_CASEBOOL(313, xrender_sync_fence); + P_CASEBOOL(314, no_fading_opacitychange); default: usage(1); break; @@ -5879,7 +6222,7 @@ init_atoms(session_t *ps) { ps->atom_transient = XA_WM_TRANSIENT_FOR; ps->atom_client_leader = get_atom(ps, "WM_CLIENT_LEADER"); ps->atom_ewmh_active_win = get_atom(ps, "_NET_ACTIVE_WINDOW"); - ps->atom_compton_shadow = get_atom(ps, "_COMPTON_SHADOW"); + ps->atom_compton_shadow = get_atom(ps, "_TDE_WM_WINDOW_SHADOW"); ps->atom_win_type = get_atom(ps, "_NET_WM_WINDOW_TYPE"); ps->atoms_wintypes[WINTYPE_UNKNOWN] = 0; @@ -5911,6 +6254,9 @@ init_atoms(session_t *ps) { "_NET_WM_WINDOW_TYPE_COMBO"); ps->atoms_wintypes[WINTYPE_DND] = get_atom(ps, "_NET_WM_WINDOW_TYPE_DND"); + + ps->atom_win_type_tde_transparent_to_black = get_atom(ps, "_TDE_TRANSPARENT_TO_BLACK"); + ps->atom_win_type_tde_transparent_to_desktop = get_atom(ps, "_TDE_TRANSPARENT_TO_DESKTOP"); } /** @@ -6754,6 +7100,7 @@ session_init(session_t *ps_old, int argc, char **argv) { .fade_out_step = 0.03 * OPAQUE, .fade_delta = 10, .no_fading_openclose = false, + .no_fading_opacitychange = false, .fade_blacklist = NULL, .wintype_opacity = { 0.0 }, @@ -6871,6 +7218,8 @@ session_init(session_t *ps_old, int argc, char **argv) { .atom_ewmh_active_win = None, .atom_compton_shadow = None, .atom_win_type = None, + .atom_win_type_tde_transparent_to_black = None, + .atom_win_type_tde_transparent_to_desktop = None, .atoms_wintypes = { 0 }, .track_atom_lst = NULL, @@ -7098,6 +7447,8 @@ session_init(session_t *ps_old, int argc, char **argv) { cxinerama_upd_scrs(ps); + fprintf(stderr, "Started\n"); + // Create registration window if (!ps->reg_win && !register_cm(ps)) exit(1); @@ -7470,17 +7821,14 @@ main(int argc, char **argv) { // correctly setlocale(LC_ALL, ""); - // Set up SIGUSR1 signal handler to reset program - { - sigset_t block_mask; - sigemptyset(&block_mask); - const struct sigaction action= { - .sa_handler = reset_enable, - .sa_mask = block_mask, - .sa_flags = 0 - }; - sigaction(SIGUSR1, &action, NULL); - } + // Initialize signal handlers + sigfillset(&block_mask); + usr_action.sa_handler = handle_siguser; + usr_action.sa_mask = block_mask; + usr_action.sa_flags = 0; + sigaction(SIGUSR1, &usr_action, NULL); + sigaction(SIGUSR2, &usr_action, NULL); + sigaction(SIGTERM, &usr_action, NULL); // Main loop session_t *ps_old = ps_g; @@ -7490,6 +7838,11 @@ main(int argc, char **argv) { printf_errf("(): Failed to create new session."); return 1; } + + /* Under no circumstances should these two lines EVER be moved earlier in main() than this point */ + atexit(delete_pid_file); + write_pid_file(getpid()); + session_run(ps_g); ps_old = ps_g; session_destroy(ps_g); diff --git a/compton.h b/compton.h index a4e838efb..dfdbe53d3 100644 --- a/compton.h +++ b/compton.h @@ -17,6 +17,9 @@ #include #include +#include +#include + #ifdef CONFIG_VSYNC_DRM #include // We references some definitions in drm.h, which could also be found in @@ -754,6 +757,9 @@ unmap_win(session_t *ps, win *w); static opacity_t wid_get_opacity_prop(session_t *ps, Window wid, opacity_t def); +static bool +init_filters(session_t *ps); + /** * Reread opacity property of a window. */ diff --git a/opengl.c b/opengl.c index 951d8e50a..bbef9bf16 100644 --- a/opengl.c +++ b/opengl.c @@ -4,6 +4,7 @@ * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard * * Copyright (c) 2011-2013, Christopher Jeffrey + * Copyright (c) 2014 Timothy Pearson * See LICENSE for more information. * */ @@ -1378,7 +1379,7 @@ glx_render(session_t *ps, const glx_texture_t *ptex, glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - + // Texture stage 1 glActiveTexture(GL_TEXTURE1); glEnable(ptex->target); @@ -1508,6 +1509,41 @@ glx_render(session_t *ps, const glx_texture_t *ptex, return true; } +/** + * @brief Render a region with a specified color. + */ +bool +glx_render_specified_color(session_t *ps, int color, int dx, int dy, int width, int height, int z, + XserverRegion reg_tgt, const reg_data_t *pcache_reg) { + + glColor4f(color, + color, + color, + 1.0f + ); + + { + P_PAINTREG_START(); + { + GLint rdx = crect.x; + GLint rdy = ps->root_height - crect.y; + GLint rdxe = rdx + crect.width; + GLint rdye = rdy - crect.height; + + glVertex3i(rdx, rdy, z); + glVertex3i(rdxe, rdy, z); + glVertex3i(rdxe, rdye, z); + glVertex3i(rdx, rdye, z); + } + P_PAINTREG_END(); + } + glColor4f(0.0f, 0.0f, 0.0f, 0.0f); + + glx_check_err(ps); + + return true; +} + /** * Render a region with color. */ From fb41c5018bbe1808176636ac6c51e2b927cda8f2 Mon Sep 17 00:00:00 2001 From: Timothy Pearson Date: Mon, 31 Mar 2014 23:20:55 -0500 Subject: [PATCH 178/178] Move sources to correct directory --- c2.c => twin/compton-tde/c2.c | 0 c2.h => twin/compton-tde/c2.h | 0 common.h => twin/compton-tde/common.h | 0 compton.c => twin/compton-tde/compton.c | 0 compton.h => twin/compton-tde/compton.h | 0 dbus.c => twin/compton-tde/dbus.c | 0 dbus.h => twin/compton-tde/dbus.h | 0 twin/compton-tde/man/compton-tde-trans.1.html | 897 +++++++++ twin/compton-tde/man/compton-tde.1.html | 1603 +++++++++++++++++ twin/compton-tde/man/compton-trans.1 | 201 +++ twin/compton-tde/man/compton.1 | 904 ++++++++++ opengl.c => twin/compton-tde/opengl.c | 0 opengl.h => twin/compton-tde/opengl.h | 0 13 files changed, 3605 insertions(+) rename c2.c => twin/compton-tde/c2.c (100%) rename c2.h => twin/compton-tde/c2.h (100%) rename common.h => twin/compton-tde/common.h (100%) rename compton.c => twin/compton-tde/compton.c (100%) rename compton.h => twin/compton-tde/compton.h (100%) rename dbus.c => twin/compton-tde/dbus.c (100%) rename dbus.h => twin/compton-tde/dbus.h (100%) create mode 100644 twin/compton-tde/man/compton-tde-trans.1.html create mode 100644 twin/compton-tde/man/compton-tde.1.html create mode 100644 twin/compton-tde/man/compton-trans.1 create mode 100644 twin/compton-tde/man/compton.1 rename opengl.c => twin/compton-tde/opengl.c (100%) rename opengl.h => twin/compton-tde/opengl.h (100%) diff --git a/c2.c b/twin/compton-tde/c2.c similarity index 100% rename from c2.c rename to twin/compton-tde/c2.c diff --git a/c2.h b/twin/compton-tde/c2.h similarity index 100% rename from c2.h rename to twin/compton-tde/c2.h diff --git a/common.h b/twin/compton-tde/common.h similarity index 100% rename from common.h rename to twin/compton-tde/common.h diff --git a/compton.c b/twin/compton-tde/compton.c similarity index 100% rename from compton.c rename to twin/compton-tde/compton.c diff --git a/compton.h b/twin/compton-tde/compton.h similarity index 100% rename from compton.h rename to twin/compton-tde/compton.h diff --git a/dbus.c b/twin/compton-tde/dbus.c similarity index 100% rename from dbus.c rename to twin/compton-tde/dbus.c diff --git a/dbus.h b/twin/compton-tde/dbus.h similarity index 100% rename from dbus.h rename to twin/compton-tde/dbus.h diff --git a/twin/compton-tde/man/compton-tde-trans.1.html b/twin/compton-tde/man/compton-tde-trans.1.html new file mode 100644 index 000000000..a6c30fed8 --- /dev/null +++ b/twin/compton-tde/man/compton-tde-trans.1.html @@ -0,0 +1,897 @@ + + + + + +compton-trans(1) + + + + + +
+
+

SYNOPSIS

+
+

compton-trans [-w WINDOW_ID] [-n WINDOW_NAME] [-c] [-s] OPACITY

+
+
+
+

DESCRIPTION

+
+

compton-trans is a bash script that sets _NET_WM_WINDOW_OPACITY attribute of a window using standard X11 command-line utilities, including xprop(1) and xwininfo(1). It is similar to transset(1) or transset-df(1).

+
+
+
+

OPTIONS

+
+
+
+-w WINDOW_ID +
+
+

+Specify the window id of the target window. +

+
+
+-n WINDOW_NAME +
+
+

+Specify and try to match a window name. +

+
+
+-c +
+
+

+Specify the currently active window as target. Only works if EWMH _NET_ACTIVE_WINDOW property exists on root window. +

+
+
+-s +
+
+

+Select target window with mouse cursor. This is the default if no window has been specified. +

+
+
+-o OPACITY +
+
+

+Specify the new opacity value for the window. This value can be anywhere from 1-100. If it is prefixed with a plus or minus (+/-), this will increment or decrement from the target window’s current opacity instead. +

+
+
+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Set the opacity of the window with specific window ID to 75%: +

    +
    +
    +
    compton-trans -w "$WINDOWID" 75
    +
    +
  • +
  • +

    +Set the opacity of the window with the name "urxvt" to 75%: +

    +
    +
    +
    compton-trans -n "urxvt" 75
    +
    +
  • +
  • +

    +Set current window to opacity of 75%: +

    +
    +
    +
    compton-trans -c 75
    +
    +
  • +
  • +

    +Select target window and set opacity to 75%: +

    +
    +
    +
    compton-trans -s 75
    +
    +
  • +
  • +

    +Increment opacity of current active window by 5%: +

    +
    +
    +
    compton-trans -c +5
    +
    +
  • +
  • +

    +Decrement opacity of current active window by 5%: +

    +
    +
    +
    compton-trans -c -- -5
    +
    +
  • +
+
+
+
+

BUGS

+
+

Please report any bugs you find to https://github.com/chjj/compton .

+
+
+
+

AUTHORS

+
+

Christopher Jeffrey (https://github.com/chjj).

+
+
+
+

SEE ALSO

+
+

compton(1), xprop(1), xwininfo(1)

+
+
+
+

+ + + diff --git a/twin/compton-tde/man/compton-tde.1.html b/twin/compton-tde/man/compton-tde.1.html new file mode 100644 index 000000000..26d2a3b21 --- /dev/null +++ b/twin/compton-tde/man/compton-tde.1.html @@ -0,0 +1,1603 @@ + + + + + +compton(1) + + + + + +
+
+

SYNOPSIS

+
+

compton [OPTIONS]

+
+
+
+

WARNING

+
+

This man page may be less up-to-date than the usage text in compton (compton -h).

+
+
+
+

DESCRIPTION

+
+

compton is a compositor based on Dana Jansens' version of xcompmgr (which itself was written by Keith Packard). It includes some improvements over the original xcompmgr, like window frame opacity and inactive window transparency.

+
+
+
+

OPTIONS

+
+
+
+-h, --help +
+
+

+ Get the usage text embedded in program code, which may be more up-to-date than this man page. +

+
+
+-d DISPLAY +
+
+

+ Display to be managed. +

+
+
+-r RADIUS +
+
+

+ The blur radius for shadows, in pixels. (defaults to 12) +

+
+
+-o OPACITY +
+
+

+ The opacity of shadows. (0.0 - 1.0, defaults to 0.75) +

+
+
+-l OFFSET +
+
+

+ The left offset for shadows, in pixels. (defaults to -15) +

+
+
+-t OFFSET +
+
+

+ The top offset for shadows, in pixels. (defaults to -15) +

+
+
+-I OPACITY_STEP +
+
+

+ Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028) +

+
+
+-O OPACITY_STEP +
+
+

+ Opacity change between steps while fading out. (0.01 - 1.0, defaults to 0.03) +

+
+
+-D MILLISECONDS +
+
+

+ The time between steps in fade step, in milliseconds. (> 0, defaults to 10) +

+
+
+-m OPACITY +
+
+

+ Default opacity for dropdown menus and popup menus. (0.0 - 1.0, defaults to 1.0) +

+
+
+-c +
+
+

+ Enabled client-side shadows on windows. Note desktop windows (windows with _NET_WM_WINDOW_TYPE_DESKTOP) never get shadow. +

+
+
+-C +
+
+

+ Avoid drawing shadows on dock/panel windows. +

+
+
+-z +
+
+

+ Zero the part of the shadow’s mask behind the window. Note this may not work properly on ARGB windows with fully transparent areas. +

+
+
+-f +
+
+

+ Fade windows in/out when opening/closing and when opacity changes, unless --no-fading-openclose is used. +

+
+
+-F +
+
+

+ Equals -f. Deprecated. +

+
+
+-i OPACITY +
+
+

+ Opacity of inactive windows. (0.1 - 1.0, disabled by default) +

+
+
+-e OPACITY +
+
+

+ Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default) +

+
+
+-G +
+
+

+ Don’t draw shadows on drag-and-drop windows. +

+
+
+-b +
+
+

+ Daemonize process. Fork to background after initialization. +

+
+
+-S +
+
+

+ Enable synchronous X operation (for debugging). +

+
+
+--config PATH +
+
+

+ Look for configuration file at the path. See CONFIGURATION FILES section below for where compton looks for a configuration file by default. +

+
+
+--write-pid-path PATH +
+
+

+ Write process ID to a file. +

+
+
+--shadow-red VALUE +
+
+

+ Red color value of shadow (0.0 - 1.0, defaults to 0). +

+
+
+--shadow-green VALUE +
+
+

+ Green color value of shadow (0.0 - 1.0, defaults to 0). +

+
+
+--shadow-blue VALUE +
+
+

+ Blue color value of shadow (0.0 - 1.0, defaults to 0). +

+
+
+--inactive-opacity-override +
+
+

+ Let inactive opacity set by -i overrides the windows' _NET_WM_OPACITY values. +

+
+
+--active-opacity OPACITY +
+
+

+ Default opacity for active windows. (0.0 - 1.0) +

+
+
+--inactive-dim VALUE +
+
+

+ Dim inactive windows. (0.0 - 1.0, defaults to 0.0) +

+
+
+--mark-wmwin-focused +
+
+

+ Try to detect WM windows (a non-override-redirect window with no child that has WM_STATE) and mark them as active. +

+
+
+--mark-ovredir-focused +
+
+

+ Mark override-redirect windows that doesn’t have a child window with WM_STATE focused. +

+
+
+--no-fading-openclose +
+
+

+ Do not fade on window open/close. +

+
+
+--shadow-ignore-shaped +
+
+

+ Do not paint shadows on shaped windows. Note shaped windows here means windows setting its shape through X Shape extension. Those using ARGB background is beyond our control. +

+
+
+--detect-rounded-corners +
+
+

+ Try to detect windows with rounded corners and don’t consider them shaped windows. The accuracy is not very high, unfortunately. +

+
+
+--detect-client-opacity +
+
+

+ Detect _NET_WM_OPACITY on client windows, useful for window managers not passing _NET_WM_OPACITY of client windows to frame windows. +

+
+
+--refresh-rate REFRESH_RATE +
+
+

+ Specify refresh rate of the screen. If not specified or 0, compton will try detecting this with X RandR extension. +

+
+
+--vsync VSYNC_METHOD +
+
+

+ Set VSync method. VSync methods currently available: +

+
+
+
    +
  • +

    +none: No VSync +

    +
  • +
  • +

    +drm: VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some drivers. +

    +
  • +
  • +

    +opengl: Try to VSync with SGI_video_sync OpenGL extension. Only work on some drivers. +

    +
  • +
  • +

    +opengl-oml: Try to VSync with OML_sync_control OpenGL extension. Only work on some drivers. +

    +
  • +
  • +

    +opengl-swc: Try to VSync with SGI_swap_control OpenGL extension. Only work on some drivers. Works only with GLX backend. Known to be most effective on many drivers. Does not actually control paint timing, only buffer swap is affected, so it doesn’t have the effect of --sw-opti unlike other methods. Experimental. +

    +
  • +
  • +

    +opengl-mswc: Try to VSync with MESA_swap_control OpenGL extension. Basically the same as opengl-swc above, except the extension we use. +

    +
  • +
+

(Note some VSync methods may not be enabled at compile time.)

+
+
+
+--vsync-aggressive +
+
+

+ Attempt to send painting request before VBlank and do XFlush() during VBlank. Reported to work pretty terribly. This switch may be lifted out at any moment. +

+
+
+--alpha-step VALUE +
+
+

+ X Render backend: Step for pregenerating alpha pictures. (0.01 - 1.0, defaults to 0.03) +

+
+
+--dbe +
+
+

+ Enable DBE painting mode, intended to use with VSync to (hopefully) eliminate tearing. Reported to have no effect, though. +

+
+
+--paint-on-overlay +
+
+

+ Painting on X Composite overlay window instead of on root window. +

+
+
+--sw-opti +
+
+

+ Limit compton to repaint at most once every 1 / refresh_rate second to boost performance. This should not be used with --vsync drm/opengl/opengl-oml as they essentially does --sw-opti's job already, unless you wish to specify a lower refresh rate than the actual value. +

+
+
+--use-ewmh-active-win +
+
+

+ Use EWMH _NET_ACTIVE_WINDOW to determine currently focused window, rather than listening to FocusIn/FocusOut event. Might have more accuracy, provided that the WM supports it. +

+
+
+--respect-prop-shadow +
+
+

+ Respect _COMPTON_SHADOW. This a prototype-level feature, which you must not rely on. +

+
+
+--unredir-if-possible +
+
+

+ Unredirect all windows if a full-screen opaque window is detected, to maximize performance for full-screen windows. Known to cause flickering when redirecting/unredirecting windows. --paint-on-overlay may make the flickering less obvious. +

+
+
+--unredir-if-possible-delay MILLISECONDS +
+
+

+ Delay before unredirecting the window, in milliseconds. Defaults to 0. +

+
+
+--unredir-if-possible-exclude CONDITION +
+
+

+ Conditions of windows that shouldn’t be considered full-screen for unredirecting screen. +

+
+
+--shadow-exclude CONDITION +
+
+

+ Specify a list of conditions of windows that should have no shadow. +

+
+
+--fade-exclude CONDITION +
+
+

+ Specify a list of conditions of windows that should not be faded. +

+
+
+--focus-exclude CONDITION +
+
+

+ Specify a list of conditions of windows that should always be considered focused. +

+
+
+--inactive-dim-fixed +
+
+

+ Use fixed inactive dim value, instead of adjusting according to window opacity. +

+
+
+--detect-transient +
+
+

+ Use WM_TRANSIENT_FOR to group windows, and consider windows in the same group focused at the same time. +

+
+
+--detect-client-leader +
+
+

+ Use WM_CLIENT_LEADER to group windows, and consider windows in the same group focused at the same time. WM_TRANSIENT_FOR has higher priority if --detect-transient is enabled, too. +

+
+
+--blur-background +
+
+

+ Blur background of semi-transparent / ARGB windows. Bad in performance, with driver-dependent behavior. The name of the switch may change without prior notifications. +

+
+
+--blur-background-frame +
+
+

+ Blur background of windows when the window frame is not opaque. Implies --blur-background. Bad in performance, with driver-dependent behavior. The name may change. +

+
+
+--blur-background-fixed +
+
+

+ Use fixed blur strength rather than adjusting according to window opacity. +

+
+
+--blur-kern MATRIX +
+
+

+ Specify the blur convolution kernel, with the following format: +

+
+
+
WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...
+
+

The element in the center must not be included, it will be forever 1.0 or changing based on opacity, depending on whether you have --blur-background-fixed. Yet the automatic adjustment of blur factor may not work well with a custom blur kernel.

+

A 7x7 Guassian blur kernel (sigma = 0.84089642) looks like:

+
+
+
--blur-kern '7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003494,0.029143,0.059106,0.029143,0.003494,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003'
+
+

May also be one of the predefined kernels: 3x3box (default), 5x5box, 7x7box, 3x3gaussian, 5x5gaussian, 7x7gaussian, 9x9gaussian, 11x11gaussian. All Guassian kernels are generated with sigma = 0.84089642 . You may use the accompanied compton-convgen.py to generate blur kernels.

+
+
+--blur-background-exclude CONDITION +
+
+

+ Exclude conditions for background blur. +

+
+
+--resize-damage INTEGER +
+
+

+ Resize damaged region by a specific number of pixels. A positive value enlarges it while a negative one shrinks it. If the value is positive, those additional pixels will not be actually painted to screen, only used in blur calculation, and such. (Due to technical limitations, with --dbe or --glx-swap-method, those pixels will still be incorrectly painted to screen.) Primarily used to fix the line corruption issues of blur, in which case you should use the blur radius value here (e.g. with a 3x3 kernel, you should use --resize-damage 1, with a 5x5 one you use --resize-damage 2, and so on). May or may not work with --glx-no-stencil. Shrinking doesn’t function correctly. +

+
+
+--invert-color-include CONDITION +
+
+

+ Specify a list of conditions of windows that should be painted with inverted color. Resource-hogging, and is not well tested. +

+
+
+--opacity-rule OPACITY:'CONDITION' +
+
+

+ Specify a list of opacity rules, in the format PERCENT:PATTERN, like 50:name *= "Firefox". compton-trans is recommended over this. Note we do not distinguish 100% and unset, and we don’t make any guarantee about possible conflicts with other programs that set _NET_WM_WINDOW_OPACITY on frame or client windows. +

+
+
+--shadow-exclude-reg GEOMETRY +
+
+

+ Specify a X geometry that describes the region in which shadow should not be painted in, such as a dock window region. Use --shadow-exclude-reg x10+0-0, for example, if the 10 pixels on the bottom of the screen should not have shadows painted on. +

+
+
+--xinerama-shadow-crop +
+
+

+ Crop shadow of a window fully on a particular Xinerama screen to the screen. +

+
+
+--backend BACKEND +
+
+

+ Specify the backend to use: xrender or glx. GLX (OpenGL) backend generally has much superior performance as far as you have a graphic card/chip and driver. +

+
+
+--glx-no-stencil +
+
+

+ GLX backend: Avoid using stencil buffer, useful if you don’t have a stencil buffer. Might cause incorrect opacity when rendering transparent content (but never practically happened) and may not work with --blur-background. My tests show a 15% performance boost. Recommended. +

+
+
+--glx-copy-from-front +
+
+

+ GLX backend: Copy unmodified regions from front buffer instead of redrawing them all. My tests with nvidia-drivers show a 10% decrease in performance when the whole screen is modified, but a 20% increase when only 1/4 is. My tests on nouveau show terrible slowdown. Useful with --glx-swap-method, as well. +

+
+
+--glx-use-copysubbuffermesa +
+
+

+ GLX backend: Use MESA_copy_sub_buffer to do partial screen update. My tests on nouveau shows a 200% performance boost when only 1/4 of the screen is updated. May break VSync and is not available on some drivers. Overrides --glx-copy-from-front. +

+
+
+--glx-no-rebind-pixmap +
+
+

+ GLX backend: Avoid rebinding pixmap on window damage. Probably could improve performance on rapid window content changes, but is known to break things on some drivers (LLVMpipe). Recommended if it works. +

+
+
+--glx-swap-method undefined/exchange/copy/3/4/5/6/buffer-age +
+
+

+ GLX backend: GLX buffer swap method we assume. Could be undefined (0), copy (1), exchange (2), 3-6, or buffer-age (-1). undefined is the slowest and the safest, and the default value. copy is fastest, but may fail on some drivers, 2-6 are gradually slower but safer (6 is still faster than 0). Usually, double buffer means 2, triple buffer means 3. buffer-age means auto-detect using GLX_EXT_buffer_age, supported by some drivers. Useless with --glx-use-copysubbuffermesa. Partially breaks --resize-damage. Defaults to undefined. +

+
+
+--glx-use-gpushader4 +
+
+

+ GLX backend: Use GL_EXT_gpu_shader4 for some optimization on blur GLSL code. My tests on GTX 670 show no noticeable effect. +

+
+
+--dbus +
+
+

+ Enable remote control via D-Bus. See the D-BUS API section below for more details. +

+
+
+--benchmark CYCLES +
+
+

+ Benchmark mode. Repeatedly paint until reaching the specified cycles. +

+
+
+--benchmark-wid WINDOW_ID +
+
+

+ Specify window ID to repaint in benchmark mode. If omitted or is 0, the whole screen is repainted. +

+
+
+
+
+
+

FORMAT OF CONDITIONS

+
+

Some options accept a condition string to match certain windows. A condition string is formed by one or more conditions, joined by logical operators.

+

A condition with "exists" operator looks like this:

+
+
+
<NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <FORMAT> <TYPE>
+
+

With equals operator it looks like:

+
+
+
<NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <FORMAT> <TYPE> <NEGATION> <OP QUALIFIER> <MATCH TYPE> = <PATTERN>
+
+

With greater-than/less-than operators it looks like:

+
+
+
<NEGATION> <TARGET> <CLIENT/FRAME> [<INDEX>] : <FORMAT> <TYPE> <NEGATION> <OPERATOR> <PATTERN>
+
+

NEGATION (optional) is one or more exclamation marks;

+

TARGET is either a predefined target name, or the name of a window property to match. Supported predefined targets are id, x, y, x2 (x + widthb), y2, width, height, widthb (width + 2 * border), heightb, override_redirect, argb (whether the window has an ARGB visual), focused, wmwin (whether the window looks like a WM window, i.e. has no child window with WM_STATE and is not override-redirected), client (ID of client window), window_type (window type in string), leader (ID of window leader), name, class_g (= WM_CLASS[1]), class_i (= WM_CLASS[0]), and role.

+

CLIENT/FRAME is a single @ if the window attribute should be be looked up on client window, nothing if on frame window;

+

INDEX (optional) is the index number of the property to look up. For example, [2] means look at the third value in the property. Do not specify it for predefined targets.

+

FORMAT (optional) specifies the format of the property, 8, 16, or 32. On absence we use format X reports. Do not specify it for predefined or string targets.

+

TYPE is a single character representing the type of the property to match for: c for CARDINAL, a for ATOM, w for WINDOW, d for DRAWABLE, s for STRING (and any other string types, such as UTF8_STRING). Do not specify it for predefined targets.

+

OP QUALIFIER (optional), applicable only for equals operator, could be ? (ignore-case).

+

MATCH TYPE (optional), applicable only for equals operator, could be nothing (exact match), * (match anywhere), ^ (match from start), % (wildcard), or ~ (PCRE regular expression).

+

OPERATOR is one of = (equals), <, >, <=, =>, or nothing (exists). Exists operator checks whether a property exists on a window (but for predefined targets, exists means != 0 then).

+

PATTERN is either an integer or a string enclosed by single or double quotes. Python-3-style escape sequences and raw string are supported in the string format.

+

Supported logical operators are && (and) and || (or). && has higher precedence than ||, left-to-right associativity. Use parentheses to change precedence.

+

Examples:

+
+
+
# If the window is focused
+focused
+focused = 1
+# If the window is not override-redirected
+!override_redirect
+override_redirect = false
+override_redirect != true
+override_redirect != 1
+# If the window is a menu
+window_type *= "menu"
+_NET_WM_WINDOW_TYPE@:a *= "MENU"
+# If the window name contains "Firefox", ignore case
+name *?= "Firefox"
+_NET_WM_NAME@:s *?= "Firefox"
+# If the window name ends with "Firefox"
+name %= "*Firefox"
+name ~= "Firefox$"
+# If the window has a property _COMPTON_SHADOW with value 0, type CARDINAL,
+# format 32, value 0, on its frame window
+_COMPTON_SHADOW:32c = 0
+# If the third value of _NET_FRAME_EXTENTS is less than 20, or there's no
+# _NET_FRAME_EXTENTS property on client window
+_NET_FRAME_EXTENTS@[2]:32c < 20 || !_NET_FRAME_EXTENTS@:32c
+# The pattern here will be parsed as "dd4"
+name = "\x64\x64\o64"
+# The pattern here will be parsed as "\x64\x64\x64"
+name = r"\x64\x64\o64"
+
+
+
+
+

LEGACY FORMAT OF CONDITIONS

+
+

This is the old condition format we once used. Support of this format might be removed in the future.

+
+
+
condition = TARGET:TYPE[FLAGS]:PATTERN
+
+

TARGET is one of "n" (window name), "i" (window class instance), "g" (window general class), and "r" (window role).

+

TYPE is one of "e" (exact match), "a" (match anywhere), "s" (match from start), "w" (wildcard), and "p" (PCRE regular expressions, if compiled with the support).

+

FLAGS could be a series of flags. Currently the only defined flag is "i" (ignore case).

+

PATTERN is the actual pattern string.

+
+
+
+

CONFIGURATION FILES

+
+

compton could read from a configuration file if libconfig support is compiled in. If --config is not used, compton will seek for a configuration file in $XDG_CONFIG_HOME/compton.conf (~/.config/compton.conf, usually), then ~/.compton.conf, then compton.conf under $XDG_DATA_DIRS (often /etc/xdg/compton.conf).

+

compton uses general libconfig configuration file format. A sample configuration file is available as compton.sample.conf in the source tree. Most commandline switches each could be replaced with an option in configuration file, thus documented above. Window-type-specific settings are exposed only in configuration file and has the following format:

+
+
+
wintypes:
+{
+  WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; };
+};
+
+

WINDOW_TYPE is one of the 15 window types defined in EWMH standard: "unknown", "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal", "dropdown_menu", "popup_menu", "tooltip", "notify", "combo", and "dnd". "fade" and "shadow" controls window-type-specific shadow and fade settings. "opacity" controls default opacity of the window type. "focus" controls whether the window of this type is to be always considered focused. (By default, all window types except "normal" and "dialog" has this on.)

+
+
+
+

SIGNALS

+
+
    +
  • +

    +compton reinitializes itself upon receiving SIGUSR1. +

    +
  • +
+
+
+
+

D-BUS API

+
+

It’s possible to control compton via D-Bus messages, by running compton with --dbus and send messages to com.github.chjj.compton.<DISPLAY>. <DISPLAY> is the display used by compton, with all non-alphanumeric characters transformed to underscores. For DISPLAY=:0.0 you should use com.github.chjj.compton._0_0, for example.

+

The D-Bus methods and signals are not yet stable, thus undocumented right now.

+
+
+
+

EXAMPLES

+
+
    +
  • +

    +Disable configuration file parsing: +

    +
    +
    +
    $ compton --config /dev/null
    +
    +
  • +
  • +

    +Run compton with client-side shadow and fading, disable shadow on dock windows and drag-and-drop windows: +

    +
    +
    +
    $ compton -cCGf
    +
    +
  • +
  • +

    +Same thing as above, plus making inactive windows 80% transparent, making frame 80% transparent, don’t fade on window open/close, enable software optimization, and fork to background: +

    +
    +
    +
    $ compton -bcCGf -i 0.8 -e 0.8 --no-fading-openclose --sw-opti
    +
    +
  • +
  • +

    +Draw white shadows: +

    +
    +
    +
    $ compton -c --shadow-red 1 --shadow-green 1 --shadow-blue 1
    +
    +
  • +
  • +

    +Avoid drawing shadows on wbar window: +

    +
    +
    +
    $ compton -c --shadow-exclude 'class_g = "wbar"'
    +
    +
  • +
  • +

    +Enable OpenGL SGI_swap_control VSync with GLX backend: +

    +
    +
    +
    $ compton --backend glx --vsync opengl-swc
    +
    +
  • +
+
+
+
+

BUGS

+
+

Please report any you find to https://github.com/chjj/compton .

+
+
+
+

AUTHORS

+
+

xcompmgr, originally written by Keith Packard, with contributions from Matthew Allum, Eric Anholt, Dan Doel, Thomas Luebking, Matthew Hawn, Ely Levy, Phil Blundell, and Carl Worth. Compton by Christopher Jeffrey, based on Dana Jansens' original work, with contributions from Richard Grenville.

+
+
+
+

RESOURCES

+ +
+
+

SEE ALSO

+
+

xcompmgr(1), compton-trans(1)

+
+
+
+

+ + + diff --git a/twin/compton-tde/man/compton-trans.1 b/twin/compton-tde/man/compton-trans.1 new file mode 100644 index 000000000..8f4351ac3 --- /dev/null +++ b/twin/compton-tde/man/compton-trans.1 @@ -0,0 +1,201 @@ +'\" t +.\" Title: compton-trans +.\" Author: [see the "AUTHORS" section] +.\" Generator: DocBook XSL Stylesheets v1.76.1 +.\" Date: 03/31/2014 +.\" Manual: LOCAL USER COMMANDS +.\" Source: compton nightly-20121114 +.\" Language: English +.\" +.TH "COMPTON\-TRANS" "1" "03/31/2014" "compton nightly\-20121114" "LOCAL USER COMMANDS" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +compton-trans \- an opacity setter tool +.SH "SYNOPSIS" +.sp +\fBcompton\-trans\fR [\-w \fIWINDOW_ID\fR] [\-n \fIWINDOW_NAME\fR] [\-c] [\-s] \fIOPACITY\fR +.SH "DESCRIPTION" +.sp +\fBcompton\-trans\fR is a bash script that sets \fI_NET_WM_WINDOW_OPACITY\fR attribute of a window using standard X11 command\-line utilities, including \fBxprop\fR(1) and \fBxwininfo\fR(1)\&. It is similar to \fBtransset\fR(1) or \fBtransset\-df\fR(1)\&. +.SH "OPTIONS" +.PP +\fB\-w\fR \fIWINDOW_ID\fR +.RS 4 +Specify the window id of the target window\&. +.RE +.PP +\fB\-n\fR \fIWINDOW_NAME\fR +.RS 4 +Specify and try to match a window name\&. +.RE +.PP +\fB\-c\fR +.RS 4 +Specify the currently active window as target\&. Only works if EWMH +\fI_NET_ACTIVE_WINDOW\fR +property exists on root window\&. +.RE +.PP +\fB\-s\fR +.RS 4 +Select target window with mouse cursor\&. This is the default if no window has been specified\&. +.RE +.PP +\fB\-o\fR \fIOPACITY\fR +.RS 4 +Specify the new opacity value for the window\&. This value can be anywhere from 1\-100\&. If it is prefixed with a plus or minus (+/\-), this will increment or decrement from the target window\(cqs current opacity instead\&. +.RE +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Set the opacity of the window with specific window ID to 75%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +compton\-trans \-w "$WINDOWID" 75 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Set the opacity of the window with the name "urxvt" to 75%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +compton\-trans \-n "urxvt" 75 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Set current window to opacity of 75%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +compton\-trans \-c 75 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Select target window and set opacity to 75%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +compton\-trans \-s 75 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Increment opacity of current active window by 5%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +compton\-trans \-c +5 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Decrement opacity of current active window by 5%: +.sp +.if n \{\ +.RS 4 +.\} +.nf +compton\-trans \-c \-\- \-5 +.fi +.if n \{\ +.RE +.\} +.RE +.SH "BUGS" +.sp +Please report any bugs you find to https://github\&.com/chjj/compton \&. +.SH "AUTHORS" +.sp +Christopher Jeffrey (https://github\&.com/chjj)\&. +.SH "SEE ALSO" +.sp +\fBcompton\fR(1), \fBxprop\fR(1), \fBxwininfo\fR(1) diff --git a/twin/compton-tde/man/compton.1 b/twin/compton-tde/man/compton.1 new file mode 100644 index 000000000..964c4158d --- /dev/null +++ b/twin/compton-tde/man/compton.1 @@ -0,0 +1,904 @@ +'\" t +.\" Title: compton +.\" Author: [see the "AUTHORS" section] +.\" Generator: DocBook XSL Stylesheets v1.76.1 +.\" Date: 03/31/2014 +.\" Manual: LOCAL USER COMMANDS +.\" Source: compton nightly-20130421 +.\" Language: English +.\" +.TH "COMPTON" "1" "03/31/2014" "compton nightly\-20130421" "LOCAL USER COMMANDS" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +compton \- a compositor for X11 +.SH "SYNOPSIS" +.sp +\fBcompton\fR [\fIOPTIONS\fR] +.SH "WARNING" +.sp +This man page may be less up\-to\-date than the usage text in compton (compton \-h)\&. +.SH "DESCRIPTION" +.sp +compton is a compositor based on Dana Jansens\*(Aq version of xcompmgr (which itself was written by Keith Packard)\&. It includes some improvements over the original xcompmgr, like window frame opacity and inactive window transparency\&. +.SH "OPTIONS" +.PP +\fB\-h\fR, \fB\-\-help\fR +.RS 4 +Get the usage text embedded in program code, which may be more up\-to\-date than this man page\&. +.RE +.PP +\fB\-d\fR \fIDISPLAY\fR +.RS 4 +Display to be managed\&. +.RE +.PP +\fB\-r\fR \fIRADIUS\fR +.RS 4 +The blur radius for shadows, in pixels\&. (defaults to 12) +.RE +.PP +\fB\-o\fR \fIOPACITY\fR +.RS 4 +The opacity of shadows\&. (0\&.0 \- 1\&.0, defaults to 0\&.75) +.RE +.PP +\fB\-l\fR \fIOFFSET\fR +.RS 4 +The left offset for shadows, in pixels\&. (defaults to \-15) +.RE +.PP +\fB\-t\fR \fIOFFSET\fR +.RS 4 +The top offset for shadows, in pixels\&. (defaults to \-15) +.RE +.PP +\fB\-I\fR \fIOPACITY_STEP\fR +.RS 4 +Opacity change between steps while fading in\&. (0\&.01 \- 1\&.0, defaults to 0\&.028) +.RE +.PP +\fB\-O\fR \fIOPACITY_STEP\fR +.RS 4 +Opacity change between steps while fading out\&. (0\&.01 \- 1\&.0, defaults to 0\&.03) +.RE +.PP +\fB\-D\fR \fIMILLISECONDS\fR +.RS 4 +The time between steps in fade step, in milliseconds\&. (> 0, defaults to 10) +.RE +.PP +\fB\-m\fR \fIOPACITY\fR +.RS 4 +Default opacity for dropdown menus and popup menus\&. (0\&.0 \- 1\&.0, defaults to 1\&.0) +.RE +.PP +\fB\-c\fR +.RS 4 +Enabled client\-side shadows on windows\&. Note desktop windows (windows with +\fI_NET_WM_WINDOW_TYPE_DESKTOP\fR) never get shadow\&. +.RE +.PP +\fB\-C\fR +.RS 4 +Avoid drawing shadows on dock/panel windows\&. +.RE +.PP +\fB\-z\fR +.RS 4 +Zero the part of the shadow\(cqs mask behind the window\&. Note this may not work properly on ARGB windows with fully transparent areas\&. +.RE +.PP +\fB\-f\fR +.RS 4 +Fade windows in/out when opening/closing and when opacity changes, unless +\fB\-\-no\-fading\-openclose\fR +is used\&. +.RE +.PP +\fB\-F\fR +.RS 4 +Equals +\fB\-f\fR\&. Deprecated\&. +.RE +.PP +\fB\-i\fR \fIOPACITY\fR +.RS 4 +Opacity of inactive windows\&. (0\&.1 \- 1\&.0, disabled by default) +.RE +.PP +\fB\-e\fR \fIOPACITY\fR +.RS 4 +Opacity of window titlebars and borders\&. (0\&.1 \- 1\&.0, disabled by default) +.RE +.PP +\fB\-G\fR +.RS 4 +Don\(cqt draw shadows on drag\-and\-drop windows\&. +.RE +.PP +\fB\-b\fR +.RS 4 +Daemonize process\&. Fork to background after initialization\&. +.RE +.PP +\fB\-S\fR +.RS 4 +Enable synchronous X operation (for debugging)\&. +.RE +.PP +\fB\-\-config\fR \fIPATH\fR +.RS 4 +Look for configuration file at the path\&. See +\fBCONFIGURATION FILES\fR +section below for where compton looks for a configuration file by default\&. +.RE +.PP +\fB\-\-write\-pid\-path\fR \fIPATH\fR +.RS 4 +Write process ID to a file\&. +.RE +.PP +\fB\-\-shadow\-red\fR \fIVALUE\fR +.RS 4 +Red color value of shadow (0\&.0 \- 1\&.0, defaults to 0)\&. +.RE +.PP +\fB\-\-shadow\-green\fR \fIVALUE\fR +.RS 4 +Green color value of shadow (0\&.0 \- 1\&.0, defaults to 0)\&. +.RE +.PP +\fB\-\-shadow\-blue\fR \fIVALUE\fR +.RS 4 +Blue color value of shadow (0\&.0 \- 1\&.0, defaults to 0)\&. +.RE +.PP +\fB\-\-inactive\-opacity\-override\fR +.RS 4 +Let inactive opacity set by +\fB\-i\fR +overrides the windows\*(Aq +\fI_NET_WM_OPACITY\fR +values\&. +.RE +.PP +\fB\-\-active\-opacity\fR \fIOPACITY\fR +.RS 4 +Default opacity for active windows\&. (0\&.0 \- 1\&.0) +.RE +.PP +\fB\-\-inactive\-dim\fR \fIVALUE\fR +.RS 4 +Dim inactive windows\&. (0\&.0 \- 1\&.0, defaults to 0\&.0) +.RE +.PP +\fB\-\-mark\-wmwin\-focused\fR +.RS 4 +Try to detect WM windows (a non\-override\-redirect window with no child that has +WM_STATE) and mark them as active\&. +.RE +.PP +\fB\-\-mark\-ovredir\-focused\fR +.RS 4 +Mark override\-redirect windows that doesn\(cqt have a child window with +WM_STATE +focused\&. +.RE +.PP +\fB\-\-no\-fading\-openclose\fR +.RS 4 +Do not fade on window open/close\&. +.RE +.PP +\fB\-\-shadow\-ignore\-shaped\fR +.RS 4 +Do not paint shadows on shaped windows\&. Note shaped windows here means windows setting its shape through X Shape extension\&. Those using ARGB background is beyond our control\&. +.RE +.PP +\fB\-\-detect\-rounded\-corners\fR +.RS 4 +Try to detect windows with rounded corners and don\(cqt consider them shaped windows\&. The accuracy is not very high, unfortunately\&. +.RE +.PP +\fB\-\-detect\-client\-opacity\fR +.RS 4 +Detect +\fI_NET_WM_OPACITY\fR +on client windows, useful for window managers not passing +\fI_NET_WM_OPACITY\fR +of client windows to frame windows\&. +.RE +.PP +\fB\-\-refresh\-rate\fR \fIREFRESH_RATE\fR +.RS 4 +Specify refresh rate of the screen\&. If not specified or 0, compton will try detecting this with X RandR extension\&. +.RE +.PP +\fB\-\-vsync\fR \fIVSYNC_METHOD\fR +.RS 4 +Set VSync method\&. VSync methods currently available: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} + +\fInone\fR: No VSync +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} + +\fIdrm\fR: VSync with +\fIDRM_IOCTL_WAIT_VBLANK\fR\&. May only work on some drivers\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} + +\fIopengl\fR: Try to VSync with +\fISGI_video_sync\fR +OpenGL extension\&. Only work on some drivers\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} + +\fIopengl\-oml\fR: Try to VSync with +\fIOML_sync_control\fR +OpenGL extension\&. Only work on some drivers\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} + +\fIopengl\-swc\fR: Try to VSync with +\fISGI_swap_control\fR +OpenGL extension\&. Only work on some drivers\&. Works only with GLX backend\&. Known to be most effective on many drivers\&. Does not actually control paint timing, only buffer swap is affected, so it doesn\(cqt have the effect of +\fB\-\-sw\-opti\fR +unlike other methods\&. Experimental\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} + +\fIopengl\-mswc\fR: Try to VSync with +\fIMESA_swap_control\fR +OpenGL extension\&. Basically the same as +\fIopengl\-swc\fR +above, except the extension we use\&. +.RE +.sp +(Note some VSync methods may not be enabled at compile time\&.) +.RE +.PP +\fB\-\-vsync\-aggressive\fR +.RS 4 +Attempt to send painting request before VBlank and do XFlush() during VBlank\&. Reported to work pretty terribly\&. This switch may be lifted out at any moment\&. +.RE +.PP +\fB\-\-alpha\-step\fR \fIVALUE\fR +.RS 4 +X Render backend: Step for pregenerating alpha pictures\&. (0\&.01 \- 1\&.0, defaults to 0\&.03) +.RE +.PP +\fB\-\-dbe\fR +.RS 4 +Enable DBE painting mode, intended to use with VSync to (hopefully) eliminate tearing\&. Reported to have no effect, though\&. +.RE +.PP +\fB\-\-paint\-on\-overlay\fR +.RS 4 +Painting on X Composite overlay window instead of on root window\&. +.RE +.PP +\fB\-\-sw\-opti\fR +.RS 4 +Limit compton to repaint at most once every 1 / +\fIrefresh_rate\fR +second to boost performance\&. This should not be used with +\fB\-\-vsync\fR +drm/opengl/opengl\-oml as they essentially does +\fB\-\-sw\-opti\fR\*(Aqs job already, unless you wish to specify a lower refresh rate than the actual value\&. +.RE +.PP +\fB\-\-use\-ewmh\-active\-win\fR +.RS 4 +Use EWMH +\fI_NET_ACTIVE_WINDOW\fR +to determine currently focused window, rather than listening to +\fIFocusIn\fR/\fIFocusOut\fR +event\&. Might have more accuracy, provided that the WM supports it\&. +.RE +.PP +\fB\-\-respect\-prop\-shadow\fR +.RS 4 +Respect +\fI_COMPTON_SHADOW\fR\&. This a prototype\-level feature, which you must not rely on\&. +.RE +.PP +\fB\-\-unredir\-if\-possible\fR +.RS 4 +Unredirect all windows if a full\-screen opaque window is detected, to maximize performance for full\-screen windows\&. Known to cause flickering when redirecting/unredirecting windows\&. +\fB\-\-paint\-on\-overlay\fR +may make the flickering less obvious\&. +.RE +.PP +\fB\-\-unredir\-if\-possible\-delay\fR \fIMILLISECONDS\fR +.RS 4 +Delay before unredirecting the window, in milliseconds\&. Defaults to 0\&. +.RE +.PP +\fB\-\-unredir\-if\-possible\-exclude\fR \fICONDITION\fR +.RS 4 +Conditions of windows that shouldn\(cqt be considered full\-screen for unredirecting screen\&. +.RE +.PP +\fB\-\-shadow\-exclude\fR \fICONDITION\fR +.RS 4 +Specify a list of conditions of windows that should have no shadow\&. +.RE +.PP +\fB\-\-fade\-exclude\fR \fICONDITION\fR +.RS 4 +Specify a list of conditions of windows that should not be faded\&. +.RE +.PP +\fB\-\-focus\-exclude\fR \fICONDITION\fR +.RS 4 +Specify a list of conditions of windows that should always be considered focused\&. +.RE +.PP +\fB\-\-inactive\-dim\-fixed\fR +.RS 4 +Use fixed inactive dim value, instead of adjusting according to window opacity\&. +.RE +.PP +\fB\-\-detect\-transient\fR +.RS 4 +Use +\fIWM_TRANSIENT_FOR\fR +to group windows, and consider windows in the same group focused at the same time\&. +.RE +.PP +\fB\-\-detect\-client\-leader\fR +.RS 4 +Use +\fIWM_CLIENT_LEADER\fR +to group windows, and consider windows in the same group focused at the same time\&. +\fIWM_TRANSIENT_FOR\fR +has higher priority if +\fB\-\-detect\-transient\fR +is enabled, too\&. +.RE +.PP +\fB\-\-blur\-background\fR +.RS 4 +Blur background of semi\-transparent / ARGB windows\&. Bad in performance, with driver\-dependent behavior\&. The name of the switch may change without prior notifications\&. +.RE +.PP +\fB\-\-blur\-background\-frame\fR +.RS 4 +Blur background of windows when the window frame is not opaque\&. Implies +\fB\-\-blur\-background\fR\&. Bad in performance, with driver\-dependent behavior\&. The name may change\&. +.RE +.PP +\fB\-\-blur\-background\-fixed\fR +.RS 4 +Use fixed blur strength rather than adjusting according to window opacity\&. +.RE +.PP +\fB\-\-blur\-kern\fR \fIMATRIX\fR +.RS 4 +Specify the blur convolution kernel, with the following format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5\&.\&.\&. +.fi +.if n \{\ +.RE +.\} +.sp +The element in the center must not be included, it will be forever 1\&.0 or changing based on opacity, depending on whether you have +\-\-blur\-background\-fixed\&. Yet the automatic adjustment of blur factor may not work well with a custom blur kernel\&. +.sp +A 7x7 Guassian blur kernel (sigma = 0\&.84089642) looks like: +.sp +.if n \{\ +.RS 4 +.\} +.nf +\-\-blur\-kern \*(Aq7,7,0\&.000003,0\&.000102,0\&.000849,0\&.001723,0\&.000849,0\&.000102,0\&.000003,0\&.000102,0\&.003494,0\&.029143,0\&.059106,0\&.029143,0\&.003494,0\&.000102,0\&.000849,0\&.029143,0\&.243117,0\&.493069,0\&.243117,0\&.029143,0\&.000849,0\&.001723,0\&.059106,0\&.493069,0\&.493069,0\&.059106,0\&.001723,0\&.000849,0\&.029143,0\&.243117,0\&.493069,0\&.243117,0\&.029143,0\&.000849,0\&.000102,0\&.003494,0\&.029143,0\&.059106,0\&.029143,0\&.003494,0\&.000102,0\&.000003,0\&.000102,0\&.000849,0\&.001723,0\&.000849,0\&.000102,0\&.000003\*(Aq +.fi +.if n \{\ +.RE +.\} +.sp +May also be one of the predefined kernels: +3x3box +(default), +5x5box, +7x7box, +3x3gaussian, +5x5gaussian, +7x7gaussian, +9x9gaussian, +11x11gaussian\&. All Guassian kernels are generated with sigma = 0\&.84089642 \&. You may use the accompanied +compton\-convgen\&.py +to generate blur kernels\&. +.RE +.PP +\fB\-\-blur\-background\-exclude\fR \fICONDITION\fR +.RS 4 +Exclude conditions for background blur\&. +.RE +.PP +\fB\-\-resize\-damage\fR \fIINTEGER\fR +.RS 4 +Resize damaged region by a specific number of pixels\&. A positive value enlarges it while a negative one shrinks it\&. If the value is positive, those additional pixels will not be actually painted to screen, only used in blur calculation, and such\&. (Due to technical limitations, with +\fB\-\-dbe\fR +or +\fB\-\-glx\-swap\-method\fR, those pixels will still be incorrectly painted to screen\&.) Primarily used to fix the line corruption issues of blur, in which case you should use the blur radius value here (e\&.g\&. with a 3x3 kernel, you should use +\fB\-\-resize\-damage\fR +1, with a 5x5 one you use +\fB\-\-resize\-damage\fR +2, and so on)\&. May or may not work with +\-\-glx\-no\-stencil\&. Shrinking doesn\(cqt function correctly\&. +.RE +.PP +\fB\-\-invert\-color\-include\fR \fICONDITION\fR +.RS 4 +Specify a list of conditions of windows that should be painted with inverted color\&. Resource\-hogging, and is not well tested\&. +.RE +.PP +\fB\-\-opacity\-rule\fR \fIOPACITY\fR:\*(AqCONDITION\*(Aq +.RS 4 +Specify a list of opacity rules, in the format +PERCENT:PATTERN, like +50:name *= "Firefox"\&. compton\-trans is recommended over this\&. Note we do not distinguish 100% and unset, and we don\(cqt make any guarantee about possible conflicts with other programs that set +\fI_NET_WM_WINDOW_OPACITY\fR +on frame or client windows\&. +.RE +.PP +\fB\-\-shadow\-exclude\-reg\fR \fIGEOMETRY\fR +.RS 4 +Specify a X geometry that describes the region in which shadow should not be painted in, such as a dock window region\&. Use +\-\-shadow\-exclude\-reg x10+0\-0, for example, if the 10 pixels on the bottom of the screen should not have shadows painted on\&. +.RE +.PP +\fB\-\-xinerama\-shadow\-crop\fR +.RS 4 +Crop shadow of a window fully on a particular Xinerama screen to the screen\&. +.RE +.PP +\fB\-\-backend\fR \fIBACKEND\fR +.RS 4 +Specify the backend to use: +xrender +or +glx\&. GLX (OpenGL) backend generally has much superior performance as far as you have a graphic card/chip and driver\&. +.RE +.PP +\fB\-\-glx\-no\-stencil\fR +.RS 4 +GLX backend: Avoid using stencil buffer, useful if you don\(cqt have a stencil buffer\&. Might cause incorrect opacity when rendering transparent content (but never practically happened) and may not work with +\fB\-\-blur\-background\fR\&. My tests show a 15% performance boost\&. Recommended\&. +.RE +.PP +\fB\-\-glx\-copy\-from\-front\fR +.RS 4 +GLX backend: Copy unmodified regions from front buffer instead of redrawing them all\&. My tests with nvidia\-drivers show a 10% decrease in performance when the whole screen is modified, but a 20% increase when only 1/4 is\&. My tests on nouveau show terrible slowdown\&. Useful with +\-\-glx\-swap\-method, as well\&. +.RE +.PP +\fB\-\-glx\-use\-copysubbuffermesa\fR +.RS 4 +GLX backend: Use +\fIMESA_copy_sub_buffer\fR +to do partial screen update\&. My tests on nouveau shows a 200% performance boost when only 1/4 of the screen is updated\&. May break VSync and is not available on some drivers\&. Overrides +\fB\-\-glx\-copy\-from\-front\fR\&. +.RE +.PP +\fB\-\-glx\-no\-rebind\-pixmap\fR +.RS 4 +GLX backend: Avoid rebinding pixmap on window damage\&. Probably could improve performance on rapid window content changes, but is known to break things on some drivers (LLVMpipe)\&. Recommended if it works\&. +.RE +.PP +\fB\-\-glx\-swap\-method\fR undefined/exchange/copy/3/4/5/6/buffer\-age +.RS 4 +GLX backend: GLX buffer swap method we assume\&. Could be +undefined +(0), +copy +(1), +exchange +(2), 3\-6, or +buffer\-age +(\-1)\&. +undefined +is the slowest and the safest, and the default value\&. +copy +is fastest, but may fail on some drivers, 2\-6 are gradually slower but safer (6 is still faster than 0)\&. Usually, double buffer means 2, triple buffer means 3\&. +buffer\-age +means auto\-detect using +\fIGLX_EXT_buffer_age\fR, supported by some drivers\&. Useless with +\fB\-\-glx\-use\-copysubbuffermesa\fR\&. Partially breaks +\-\-resize\-damage\&. Defaults to +undefined\&. +.RE +.PP +\fB\-\-glx\-use\-gpushader4\fR +.RS 4 +GLX backend: Use +\fIGL_EXT_gpu_shader4\fR +for some optimization on blur GLSL code\&. My tests on GTX 670 show no noticeable effect\&. +.RE +.PP +\fB\-\-dbus\fR +.RS 4 +Enable remote control via D\-Bus\&. See the +\fBD\-BUS API\fR +section below for more details\&. +.RE +.PP +\fB\-\-benchmark\fR \fICYCLES\fR +.RS 4 +Benchmark mode\&. Repeatedly paint until reaching the specified cycles\&. +.RE +.PP +\fB\-\-benchmark\-wid\fR \fIWINDOW_ID\fR +.RS 4 +Specify window ID to repaint in benchmark mode\&. If omitted or is 0, the whole screen is repainted\&. +.RE +.SH "FORMAT OF CONDITIONS" +.sp +Some options accept a condition string to match certain windows\&. A condition string is formed by one or more conditions, joined by logical operators\&. +.sp +A condition with "exists" operator looks like this: +.sp +.if n \{\ +.RS 4 +.\} +.nf + [] : +.fi +.if n \{\ +.RE +.\} +.sp +With equals operator it looks like: +.sp +.if n \{\ +.RS 4 +.\} +.nf + [] : = +.fi +.if n \{\ +.RE +.\} +.sp +With greater\-than/less\-than operators it looks like: +.sp +.if n \{\ +.RS 4 +.\} +.nf + [] : +.fi +.if n \{\ +.RE +.\} +.sp +\fINEGATION\fR (optional) is one or more exclamation marks; +.sp +\fITARGET\fR is either a predefined target name, or the name of a window property to match\&. Supported predefined targets are id, x, y, x2 (x + widthb), y2, width, height, widthb (width + 2 * border), heightb, override_redirect, argb (whether the window has an ARGB visual), focused, wmwin (whether the window looks like a WM window, i\&.e\&. has no child window with WM_STATE and is not override\-redirected), client (ID of client window), window_type (window type in string), leader (ID of window leader), name, class_g (= WM_CLASS[1]), class_i (= WM_CLASS[0]), and role\&. +.sp +\fICLIENT/FRAME\fR is a single @ if the window attribute should be be looked up on client window, nothing if on frame window; +.sp +\fIINDEX\fR (optional) is the index number of the property to look up\&. For example, [2] means look at the third value in the property\&. Do not specify it for predefined targets\&. +.sp +\fIFORMAT\fR (optional) specifies the format of the property, 8, 16, or 32\&. On absence we use format X reports\&. Do not specify it for predefined or string targets\&. +.sp +\fITYPE\fR is a single character representing the type of the property to match for: c for \fICARDINAL\fR, a for \fIATOM\fR, w for \fIWINDOW\fR, d for \fIDRAWABLE\fR, s for \fISTRING\fR (and any other string types, such as \fIUTF8_STRING\fR)\&. Do not specify it for predefined targets\&. +.sp +\fIOP QUALIFIER\fR (optional), applicable only for equals operator, could be ? (ignore\-case)\&. +.sp +\fIMATCH TYPE\fR (optional), applicable only for equals operator, could be nothing (exact match), * (match anywhere), ^ (match from start), % (wildcard), or ~ (PCRE regular expression)\&. +.sp +\fIOPERATOR\fR is one of = (equals), <, >, <=, =>, or nothing (exists)\&. Exists operator checks whether a property exists on a window (but for predefined targets, exists means != 0 then)\&. +.sp +\fIPATTERN\fR is either an integer or a string enclosed by single or double quotes\&. Python\-3\-style escape sequences and raw string are supported in the string format\&. +.sp +Supported logical operators are && (and) and || (or)\&. && has higher precedence than ||, left\-to\-right associativity\&. Use parentheses to change precedence\&. +.sp +Examples: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# If the window is focused +focused +focused = 1 +# If the window is not override\-redirected +!override_redirect +override_redirect = false +override_redirect != true +override_redirect != 1 +# If the window is a menu +window_type *= "menu" +_NET_WM_WINDOW_TYPE@:a *= "MENU" +# If the window name contains "Firefox", ignore case +name *?= "Firefox" +_NET_WM_NAME@:s *?= "Firefox" +# If the window name ends with "Firefox" +name %= "*Firefox" +name ~= "Firefox$" +# If the window has a property _COMPTON_SHADOW with value 0, type CARDINAL, +# format 32, value 0, on its frame window +_COMPTON_SHADOW:32c = 0 +# If the third value of _NET_FRAME_EXTENTS is less than 20, or there\*(Aqs no +# _NET_FRAME_EXTENTS property on client window +_NET_FRAME_EXTENTS@[2]:32c < 20 || !_NET_FRAME_EXTENTS@:32c +# The pattern here will be parsed as "dd4" +name = "\ex64\ex64\eo64" +# The pattern here will be parsed as "\ex64\ex64\ex64" +name = r"\ex64\ex64\eo64" +.fi +.if n \{\ +.RE +.\} +.SH "LEGACY FORMAT OF CONDITIONS" +.sp +This is the old condition format we once used\&. Support of this format might be removed in the future\&. +.sp +.if n \{\ +.RS 4 +.\} +.nf +condition = TARGET:TYPE[FLAGS]:PATTERN +.fi +.if n \{\ +.RE +.\} +.sp +\fITARGET\fR is one of "n" (window name), "i" (window class instance), "g" (window general class), and "r" (window role)\&. +.sp +\fITYPE\fR is one of "e" (exact match), "a" (match anywhere), "s" (match from start), "w" (wildcard), and "p" (PCRE regular expressions, if compiled with the support)\&. +.sp +\fIFLAGS\fR could be a series of flags\&. Currently the only defined flag is "i" (ignore case)\&. +.sp +\fIPATTERN\fR is the actual pattern string\&. +.SH "CONFIGURATION FILES" +.sp +compton could read from a configuration file if libconfig support is compiled in\&. If \fB\-\-config\fR is not used, compton will seek for a configuration file in $XDG_CONFIG_HOME/compton\&.conf (~/\&.config/compton\&.conf, usually), then ~/\&.compton\&.conf, then compton\&.conf under $XDG_DATA_DIRS (often /etc/xdg/compton\&.conf)\&. +.sp +compton uses general libconfig configuration file format\&. A sample configuration file is available as compton\&.sample\&.conf in the source tree\&. Most commandline switches each could be replaced with an option in configuration file, thus documented above\&. Window\-type\-specific settings are exposed only in configuration file and has the following format: +.sp +.if n \{\ +.RS 4 +.\} +.nf +wintypes: +{ + WINDOW_TYPE = { fade = BOOL; shadow = BOOL; opacity = FLOAT; focus = BOOL; }; +}; +.fi +.if n \{\ +.RE +.\} +.sp +\fIWINDOW_TYPE\fR is one of the 15 window types defined in EWMH standard: "unknown", "desktop", "dock", "toolbar", "menu", "utility", "splash", "dialog", "normal", "dropdown_menu", "popup_menu", "tooltip", "notify", "combo", and "dnd"\&. "fade" and "shadow" controls window\-type\-specific shadow and fade settings\&. "opacity" controls default opacity of the window type\&. "focus" controls whether the window of this type is to be always considered focused\&. (By default, all window types except "normal" and "dialog" has this on\&.) +.SH "SIGNALS" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +compton reinitializes itself upon receiving +SIGUSR1\&. +.RE +.SH "D-BUS API" +.sp +It\(cqs possible to control compton via D\-Bus messages, by running compton with \fB\-\-dbus\fR and send messages to com\&.github\&.chjj\&.compton\&.\&. is the display used by compton, with all non\-alphanumeric characters transformed to underscores\&. For DISPLAY=:0\&.0 you should use com\&.github\&.chjj\&.compton\&._0_0, for example\&. +.sp +The D\-Bus methods and signals are not yet stable, thus undocumented right now\&. +.SH "EXAMPLES" +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Disable configuration file parsing: +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ compton \-\-config /dev/null +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Run compton with client\-side shadow and fading, disable shadow on dock windows and drag\-and\-drop windows: +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ compton \-cCGf +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Same thing as above, plus making inactive windows 80% transparent, making frame 80% transparent, don\(cqt fade on window open/close, enable software optimization, and fork to background: +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ compton \-bcCGf \-i 0\&.8 \-e 0\&.8 \-\-no\-fading\-openclose \-\-sw\-opti +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Draw white shadows: +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ compton \-c \-\-shadow\-red 1 \-\-shadow\-green 1 \-\-shadow\-blue 1 +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Avoid drawing shadows on wbar window: +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ compton \-c \-\-shadow\-exclude \*(Aqclass_g = "wbar"\*(Aq +.fi +.if n \{\ +.RE +.\} +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +Enable OpenGL SGI_swap_control VSync with GLX backend: +.sp +.if n \{\ +.RS 4 +.\} +.nf +$ compton \-\-backend glx \-\-vsync opengl\-swc +.fi +.if n \{\ +.RE +.\} +.RE +.SH "BUGS" +.sp +Please report any you find to https://github\&.com/chjj/compton \&. +.SH "AUTHORS" +.sp +xcompmgr, originally written by Keith Packard, with contributions from Matthew Allum, Eric Anholt, Dan Doel, Thomas Luebking, Matthew Hawn, Ely Levy, Phil Blundell, and Carl Worth\&. Compton by Christopher Jeffrey, based on Dana Jansens\*(Aq original work, with contributions from Richard Grenville\&. +.SH "RESOURCES" +.sp +Homepage: https://github\&.com/chjj/compton +.SH "SEE ALSO" +.sp +\fBxcompmgr\fR(1), \fBcompton\-trans\fR(1) diff --git a/opengl.c b/twin/compton-tde/opengl.c similarity index 100% rename from opengl.c rename to twin/compton-tde/opengl.c diff --git a/opengl.h b/twin/compton-tde/opengl.h similarity index 100% rename from opengl.h rename to twin/compton-tde/opengl.h