/* * $Id$ * * Copyright © 2003 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* Modified by Matthew Hawn. I don't know what to say here so follow what it says above. Not that I can really do anything about it */ #include #include #include #include #include #include #include #include #include #include #include #include typedef struct _win { struct _win *next; Window id; XWindowAttributes a; int mode; int damaged; Damage damage; Picture picture; XserverRegion borderSize; XserverRegion extents; Picture shadow; int shadow_dx; int shadow_dy; int shadow_width; int shadow_height; unsigned long damage_sequence; /* sequence when damage was created */ /* for drawing translucent windows */ XserverRegion borderClip; struct _win *prev_trans; } win; typedef struct _conv { int size; double *data; } conv; win *list; Display *dpy; int scr; Window root; Picture rootPicture; Picture rootBuffer; Picture transPicture; Picture blackPicture; Picture rootTile; XserverRegion allDamage; int root_height, root_width; /* find these once and be done with it */ Atom transPropAtom; Atom intAtom; /* translucency property name */ #define TRANS_PROP "CM_TRANSLUCENCY" conv *gaussianMap; #define WINDOW_SOLID 0 #define WINDOW_TRANS 1 #define WINDOW_ARGB 2 #define TRANS_OPACITY 0.75 #define SHADOW_RADIUS 15 #define SHADOW_OPACITY 0.75 #define SHADOW_OFFSET_X (-SHADOW_RADIUS) #define SHADOW_OFFSET_Y (-SHADOW_RADIUS) #define DEBUG 0 #define MONITOR_REPAINT 0 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); for (y = 0; y < size; y++) for (x = 0; x < size; x++) { g = gaussian (r, (double) (x - center), (double) (y - center)); t += g; c->data[y * size + x] = g; } /* printf ("gaussian total %f\n", t); */ for (y = 0; y < size; y++) for (x = 0; x < size; x++) { c->data[y*size + x] /= t; } return c; } /* * A picture will help * * -center 0 width width+center * -center +-----+-------------------+-----+ * | | | | * | | | | * 0 +-----+-------------------+-----+ * | | | | * | | | | * | | | | * height +-----+-------------------+-----+ * | | | | * height+ | | | | * center +-----+-------------------+-----+ */ static unsigned char sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) { int fx, fy; double *g_data; double *g_line = map->data; int g_size = map->size; int center = g_size / 2; int fx_start, fx_end; int fy_start, fy_end; double v; /* * Compute set of filter values which are "in range", * that's the set with: * 0 <= x + (fx-center) && x + (fx-center) < width && * 0 <= y + (fy-center) && y + (fy-center) < height * * 0 <= x + (fx - center) x + fx - center < width * center - x <= fx fx < width + center - x */ fx_start = center - x; if (fx_start < 0) fx_start = 0; fx_end = width + center - x; if (fx_end > g_size) fx_end = g_size; fy_start = center - y; if (fy_start < 0) fy_start = 0; fy_end = height + center - y; if (fy_end > g_size) fy_end = g_size; g_line = g_line + fy_start * g_size + fx_start; v = 0; for (fy = fy_start; fy < fy_end; fy++) { g_data = g_line; g_line += g_size; for (fx = fx_start; fx < fx_end; fx++) v += *g_data++; } if (v > 1) v = 1; return ((unsigned int) (v * opacity * 255.0)); } static XImage * make_shadow (Display *dpy, double opacity, int width, int height) { XImage *ximage; unsigned char *data; int gsize = gaussianMap->size; int ylimit, xlimit; int swidth = width + gsize; int sheight = height + gsize; int center = gsize / 2; int x, y; unsigned char d; data = malloc (swidth * sheight * sizeof (unsigned char)); ximage = XCreateImage (dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 8, ZPixmap, 0, (char *) data, swidth, sheight, 8, swidth * sizeof (unsigned char)); /* * Build the gaussian in sections */ /* * 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++) { d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height); data[y * swidth + x] = d; data[(sheight - y - 1) * swidth + x] = d; data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; data[y * swidth + (swidth - x - 1)] = d; } /* * top/bottom */ for (y = 0; y < ylimit; y++) { d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height); for (x = gsize; x < swidth - gsize; x++) { data[y * swidth + x] = d; data[(sheight - y - 1) * swidth + x] = d; } } /* * sides */ for (x = 0; x < xlimit; x++) { d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height); for (y = gsize; y < sheight - gsize; y++) { data[y * swidth + x] = d; data[y * swidth + (swidth - x - 1)] = d; } } /* * center */ d = sum_gaussian (gaussianMap, opacity, center, center, width, height); for (y = ylimit; y < sheight - ylimit; y++) for (x = xlimit; x < swidth - xlimit; x++) data[y * swidth + x] = d; return ximage; } static Picture shadow_picture (Display *dpy, double opacity, int width, int height, int *wp, int *hp) { XImage *shadowImage = make_shadow (dpy, opacity, width, height); Pixmap shadowPixmap = XCreatePixmap (dpy, root, shadowImage->width, shadowImage->height, 8); Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap, XRenderFindStandardFormat (dpy, PictStandardA8), 0, 0); GC gc = XCreateGC (dpy, shadowPixmap, 0, 0); XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, shadowImage->width, shadowImage->height); *wp = shadowImage->width; *hp = shadowImage->height; XFreeGC (dpy, gc); XDestroyImage (shadowImage); XFreePixmap (dpy, shadowPixmap); return shadowPicture; } static win * find_win (Display *dpy, Window id) { win *w; for (w = list; w; w = w->next) if (w->id == id) return w; return 0; } static char *backgroundProps[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", 0, }; static Picture root_tile (Display *dpy) { Picture picture; Atom actual_type; Pixmap pixmap; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char *prop; Bool fill; XRenderPictureAttributes pa; int p; for (p = 0; backgroundProps[p]; p++) { if (XGetWindowProperty (dpy, root, XInternAtom (dpy, backgroundProps[p], False), 0, 4, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1) { memcpy (&pixmap, prop, 4); XFree (prop); fill = False; break; } } if (!backgroundProps[p]) { 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 (!rootTile) rootTile = root_tile (dpy); XRenderComposite (dpy, PictOpSrc, rootTile, None, rootBuffer, 0, 0, 0, 0, 0, 0, root_width, root_height); } static XserverRegion win_extents (Display *dpy, win *w) { XRectangle r; r.x = w->a.x; r.y = w->a.y; r.width = w->a.width + w->a.border_width * 2; r.height = w->a.height + w->a.border_width * 2; if (w->mode != WINDOW_ARGB) { XRectangle sr; if (!w->shadow) { double opacity = SHADOW_OPACITY; if (w->mode == WINDOW_TRANS) opacity = opacity * TRANS_OPACITY; w->shadow = 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); w->shadow_dx = SHADOW_OFFSET_X; w->shadow_dy = SHADOW_OFFSET_Y; } 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.width > r.width) r.width = sr.width; if (sr.height > r.height) r.height = sr.height; } return XFixesCreateRegion (dpy, &r, 1); } static XserverRegion border_size (Display *dpy, win *w) { XserverRegion border; border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding); /* translate this */ XFixesTranslateRegion (dpy, border, w->a.x + w->a.border_width, w->a.y + w->a.border_width); return border; } static void paint_all (Display *dpy, XserverRegion region) { win *w; win *t = 0; if (!region) { XRectangle r; r.x = 0; r.y = 0; r.width = root_width; r.height = root_height; region = XFixesCreateRegion (dpy, &r, 1); } #if MONITOR_REPAINT rootBuffer = rootPicture; #else if (!rootBuffer) { Pixmap rootPixmap = XCreatePixmap (dpy, root, root_width, root_height, DefaultDepth (dpy, scr)); rootBuffer = XRenderCreatePicture (dpy, rootPixmap, XRenderFindVisualFormat (dpy, DefaultVisual (dpy, scr)), 0, 0); XFreePixmap (dpy, rootPixmap); } #endif XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region); #if MONITOR_REPAINT XRenderComposite (dpy, PictOpSrc, blackPicture, None, rootPicture, 0, 0, 0, 0, 0, 0, root_width, root_height); #endif #if DEBUG printf ("paint:"); #endif for (w = list; w; w = w->next) { if (w->a.map_state != IsViewable) continue; /* never painted, ignore it */ if (!w->damaged) continue; if (!w->picture) continue; #if DEBUG printf (" 0x%x", w->id); #endif if (w->borderSize) XFixesDestroyRegion (dpy, w->borderSize); w->borderSize = border_size (dpy, w); if (w->extents) XFixesDestroyRegion (dpy, w->extents); w->extents = win_extents (dpy, w); if (w->mode == WINDOW_SOLID) { XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region); XFixesSubtractRegion (dpy, region, region, w->borderSize); XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer, 0, 0, 0, 0, w->a.x + w->a.border_width, w->a.y + w->a.border_width, w->a.width, w->a.height); } w->borderClip = XFixesCreateRegion (dpy, 0, 0); XFixesCopyRegion (dpy, w->borderClip, region); w->prev_trans = t; t = w; } #if DEBUG printf ("\n"); #endif XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region); paint_root (dpy); for (w = t; w; w = w->prev_trans) { XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); if (w->shadow) { XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer, 0, 0, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height); } if (w->mode == WINDOW_TRANS) XRenderComposite (dpy, PictOpOver, w->picture, transPicture, rootBuffer, 0, 0, 0, 0, w->a.x + w->a.border_width, w->a.y + w->a.border_width, w->a.width, w->a.height); else if (w->mode == WINDOW_ARGB) XRenderComposite (dpy, PictOpOver, w->picture, None, rootBuffer, 0, 0, 0, 0, w->a.x + w->a.border_width, w->a.y + w->a.border_width, w->a.width, w->a.height); XFixesDestroyRegion (dpy, w->borderClip); w->borderClip = None; } XFixesDestroyRegion (dpy, region); if (rootBuffer != rootPicture) { XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None); XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture, 0, 0, 0, 0, 0, 0, root_width, root_height); } } static void add_damage (Display *dpy, XserverRegion damage) { if (allDamage) { XFixesUnionRegion (dpy, allDamage, allDamage, damage); XFixesDestroyRegion (dpy, damage); } else allDamage = damage; } static void repair_win (Display *dpy, Window id) { win *w = find_win (dpy, id); XserverRegion parts; if (!w) return; if (!w->damaged) { parts = win_extents (dpy, w); XDamageSubtract (dpy, w->damage, None, None); } else { parts = XFixesCreateRegion (dpy, 0, 0); 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; } static void map_win (Display *dpy, Window id, unsigned long sequence) { win *w = find_win (dpy, id); if (!w) return; w->a.map_state = IsViewable; /* make sure we know if property was changed */ XSelectInput(dpy, id, PropertyChangeMask); w->damaged = 0; } static void unmap_win (Display *dpy, Window id) { win *w = find_win (dpy, id); if (!w) return; w->a.map_state = IsUnmapped; w->damaged = 0; if (w->extents != None) { add_damage (dpy, w->extents); /* destroys region */ w->extents = None; } /* don't care about properties anymore */ XSelectInput(dpy, id, 0); } /* Get the translucency prop from window not found: -1 otherwise the value */ static int get_trans_prop(Display *dpy, win *w) { Atom actual; int format; unsigned long n, left; char *data; XGetWindowProperty(dpy, w->id, transPropAtom, 0L, 1L, False, intAtom, &actual, &format, &n, &left, (unsigned char **) &data); if (data != None) { int i = (int) *data; XFree( (void *) data); return i; } return -1; } /* determine mode for window all in one place. Future might check for menu flag and other cool things */ static int determine_mode(Display *dpy, win *w) { int mode; /* if trans prop == -1 fall back on previous tests*/ int p = get_trans_prop(dpy, w); if ( p != -1 ) { if (p > 0) { /* don't really care about the value as long as > 0 */ mode = WINDOW_TRANS; } else { mode = WINDOW_SOLID; } } else { XRenderPictFormat *format; 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->a.override_redirect) { /* changed this as a lot of window managers set this for all top level windows */ mode = WINDOW_SOLID; } else { mode = WINDOW_SOLID; } } return mode; } static void add_win (Display *dpy, Window id, Window prev) { win *new = malloc (sizeof (win)); win **p; XRenderPictureAttributes pa; XRenderPictFormat *format; if (!new) return; if (prev) { for (p = &list; *p; p = &(*p)->next) if ((*p)->id == prev) break; } else p = &list; new->id = id; if (!XGetWindowAttributes (dpy, id, &new->a)) { free (new); return; } new->damage = None; new->damaged = 0; new->damage = None; pa.subwindow_mode = IncludeInferiors; if (new->a.class == InputOnly) { new->picture = 0; format = 0; new->damage_sequence = 0; } else { format = XRenderFindVisualFormat (dpy, new->a.visual); new->picture = XRenderCreatePicture (dpy, id, format, CPSubwindowMode, &pa); new->damage_sequence = NextRequest (dpy); new->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty); } new->shadow = None; new->shadow_dx = 0; new->shadow_dy = 0; new->shadow_width = 0; new->shadow_height = 0; new->borderSize = None; new->extents = None; /* moved mode setting to one place */ new->mode = determine_mode(dpy, new); new->next = *p; *p = new; if (new->a.map_state == IsViewable) map_win (dpy, id, new->damage_sequence - 1); } void restack_win (Display *dpy, win *w, Window new_above) { Window old_above; if (w->next) old_above = w->next->id; else old_above = None; if (old_above != new_above) { win **prev; /* unhook */ for (prev = &list; *prev; prev = &(*prev)->next) if ((*prev) == w) break; *prev = w->next; /* rehook */ for (prev = &list; *prev; prev = &(*prev)->next) { if ((*prev)->id == new_above) break; } w->next = *prev; *prev = w; } } static void configure_win (Display *dpy, XConfigureEvent *ce) { win *w = find_win (dpy, ce->window); Window above; XserverRegion damage = None; if (!w) { if (ce->window == root) { if (rootBuffer) { XRenderFreePicture (dpy, rootBuffer); rootBuffer = None; } root_width = ce->width; root_height = ce->height; } return; } if (w->a.map_state == IsViewable) { 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 (w->shadow) { XRenderFreePicture (dpy, w->shadow); w->shadow = None; } w->a.width = ce->width; w->a.height = ce->height; w->a.border_width = ce->border_width; w->a.override_redirect = ce->override_redirect; restack_win (dpy, w, ce->above); if (damage) { XserverRegion extents = win_extents (dpy, w); XFixesUnionRegion (dpy, damage, damage, extents); XFixesDestroyRegion (dpy, extents); add_damage (dpy, damage); } } static void circulate_win (Display *dpy, XCirculateEvent *ce) { win *w = find_win (dpy, ce->window); Window new_above; if (ce->place == PlaceOnTop) new_above = list->id; else new_above = None; restack_win (dpy, w, new_above); } static void destroy_win (Display *dpy, Window id, Bool gone) { win **prev, *w; for (prev = &list; (w = *prev); prev = &w->next) if (w->id == id) { if (!gone) unmap_win (dpy, id); *prev = w->next; if (w->picture) XRenderFreePicture (dpy, w->picture); if (w->damage != None) XDamageDestroy (dpy, w->damage); free (w); break; } } /* static void dump_win (win *w) { printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id, w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width); } static void dump_wins (void) { win *w; printf ("windows:\n"); for (w = list; w; w = w->next) dump_win (w); } */ static void damage_win (Display *dpy, XDamageNotifyEvent *de) { repair_win (dpy, de->drawable); } static int error (Display *dpy, XErrorEvent *ev) { printf ("error %d request %d minor %d\n", ev->error_code, ev->request_code, ev->minor_code); return 0; } static void expose_root (Display *dpy, Window root, XRectangle *rects, int nrects) { XserverRegion region = XFixesCreateRegion (dpy, rects, nrects); add_damage (dpy, region); } #define INTERVAL 0 #if INTERVAL static int time_in_millis (void) { struct timeval tp; gettimeofday (&tp, 0); return(tp.tv_sec * 1000) + (tp.tv_usec / 1000); } #endif #define INTERVAL 0 static int ev_serial (XEvent *ev) { if (ev->type & 0x7f != KeymapNotify) return ev->xany.serial; return NextRequest (ev->xany.display); } int damage_event, damage_error; 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; } } int main (int argc, char **argv) { XEvent ev; int event_base, error_base; Window root_return, parent_return; Window *children; Pixmap transPixmap; Pixmap blackPixmap; unsigned int nchildren; int i; int xfixes_event, xfixes_error; XRenderPictureAttributes pa; XRenderColor c; XRectangle *expose_rects = 0; int size_expose = 0; int n_expose = 0; struct pollfd ufd; int n; int last_update; int now; int p; #if INTERVAL int timeout; #endif dpy = XOpenDisplay (0); if (!dpy) { fprintf (stderr, "Can't open display\n"); exit (1); } XSetErrorHandler (error); scr = DefaultScreen (dpy); root = RootWindow (dpy, scr); /* get atoms */ transPropAtom = XInternAtom(dpy, TRANS_PROP, False); intAtom = XInternAtom(dpy, "INTEGER", True); pa.subwindow_mode = IncludeInferiors; gaussianMap = make_gaussian_map(dpy, SHADOW_RADIUS); transPixmap = XCreatePixmap (dpy, root, 1, 1, 8); pa.repeat = True; transPicture = XRenderCreatePicture (dpy, transPixmap, XRenderFindStandardFormat (dpy, PictStandardA8), CPRepeat, &pa); c.red = c.green = c.blue = 0; c.alpha = 0xc0c0; XRenderFillRectangle (dpy, PictOpSrc, transPicture, &c, 0, 0, 1, 1); root_width = DisplayWidth (dpy, scr); root_height = DisplayHeight (dpy, scr); rootPicture = XRenderCreatePicture (dpy, root, XRenderFindVisualFormat (dpy, DefaultVisual (dpy, scr)), CPSubwindowMode, &pa); blackPixmap = XCreatePixmap (dpy, root, 1, 1, 32); pa.repeat = True; blackPicture = XRenderCreatePicture (dpy, blackPixmap, XRenderFindStandardFormat (dpy, PictStandardARGB32), CPRepeat, &pa); c.red = c.green = c.blue = 0; c.alpha = 0xffff; XRenderFillRectangle (dpy, PictOpSrc, blackPicture, &c, 0, 0, 1, 1); if (!XCompositeQueryExtension (dpy, &event_base, &error_base)) { fprintf (stderr, "No composite extension\n"); exit (1); } 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); } allDamage = None; 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); XFree (children); XUngrabServer (dpy); paint_all (dpy, None); #if INTERVAL last_update = time_in_millis (); #endif for (;;) { #if INTERVAL int busy_start = 0; #endif /* dump_wins (); */ do { XNextEvent (dpy, &ev); #if INTERVAL if (!busy_start) busy_start = time_in_millis(); #endif #if DEBUG 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 CreateNotify: add_win (dpy, ev.xcreatewindow.window, 0); break; case ConfigureNotify: configure_win (dpy, &ev.xconfigure); break; case DestroyNotify: destroy_win (dpy, ev.xdestroywindow.window, True); break; case MapNotify: map_win (dpy, ev.xmap.window, ev.xmap.serial); break; case UnmapNotify: unmap_win (dpy, ev.xunmap.window); break; case ReparentNotify: if (ev.xreparent.parent == root) add_win (dpy, ev.xreparent.window, 0); else destroy_win (dpy, ev.xreparent.window, False); break; case CirculateNotify: circulate_win (dpy, &ev.xcirculate); break; case Expose: if (ev.xexpose.window == root) { int more = ev.xexpose.count + 1; if (n_expose == size_expose) { if (expose_rects) { expose_rects = realloc (expose_rects, (size_expose + more) * sizeof (XRectangle)); size_expose += more; } else { expose_rects = malloc (more * sizeof (XRectangle)); size_expose = more; } } expose_rects[n_expose].x = ev.xexpose.x; expose_rects[n_expose].y = ev.xexpose.y; expose_rects[n_expose].width = ev.xexpose.width; expose_rects[n_expose].height = ev.xexpose.height; n_expose++; if (ev.xexpose.count == 0) { expose_root (dpy, root, expose_rects, n_expose); n_expose = 0; } } break; case PropertyNotify: for (p = 0; backgroundProps[p]; p++) { if (ev.xproperty.atom == XInternAtom (dpy, backgroundProps[p], False)) { if (rootTile) { XClearArea (dpy, root, 0, 0, 0, 0, True); XRenderFreePicture (dpy, rootTile); rootTile = None; break; } } } /* check if Trans property was changed */ if (ev.xproperty.atom == transPropAtom) { /* reset mode and redraw window */ win * w = find_win(dpy, ev.xproperty.window); if (w) { w->mode = determine_mode(dpy, w); if (w->extents) { XserverRegion damage; damage = XFixesCreateRegion (dpy, 0, 0); XFixesCopyRegion (dpy, damage, w->extents); add_damage (dpy, damage); } } } break; default: if (ev.type == damage_event + XDamageNotify) damage_win (dpy, (XDamageNotifyEvent *) &ev); break; } } while (QLength (dpy)); #if INTERVAL now = time_in_millis (); /* printf ("\t\tbusy %d\n", now - busy_start); */ timeout = INTERVAL - (now - last_update); if (timeout > 0) { ufd.fd = ConnectionNumber (dpy); ufd.events = POLLIN; n = poll (&ufd, 1, timeout); if (n > 0 && (ufd.revents & POLLIN) && XEventsQueued (dpy, QueuedAfterReading)) continue; } #endif if (allDamage) { #if INTERVAL int old_update = last_update; last_update = time_in_millis(); /* printf ("delta %d\n", last_update - old_update); */ #endif paint_all (dpy, allDamage); allDamage = None; } } }