/* * $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. */ #include #include #include #include #include #include #include #include #include typedef struct _win { struct _win *next; Window id; XWindowAttributes a; int damaged; int mode; Damage damage; Picture picture; XserverRegion borderSize; XserverRegion extents; Picture shadow; int shadow_dx; int shadow_dy; int shadow_width; int shadow_height; /* for drawing translucent windows */ XserverRegion borderClip; struct _win *prev_trans; } win; win *list; Display *dpy; int scr; Window root; Picture rootPicture; Picture rootBuffer; Picture transPicture; Picture rootTile; XserverRegion allDamage; int root_height, root_width; #define BACKGROUND_PROP "_XROOTPMAP_ID" #define WINDOW_PLAIN 0 #define WINDOW_DROP 1 #define WINDOW_TRANS 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) double gaussian (double r, double x, double y) { return ((1 / (sqrt (2 * M_PI * r))) * exp ((- (x * x + y * y)) / (2 * r * r))); } typedef struct _conv { int size; double *data; } conv; 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 +-----+-------------------+-----+ */ unsigned int sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) { int fx, fy; int sx, sy; 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)) << 24; } XImage * make_shadow (Display *dpy, double opacity, double r, int width, int height) { conv *map = make_gaussian_map (dpy, r); XImage *ximage; double *gdata = map->data; unsigned int *data; int gsize = map->size; int ylimit, xlimit; int swidth = width + gsize; int sheight = height + gsize; int center = gsize / 2; int x, y; int fx, fy; int sx, sy; unsigned int d; double v; unsigned char c; data = malloc (swidth * sheight * sizeof (int)); ximage = XCreateImage (dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 32, ZPixmap, 0, (char *) data, swidth, sheight, 32, swidth * sizeof (int)); /* * 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 (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 */ for (y = 0; y < ylimit; y++) { d = sum_gaussian (map, 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 (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; } } /* * center */ d = sum_gaussian (map, opacity, center, center, width, height); for (y = ylimit; y < sheight - ylimit; y++) for (x = xlimit; x < swidth - xlimit; x++) data[y * swidth + x] = d; free (map); return ximage; } Picture shadow_picture (Display *dpy, double opacity, double r, int width, int height, int *wp, int *hp) { XImage *shadowImage = make_shadow (dpy, opacity, r, width, height); Pixmap shadowPixmap = XCreatePixmap (dpy, root, shadowImage->width, shadowImage->height, 32); Picture shadowPicture = XRenderCreatePicture (dpy, shadowPixmap, XRenderFindStandardFormat (dpy, PictStandardARGB32), 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; } win * find_win (Display *dpy, Window id) { win *w; for (w = list; w; w = w->next) if (w->id == id) return w; return 0; } 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; if (XGetWindowProperty (dpy, root, XInternAtom (dpy, BACKGROUND_PROP, 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; } else { 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; } 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); } XserverRegion win_extents (Display *dpy, win *w) { XRectangle r; if (!w->shadow) { double opacity = SHADOW_OPACITY; if (w->mode == WINDOW_TRANS) opacity = opacity * TRANS_OPACITY; w->shadow = shadow_picture (dpy, opacity, SHADOW_RADIUS, w->a.width, w->a.height, &w->shadow_width, &w->shadow_height); w->shadow_dx = SHADOW_OFFSET_X; w->shadow_dy = SHADOW_OFFSET_Y; } r.x = w->a.x + w->a.border_width + w->shadow_dx; r.y = w->a.y + w->a.border_width + w->shadow_dy; r.width = w->shadow_width; r.height = w->shadow_height; return XFixesCreateRegion (dpy, &r, 1); } XserverRegion border_size (Display *dpy, win *w) { XserverRegion border; border = XFixesCreateRegionFromWindow (dpy, w->id, WindowRegionBounding); /* translate this */ XFixesUnionRegion (dpy, border, border, w->a.x, w->a.y, None, 0, 0); return border; } 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 (!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); } XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region); for (w = list; w; w = w->next) { Picture mask; if (w->a.map_state != IsViewable) continue; if (!w->picture) continue; 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_TRANS) { XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region); XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0); 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); XFixesUnionRegion (dpy, w->borderClip, region, 0, 0, None, 0, 0); w->prev_trans = t; t = w; } 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, w->shadow, None, rootBuffer, 0, 0, 0, 0, w->a.x + w->a.border_width + w->shadow_dx, w->a.y + w->a.border_width + 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); XFixesDestroyRegion (dpy, w->borderClip); w->borderClip = None; } XFixesDestroyRegion (dpy, region); XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None); XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture, 0, 0, 0, 0, 0, 0, root_width, root_height); } void add_damage (Display *dpy, XserverRegion damage) { if (allDamage) { XFixesUnionRegion (dpy, allDamage, allDamage, 0, 0, damage, 0, 0); XFixesDestroyRegion (dpy, damage); } else allDamage = damage; } void repair_win (Display *dpy, Window id) { win *w = find_win (dpy, id); XserverRegion parts; if (!w) return; /* printf ("repair 0x%x\n", w->id); */ parts = XFixesCreateRegion (dpy, 0, 0); /* translate region */ XDamageSubtract (dpy, w->damage, None, parts); XFixesUnionRegion (dpy, parts, parts, w->a.x, w->a.y, None, 0, 0); add_damage (dpy, parts); } void map_win (Display *dpy, Window id) { win *w = find_win (dpy, id); XserverRegion region; if (!w) return; w->a.map_state = IsViewable; if (w->picture) { w->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty); region = win_extents (dpy, w); add_damage (dpy, region); } } void unmap_win (Display *dpy, Window id) { win *w = find_win (dpy, id); if (!w) return; w->a.map_state = IsUnmapped; if (w->damage != None) { XDamageDestroy (dpy, w->damage); w->damage = None; } if (w->extents != None) { add_damage (dpy, w->extents); /* destroys region */ w->extents = None; } } void add_win (Display *dpy, Window id, Window prev) { win *new = malloc (sizeof (win)); win **p; XWindowAttributes a; XRenderPictureAttributes pa; 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->damaged = 0; new->damage = None; pa.subwindow_mode = IncludeInferiors; if (new->a.class == InputOnly) new->picture = 0; else { new->picture = XRenderCreatePicture (dpy, id, XRenderFindVisualFormat (dpy, new->a.visual), CPSubwindowMode, &pa); } new->shadow = None; new->borderSize = None; new->extents = None; if (new->a.override_redirect) new->mode = WINDOW_TRANS; else new->mode = WINDOW_DROP; new->next = *p; *p = new; if (new->a.map_state == IsViewable) map_win (dpy, id); } 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) XFixesUnionRegion (dpy, damage, w->extents, 0, 0, None, 0, 0); } 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; if (w->next) above = w->next->id; else above = None; if (above != ce->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 == ce->above) break; } w->next = *prev; *prev = w; } if (damage) { XserverRegion extents = win_extents (dpy, w); XFixesUnionRegion (dpy, damage, damage, 0, 0, extents, 0, 0); XFixesDestroyRegion (dpy, extents); add_damage (dpy, damage); } } 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); if (w->picture) XRenderFreePicture (dpy, w->picture); } *prev = w->next; free (w); break; } } void dump_win (win *w) { printf ("\t%08x: %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); } void dump_wins (void) { win *w; printf ("windows:\n"); for (w = list; w; w = w->next) dump_win (w); } void damage_win (Display *dpy, XDamageNotifyEvent *de) { repair_win (dpy, de->drawable); } int error (Display *dpy, XErrorEvent *ev) { printf ("error %d request %d minor %d\n", ev->error_code, ev->request_code, ev->minor_code); } void expose_root (Display *dpy, Window root, XRectangle *rects, int nrects) { XserverRegion region = XFixesCreateRegion (dpy, rects, nrects); add_damage (dpy, region); } int time_in_millis () { struct timeval tp; gettimeofday (&tp, 0); return(tp.tv_sec * 1000) + (tp.tv_usec / 1000); } #define INTERVAL 30 main () { XEvent ev; int event_base, error_base; Window root_return, parent_return; Window *children; Pixmap transPixmap; unsigned int nchildren; int i; int damage_event, damage_error; int xfixes_event, xfixes_error; XRenderPictureAttributes pa; XRenderColor c; XRectangle *expose_rects = 0; GC gc; int size_expose = 0; int n_expose = 0; struct pollfd ufd; int n; int last_update; int now; int timeout; dpy = XOpenDisplay (0); if (!dpy) { fprintf (stderr, "Can't open display\n"); exit (1); } XSetErrorHandler (error); scr = DefaultScreen (dpy); root = RootWindow (dpy, scr); pa.subwindow_mode = IncludeInferiors; 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); if (!XCompositeQueryExtension (dpy, &event_base, &error_base)) { fprintf (stderr, "No composite extension\n"); exit (1); } printf ("Composite error %d\n", error_base); if (!XDamageQueryExtension (dpy, &damage_event, &damage_error)) { fprintf (stderr, "No damage extension\n"); exit (1); } printf ("Damage error %d\n", damage_error); if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error)) { fprintf (stderr, "No XFixes extension\n"); exit (1); } printf ("XFixes error %d\n", xfixes_error); allDamage = None; XGrabServer (dpy); XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual); paint_all (dpy, None); 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); last_update = time_in_millis (); for (;;) { /* dump_wins (); */ do { XNextEvent (dpy, &ev); /* printf ("event %d\n", ev.type); */ 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); 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 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: if (ev.xproperty.atom == XInternAtom (dpy, BACKGROUND_PROP, False)) { if (rootTile) { XClearArea (dpy, root, 0, 0, 0, 0, True); XRenderFreePicture (dpy, rootTile); rootTile = None; } } break; default: if (ev.type == damage_event + XDamageNotify) damage_win (dpy, (XDamageNotifyEvent *) &ev); break; } } while (XEventsQueued (dpy, QueuedAfterReading)); now = time_in_millis (); 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; } last_update = time_in_millis(); if (allDamage) { paint_all (dpy, allDamage); allDamage = None; } } }