/* * $Id$ * * Copyright © 2003 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* Modified by Matthew Hawn. I don't know what to say here so follow what it says above. Not that I can really do anything about it */ /* Modified by Dan Doel*/ /* Modified by Timothy Pearson * * CHANGELOG: * http://patchwork.freedesktop.org/patch/1049/ [Add default background color option] 08/11/2011 * http://patchwork.freedesktop.org/patch/1052/ [Prevent flicker on root pixmap change] 08/11/2011 * Added SIGUSR1 handler to change process UID [Prevent flicker on login] 08/12/2011 * Added ability to write PID of process to home directory 08/14/2011 * Added SIGUSR2 handler to reload settings [Prevent flicker on settings change] 08/14/2011 * Added SIGTERM handler to clean up stale PID files on exit 08/14/2011 * Added hack to work around ATI fglrx XDamage event generation bugs [WORK_AROUND_FGLRX] 09/01/2011 * * TODO: * http://patchwork.freedesktop.org/patch/1053/ [Fix window mapping with re-used window ids] */ /* Version 2.x of xcompmgr, kompmgr changes by Thomas L�bking and Heiko Przybyl check baghira.sf.net for more infos */ #define _VERSION_ 2.02 #include #include #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 1 #define WORK_AROUND_FGLRX 1 #define _TOPHEIGHT_(x) ((x >> 24) & 0xff) #define _RIGHTWIDTH_(x) ((x >> 16) & 0xff) #define _BOTTOMHEIGHT_(x) ((x >> 8) & 0xff) #define _LEFTWIDTH_(x) (x & 0xff) /* #define USE_ENV_HOME 1 */ #define WRITE_PID_FILE 1 #ifndef USE_ENV_HOME #include #endif typedef enum { 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; #if HAS_NAME_WINDOW_PIXMAP Pixmap pixmap; #endif XWindowAttributes a; XWindowAttributes a_prev; #if CAN_DO_USABLE Bool usable; /* mapped and all damaged at one point */ XRectangle damage_bounds; /* bounds of damage */ #endif Bool isInFade; int mode; int damaged; Damage damage; Picture picture; Picture alphaPict; Picture shadowPict; XserverRegion borderSize; XserverRegion decoRegion; XserverRegion contentRegion; XserverRegion extents; unsigned int preShadeOpacity; Picture shadow; /*Picture alpha;*/ int shadow_dx; int shadow_dy; int shadow_width; int shadow_height; unsigned int opacity; unsigned int shadowSize; wintype windowType; unsigned long damage_sequence; /* sequence when damage was created */ int destroyed; Bool destruct_queued; Bool destruct_requested; int destruct_request_time; Bool shapable; /* this will allow window managers to exclude windows if just the deco is shaped*/ Bool shaped; XRectangle shape_bounds; XRectangle shape_bounds_prev; unsigned int decoHash; Picture dimPicture; /* for drawing translucent windows */ XserverRegion borderClip; struct _win *prev_trans; /* setting whether a window will be transparent to the desktop or the windows below it */ Bool show_root_tile; } win; typedef struct _conv { int size; double *data; } conv; typedef struct _fade { struct _fade *next; win *w; double cur; double finish; double step; void (*callback) (Display *dpy, win *w, Bool gone); Display *dpy; unsigned int decoHash; Bool gone; } fade; struct sigaction usr_action; sigset_t block_mask; int my_exit_code = 3; win *list; fade *fades; Display *dpy; char *display = 0; int scr; Window root; Picture rootPicture; Picture rootBuffer; Picture blackPicture; Picture transBlackPicture; Picture rootTile; XserverRegion allDamage; Bool clipChanged; #if HAS_NAME_WINDOW_PIXMAP Bool hasNamePixmap; #endif XRenderColor fill_color; int root_height, root_width; ignore *ignore_head, **ignore_tail = &ignore_head; int xfixes_event, xfixes_error; int damage_event, damage_error; int composite_event, composite_error; int render_event, render_error; int xshape_event, xshape_error; Bool synchronize; int composite_opcode; Bool screen_damaged = False; Bool disable_argb = False; int shapeEvent; /* find these once and be done with it */ Atom opacityAtom; Atom shadowAtom; Atom shadeAtom; Atom shapableAtom; Atom decoHashAtom; Atom dimAtom; Atom deskChangeAtom; Atom winTypeAtom; Atom winTDETTDAtom; Atom winType[NUM_WINTYPES]; double winTypeOpacity[NUM_WINTYPES]; Bool winTypeShadow[NUM_WINTYPES]; Bool winTypeFade[NUM_WINTYPES]; /* opacity property name; sometime soon I'll write up an EWMH spec for it */ #define OPACITY_PROP "_KDE_WM_WINDOW_OPACITY" #define SHADOW_PROP "_KDE_WM_WINDOW_SHADOW" #define SHADE_PROP "_KDE_WM_WINDOW_SHADE" #define SHAPABLE_PROP "_KDE_WM_WINDOW_SHAPABLE" #define DECOHASH_PROP "_KDE_WM_WINDOW_DECOHASH" #define DIM_PROP "_KDE_WM_WINDOW_DIM" #define DESKCHANGE_PROP "_KDE_WM_DESKTOP_CHANGE" #define TRANSLUCENT 0xe0000000 #define OPAQUE 0xffffffff conv *gaussianMap; #define WINDOW_SOLID 0 #define WINDOW_TRANS 1 #define WINDOW_ARGB 2 #define TRANS_OPACITY 0.75 #define NDEBUG 1 #define DEBUG_REPAINT 0 #define DEBUG_WINDOWS 0 #define DEBUG_EVENTS 0 #define MONITOR_REPAINT 0 #define SHADOWS 1 #define SHARP_SHADOW 0 typedef enum _compMode { CompSimple, /* looks like a regular X server */ CompServerShadows, /* use window alpha for shadow; sharp, but precise */ CompClientShadows /* use window extents for shadow, blurred */ } CompMode; static void determine_mode(Display *dpy, win *w); static double get_opacity_percent(Display *dpy, win *w); static XserverRegion win_extents (Display *dpy, win *w); CompMode compMode = CompSimple; int shadowRadius = 12; int shadowOffsetX = 0; int shadowOffsetY = 0; double shadowOpacity = .75; XRenderColor shadowColor; double fade_in_step = 0.028; double fade_out_step = 0.03; int fade_delta = 10; int fade_time = 0; Bool fadeTrans = False; Bool autoRedirect = False; #if WORK_AROUND_FGLRX Bool restartOnSigterm = True; #endif /* For shadow precomputation */ int Gsize = -1; unsigned char *shadowCorner = NULL; unsigned char *shadowTop = NULL; XRenderPictFormat* sXRenderFindVisualFormat(Display *dpy, _Xconst Visual *visual) { XRenderPictFormat* format = XRenderFindVisualFormat(dpy,visual); if (format) return format; else return XRenderFindStandardFormat (dpy, PictStandardRGB24); } int get_time_in_milliseconds () { struct timeval tv; gettimeofday (&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } 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 = "/.kompmgr.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 = "/.kompmgr.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("kompmgr lost connection to X server, restarting...\n\r"); fflush(stdout); sleep(1); char me[2048]; int chars = readlink("/proc/self/exe", me, sizeof(me)); me[chars] = 0; me[2047] = 0; execl(me, NULL); } #endif } void handle_siguser (int sig) { int uidnum; if (sig == SIGTERM) { // Trap this signal and keep running... return; } if (sig == SIGUSR1) { char newuid[1024]; #ifndef NDEBUG printf("Enter the new user ID:\n\r"); 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 kompmgr process uid to %d...\n\r", 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)) { #ifdef USE_ENV_HOME const char *home = getenv("HOME"); #else const char *home; struct passwd *p; p = getpwuid(uidnum); if (p) home = p->pw_dir; else home = getenv("HOME"); #endif const char *filename; const char *configfile = "/.xcompmgrrc"; int n = strlen(home)+strlen(configfile)+1; filename = (char*)malloc(n*sizeof(char)); memset(filename,0,n); strcat(filename, home); strcat(filename, configfile); loadConfig(filename); /* reload the configuration file */ free(filename); filename = NULL; } } fade * find_fade (win *w) { fade *f; for (f = fades; f; f = f->next) { if (f->w == w) return f; } return 0; } void dequeue_fade (Display *dpy, fade *f) { fade **prev; f->w->isInFade = False; f->w->decoHash = f->decoHash; for (prev = &fades; *prev; prev = &(*prev)->next) if (*prev == f) { *prev = f->next; if (f->callback) { (*f->callback) (dpy, f->w, f->gone); } free (f); break; } } void cleanup_fade (Display *dpy, win *w) { fade *f = find_fade (w); if (f) dequeue_fade (dpy, f); } void enqueue_fade (Display *dpy, fade *f) { f->w->isInFade = True; if (!fades) fade_time = get_time_in_milliseconds () + fade_delta; f->next = fades; fades = f; } static void unmap_callback (Display *dpy, win *w, Bool gone); static void set_fade (Display *dpy, win *w, double start, double finish, double step, void (*callback) (Display *dpy, win *w, Bool gone), Bool gone, Bool exec_callback, Bool override, Bool wholeWin) { fade *f; f = find_fade (w); if (!f) { if (start == finish) return; f = malloc (sizeof (fade)); f->next = 0; f->w = w; f->decoHash = w->decoHash; f->cur = start; enqueue_fade (dpy, f); } else if(!override) return; else { if (exec_callback && f->callback) (*f->callback)(dpy, f->w, f->gone); } if (finish < 0) finish = 0; if (finish > 1) finish = 1; f->finish = finish; if (f->cur < finish) f->step = step; else if (f->cur > finish) f->step = -step; f->gone = gone && (exec_callback || f->callback != unmap_callback); f->callback = callback; w->opacity = f->cur * OPAQUE; if (wholeWin) w->decoHash = 0; #if 0 printf ("set_fade start %g step %g\n", f->cur, f->step); #endif determine_mode (dpy, w); if (w->shadow) { XRenderFreePicture (dpy, w->shadow); w->shadow = None; if( w->extents != None ) XFixesDestroyRegion( dpy, w->extents ); 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; } int fade_timeout (void) { int now; int delta; if (!fades) return -1; now = get_time_in_milliseconds(); delta = fade_time - now; if (delta < 0) delta = 0; /* printf ("timeout %d\n", delta); */ return delta; } void run_fades (Display *dpy) { int now = get_time_in_milliseconds(); fade *f, *next; int steps; Bool need_dequeue; #if 0 printf ("run fades\n"); #endif if (fade_time - now > 0) return; steps = 1 + (now - fade_time) / fade_delta; for (next = fades; (f = next); ) { win *w = f->w; next = f->next; f->cur += f->step * steps; if (f->cur >= 1) f->cur = 1; else if (f->cur < 0) f->cur = 0; #if 0 printf ("opacity now %g -> %g\n", f->cur, f->finish); #endif w->opacity = f->cur * OPAQUE; 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; } } if (w->shadow) { XRenderFreePicture (dpy, w->shadow); w->shadow = None; if( w->extents != None ) XFixesDestroyRegion( dpy, w->extents ); w->extents = win_extents(dpy, w); } 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; } #define SHADOW_OFFSET_X ((-shadowRadius * 7 / 5) - shadowOffsetX * shadowRadius / 100) * w->shadowSize #define SHADOW_OFFSET_Y ((-shadowRadius * 7 / 5) - shadowOffsetY * shadowRadius / 100) * w->shadowSize /*#define SHADOW_OFFSET_X (w->shadowSize * -shadowRadius * 7 / 500) - w->shadowSize * shadowOffsetX * shadowRadius / 10000 #define SHADOW_OFFSET_Y (w->shadowSize * -shadowRadius * 7 / 500) - w->shadowSize * shadowOffsetY * shadowRadius / 10000*/ static double gaussian (double r, double x, double y) { return ((1 / (sqrt (2 * M_PI * r))) * exp ((- (x * x + y * y)) / (2 * r * r))); } static conv * make_gaussian_map (Display *dpy, double r) { conv *c; int size = ((int) ceil ((r * 3)) + 1) & ~1; int center = size / 2; int x, y; double t; double g; c = malloc (sizeof (conv) + size * size * sizeof (double)); c->size = size; c->data = (double *) (c + 1); t = 0.0; for (y = 0; y < size; y++) for (x = 0; x < size; x++) { g = gaussian (r, (double) (x - center), (double) (y - center)); t += g; c->data[y * size + x] = g; } /* printf ("gaussian total %f\n", t); */ for (y = 0; y < size; y++) for (x = 0; x < size; x++) { c->data[y*size + x] /= t; } return c; } /* * A picture will help * * -center 0 width width+center * -center +-----+-------------------+-----+ * | | | | * | | | | * 0 +-----+-------------------+-----+ * | | | | * | | | | * | | | | * height +-----+-------------------+-----+ * | | | | * height+ | | | | * center +-----+-------------------+-----+ */ static unsigned char sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) { int fx, fy; double *g_data; double *g_line = map->data; int g_size = map->size; int center = g_size / 2; int fx_start, fx_end; int fy_start, fy_end; double v; /* * Compute set of filter values which are "in range", * that's the set with: * 0 <= x + (fx-center) && x + (fx-center) < width && * 0 <= y + (fy-center) && y + (fy-center) < height * * 0 <= x + (fx - center) x + fx - center < width * center - x <= fx fx < width + center - x */ fx_start = center - x; if (fx_start < 0) fx_start = 0; fx_end = width + center - x; if (fx_end > g_size) fx_end = g_size; fy_start = center - y; if (fy_start < 0) fy_start = 0; fy_end = height + center - y; if (fy_end > g_size) fy_end = g_size; g_line = g_line + fy_start * g_size + fx_start; v = 0; for (fy = fy_start; fy < fy_end; fy++) { g_data = g_line; g_line += g_size; for (fx = fx_start; fx < fx_end; fx++) v += *g_data++; } if (v > 1) v = 1; return ((unsigned char) (v * opacity * 255.0)); } /* precompute shadow corners and sides to save time for large windows */ static void presum_gaussian (conv *map) { int center = map->size/2; int opacity, x, y; Gsize = map->size; if (shadowCorner) free ((void *)shadowCorner); if (shadowTop) free ((void *)shadowTop); shadowCorner = (unsigned char *)(malloc ((Gsize + 1) * (Gsize + 1) * 26)); shadowTop = (unsigned char *)(malloc ((Gsize + 1) * 26)); for (x = 0; x <= Gsize; x++) { shadowTop[25 * (Gsize + 1) + x] = sum_gaussian (map, 1, x - center, center, Gsize * 2, Gsize * 2); for(opacity = 0; opacity < 25; opacity++) shadowTop[opacity * (Gsize + 1) + x] = shadowTop[25 * (Gsize + 1) + x] * opacity / 25; for(y = 0; y <= x; y++) { shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] = sum_gaussian (map, 1, x - center, y - center, Gsize * 2, Gsize * 2); shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; for(opacity = 0; opacity < 25; opacity++) shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] = shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] * opacity / 25; } } } static XImage * make_shadow (Display *dpy, double opacity, int width, int height) { XImage *ximage; unsigned char *data; int gsize = gaussianMap->size; int ylimit, xlimit; int swidth = width + gsize; int sheight = height + gsize; int center = gsize / 2; int x, y; unsigned char d; int x_diff; int opacity_int = (int)(opacity * 25); data = malloc (swidth * sheight * sizeof (unsigned char)); if (!data) return 0; ximage = XCreateImage (dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 8, ZPixmap, 0, (char *) data, swidth, sheight, 8, swidth * sizeof (unsigned char)); if (!ximage) { free (data); return 0; } /* * Build the gaussian in sections */ /* * center (fill the complete data array) */ if (Gsize > 0) d = shadowTop[opacity_int * (Gsize + 1) + Gsize]; else d = sum_gaussian (gaussianMap, opacity, center, center, width, height); memset(data, d, sheight * swidth); /* * corners */ ylimit = gsize; if (ylimit > sheight / 2) ylimit = (sheight + 1) / 2; xlimit = gsize; if (xlimit > swidth / 2) xlimit = (swidth + 1) / 2; for (y = 0; y < ylimit; y++) for (x = 0; x < xlimit; x++) { if (xlimit == Gsize && ylimit == Gsize) d = shadowCorner[opacity_int * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; else d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height); data[y * swidth + x] = d; data[(sheight - y - 1) * swidth + x] = d; data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; data[y * swidth + (swidth - x - 1)] = d; } /* * top/bottom */ x_diff = swidth - (gsize * 2); if (x_diff > 0 && ylimit > 0) { for (y = 0; y < ylimit; y++) { if (ylimit == Gsize) d = shadowTop[opacity_int * (Gsize + 1) + y]; else d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height); memset (&data[y * swidth + gsize], d, x_diff); memset (&data[(sheight - y - 1) * swidth + gsize], d, x_diff); } } /* * sides */ for (x = 0; x < xlimit; x++) { if (xlimit == Gsize) d = shadowTop[opacity_int * (Gsize + 1) + x]; else d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height); for (y = gsize; y < sheight - gsize; y++) { data[y * swidth + x] = d; data[y * swidth + (swidth - x - 1)] = d; } } return ximage; } static Picture shadow_picture (Display *dpy, double opacity, Picture alpha_pict, int width, int height, int *wp, int *hp) { XImage *shadowImage; Pixmap shadowPixmap; Pixmap finalPixmap; Picture shadowPicture; Picture finalPicture; GC gc; shadowImage = make_shadow (dpy, opacity, width, height); if (!shadowImage) return None; shadowPixmap = XCreatePixmap (dpy, root, shadowImage->width, shadowImage->height, 8); if (!shadowPixmap) { XDestroyImage (shadowImage); return None; } shadowPicture = XRenderCreatePicture (dpy, shadowPixmap, XRenderFindStandardFormat (dpy, PictStandardA8), 0, 0); if (!shadowPicture) { XDestroyImage (shadowImage); XFreePixmap (dpy, shadowPixmap); return None; } gc = XCreateGC (dpy, shadowPixmap, 0, 0); if (!gc) { XDestroyImage (shadowImage); XFreePixmap (dpy, shadowPixmap); XRenderFreePicture (dpy, shadowPicture); return None; } XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, shadowImage->width, shadowImage->height); *wp = shadowImage->width; *hp = shadowImage->height; XFreeGC (dpy, gc); XDestroyImage (shadowImage); XFreePixmap (dpy, shadowPixmap); return shadowPicture; } Picture solid_picture (Display *dpy, Bool argb, double a, double r, double g, double b) { Pixmap pixmap; Picture picture; XRenderPictureAttributes pa; XRenderColor c; pixmap = XCreatePixmap (dpy, root, 1, 1, argb ? 32 : 8); if (!pixmap) return None; pa.repeat = True; picture = XRenderCreatePicture (dpy, pixmap, XRenderFindStandardFormat (dpy, argb ? PictStandardARGB32 : PictStandardA8), CPRepeat, &pa); if (!picture) { XFreePixmap (dpy, pixmap); return None; } c.alpha = a * 0xffff; c.red = r * 0xffff; c.green = g * 0xffff; c.blue = b * 0xffff; XRenderFillRectangle (dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); XFreePixmap (dpy, pixmap); return picture; } void discard_ignore (Display *dpy, unsigned long sequence) { while (ignore_head) { if ((long) (sequence - ignore_head->sequence) > 0) { ignore *next = ignore_head->next; free (ignore_head); ignore_head = next; if (!ignore_head) ignore_tail = &ignore_head; } else break; } } void set_ignore (Display *dpy, unsigned long sequence) { ignore *i = malloc (sizeof (ignore)); if (!i) return; i->sequence = sequence; i->next = 0; *ignore_tail = i; ignore_tail = &i->next; } int should_ignore (Display *dpy, unsigned long sequence) { discard_ignore (dpy, sequence); return ignore_head && ignore_head->sequence == sequence; } static win * find_win (Display *dpy, Window id) { win *w; for (w = list; w; w = w->next) if ((!w->destroyed) && (w->id == id)) return w; return 0; } static char *backgroundProps[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", 0, }; static Picture root_tile (Display *dpy) { Picture picture; Atom actual_type; Pixmap pixmap; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char *prop; Bool fill; XRenderPictureAttributes pa; int p; pixmap = None; for (p = 0; backgroundProps[p]; p++) { if (XGetWindowProperty (dpy, root, XInternAtom (dpy, backgroundProps[p], False), 0, 4, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && actual_type == XInternAtom (dpy, "PIXMAP", False) && actual_format == 32 && nitems == 1) { pixmap = *(long*)prop; XFree (prop); fill = False; break; } } if (!pixmap) { pixmap = XCreatePixmap (dpy, root, 1, 1, DefaultDepth (dpy, scr)); fill = True; } pa.repeat = True; picture = XRenderCreatePicture (dpy, pixmap, sXRenderFindVisualFormat (dpy, DefaultVisual (dpy, scr)), CPRepeat, &pa); if (fill) { XRenderFillRectangle (dpy, PictOpSrc, picture, &fill_color, 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 (winTypeShadow[w->windowType]) { if (compMode == CompServerShadows || w->mode != WINDOW_ARGB) { XRectangle sr; if (compMode == CompServerShadows) { w->shadow_dx = 2; w->shadow_dy = 7; w->shadow_width = w->a.width; w->shadow_height = w->a.height; } else { w->shadow_dx = SHADOW_OFFSET_X; w->shadow_dx = w->shadow_dx / 100; w->shadow_dy = SHADOW_OFFSET_Y; w->shadow_dy = w->shadow_dy / 100; if (!w->shadow) { double opacity = shadowOpacity; if (w->shadowSize > 100) opacity = opacity/(w->shadowSize*0.015); if (w->mode == WINDOW_TRANS) opacity = opacity * ((double)w->opacity)/((double)OPAQUE); w->shadow = shadow_picture (dpy, opacity, w->alphaPict, w->a.width + w->a.border_width * 2 - 2*(shadowRadius - (w->shadowSize*shadowRadius/100)) , w->a.height + w->a.border_width * 2 - 2*(shadowRadius - (w->shadowSize*shadowRadius/100)), &w->shadow_width, &w->shadow_height); /*int kill; w->alpha = shadow_picture (dpy, 0.9, w->alphaPict, w->a.width + w->a.border_width * 2, w->a.height + w->a.border_width * 2, &kill, &kill);*/ } } 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 XserverRegion deco_region (Display *dpy, win *w) { XserverRegion title; XRectangle r; /*titlebounding rect*/ /* * 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. */ r.x = w->a.x - w->a.border_width + _LEFTWIDTH_(w->decoHash); r.y = w->a.y - w->a.border_width + _TOPHEIGHT_(w->decoHash); r.width = w->a.width + w->a.border_width * 2 - _LEFTWIDTH_(w->decoHash) - _RIGHTWIDTH_(w->decoHash); r.height = w->a.height + w->a.border_width - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash); set_ignore (dpy, NextRequest (dpy)); title = XFixesCreateRegion (dpy, &r, 1); if (!w->borderSize) w->borderSize = border_size (dpy, w); set_ignore (dpy, NextRequest (dpy)); XFixesSubtractRegion(dpy, title, w->borderSize, title); return title; } static void finish_destroy_win (Display *dpy, Window id, Bool gone); static XserverRegion content_region (Display *dpy, win *w) { XserverRegion content; XRectangle r; /*contentbounding rect*/ /* * 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. */ r.x = w->a.x - w->a.border_width + _LEFTWIDTH_(w->decoHash); r.y = w->a.y - w->a.border_width + _TOPHEIGHT_(w->decoHash); r.width = w->a.width + w->a.border_width * 2 - _LEFTWIDTH_(w->decoHash) - _RIGHTWIDTH_(w->decoHash); r.height = w->a.height + w->a.border_width - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash); set_ignore (dpy, NextRequest (dpy)); content = XFixesCreateRegion (dpy, &r, 1); if (!w->borderSize) w->borderSize = border_size (dpy, w); set_ignore (dpy, NextRequest (dpy)); XFixesIntersectRegion(dpy, content, w->borderSize, content); return content; } static void paint_all (Display *dpy, XserverRegion region) { win *w; win *t = 0; #if DEBUG_WINDOWS int window_count = 0; #endif if (!region) { XRectangle r; r.x = 0; r.y = 0; r.width = root_width; r.height = root_height; region = XFixesCreateRegion (dpy, &r, 1); } #if MONITOR_REPAINT rootBuffer = rootPicture; #else if (!rootBuffer) { Pixmap rootPixmap = XCreatePixmap (dpy, root, root_width, root_height, DefaultDepth (dpy, scr)); rootBuffer = XRenderCreatePicture (dpy, rootPixmap, sXRenderFindVisualFormat (dpy, DefaultVisual (dpy, scr)), 0, 0); XFreePixmap (dpy, rootPixmap); } #endif XFixesSetPictureClipRegion (dpy, rootPicture, 0, 0, region); #if MONITOR_REPAINT XRenderComposite (dpy, PictOpSrc, blackPicture, None, rootPicture, 0, 0, 0, 0, 0, 0, root_width, root_height); #endif #if DEBUG_REPAINT printf ("paint:"); #endif // Time delayed garbage collect // It waits 10 seconds before destroying window data // This allows the fade out to perform smoothly under all conditions // Yes, this code is somewhat inefficient! // But it shouldn't matter unless someone has tens of thousands of windows open... // If the user can set a fade out that is longer than 10 seconds, // then the value must be increased. I am assuming that 10 seconds // is far too long for any normal human being to wait... ;-) for (w = list; w; w = w->next) { if (w->destruct_requested) { int curtime = get_time_in_milliseconds(); if ((curtime - w->destruct_request_time) > 10000) { finish_destroy_win (dpy, w->id, True); w = list; } } } for (w = list; w; w = w->next) { #if DEBUG_WINDOWS window_count++; #endif #if CAN_DO_USABLE if (!w->usable) continue; #endif /* never painted, ignore it */ if ((!screen_damaged) && (!w->damaged)) continue; /* skip invisible windows */ if (w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= root_width || w->a.y >= root_height) continue; if (!w->picture) { XRenderPictureAttributes pa; XRenderPictFormat *format; Drawable draw = w->id; #if HAS_NAME_WINDOW_PIXMAP if (hasNamePixmap && !w->pixmap) w->pixmap = XCompositeNameWindowPixmap (dpy, w->id); if (w->pixmap) draw = w->pixmap; #endif format = sXRenderFindVisualFormat (dpy, w->a.visual); pa.subwindow_mode = IncludeInferiors; w->picture = XRenderCreatePicture (dpy, draw, format, CPSubwindowMode, &pa); } #if DEBUG_REPAINT printf (" 0x%x", w->id); #endif if (clipChanged) { if (w->borderSize) { set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, w->borderSize); w->borderSize = None; } if (w->decoRegion) { set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, w->decoRegion); w->decoRegion = None; } if (w->contentRegion) { set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, w->contentRegion); w->contentRegion = None; } if (w->extents) { XFixesDestroyRegion (dpy, w->extents); w->extents = None; } if (w->borderClip) { XFixesDestroyRegion (dpy, w->borderClip); w->borderClip = None; } } if (!w->borderSize) w->borderSize = border_size (dpy, w); if (!w->extents) w->extents = win_extents (dpy, w); if ((w->mode == WINDOW_SOLID) || ((w->mode == WINDOW_TRANS) && w->decoHash)) { int x, y, wid, hei; #if HAS_NAME_WINDOW_PIXMAP x = w->a.x; y = w->a.y; wid = w->a.width + w->a.border_width * 2; hei = w->a.height + w->a.border_width * 2; #else x = w->a.x + w->a.border_width; y = w->a.y + w->a.border_width; wid = w->a.width; hei = w->a.height; #endif XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region); set_ignore (dpy, NextRequest (dpy)); /*XFixesSubtractRegion (dpy, region, region, w->borderSize); set_ignore (dpy, NextRequest (dpy));*/ if (w->mode == WINDOW_SOLID) { XFixesSubtractRegion (dpy, region, region, w->borderSize); set_ignore (dpy, NextRequest (dpy)); XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer, 0, 0, 0, 0, x, y, wid, hei); if (w->dimPicture) XRenderComposite (dpy, PictOpOver, w->dimPicture, None, rootBuffer, 0, 0, 0, 0, x, y, wid, hei); } else { if (!w->contentRegion) w->contentRegion = content_region (dpy, w); XFixesSubtractRegion (dpy, region, region, w->contentRegion); set_ignore (dpy, NextRequest (dpy)); /*solid part*/ XRenderComposite (dpy, PictOpSrc, w->picture, None, rootBuffer, _LEFTWIDTH_(w->decoHash), _TOPHEIGHT_(w->decoHash), 0, 0, x + _LEFTWIDTH_(w->decoHash), y + _TOPHEIGHT_(w->decoHash), wid - _LEFTWIDTH_(w->decoHash) - _RIGHTWIDTH_(w->decoHash), hei - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash)); } } if (!w->borderClip) { w->borderClip = XFixesCreateRegion (dpy, 0, 0); XFixesCopyRegion (dpy, w->borderClip, region); XFixesIntersectRegion(dpy, w->borderClip, w->borderClip, w->borderSize); } w->prev_trans = t; t = w; } #if DEBUG_WINDOWS printf("window count: %d\n\r", window_count); #endif #if DEBUG_REPAINT printf ("\n"); fflush (stdout); #endif XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, region); paint_root (dpy); for (w = t; w; w = w->prev_trans) { if (w->shadowSize > 0){ if (winTypeShadow[w->windowType]) { switch (compMode) { case CompSimple: break; case CompServerShadows: XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); set_ignore (dpy, NextRequest (dpy)); if (w->opacity != OPAQUE && !w->shadowPict) w->shadowPict = solid_picture (dpy, True, (double) w->opacity / OPAQUE * 0.3, 0, 0, 0); XRenderComposite (dpy, PictOpOver, w->shadowPict ? w->shadowPict : transBlackPicture, w->picture, rootBuffer, 0, 0, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height); break; case CompClientShadows: if (w->shadow) { XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); XRenderComposite (dpy, PictOpOver, blackPicture, w->shadow, rootBuffer, 0, 0, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, w->shadow_width, w->shadow_height); } break; } } } if (w->opacity != OPAQUE && !w->alphaPict) w->alphaPict = solid_picture (dpy, False, (double) w->opacity / OPAQUE, shadowColor.red, shadowColor.green, shadowColor.blue); if (w->mode == WINDOW_TRANS) { int x, y, wid, hei; XFixesIntersectRegion (dpy, w->borderClip, w->borderClip, w->borderSize); XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); #if HAS_NAME_WINDOW_PIXMAP x = w->a.x; y = w->a.y; wid = w->a.width + w->a.border_width * 2; hei = w->a.height + w->a.border_width * 2; #else x = w->a.x + w->a.border_width; y = w->a.y + w->a.border_width; wid = w->a.width; hei = w->a.height; #endif set_ignore (dpy, NextRequest (dpy)); if (!w->decoHash) { XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, 0, 0, 0, 0, x, y, wid, hei); } else { /*trans part*/ /* PICTURE ;) |-----------------------------| | top | |-----------------------------| |l | | r| |e | | i| |f | | g| |t | | h| |--------------------------| t| | bottom | | |--------------------------|--|*/ /*top*/ XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, 0, 0, 0, 0, x, y, wid, _TOPHEIGHT_(w->decoHash)); /*right*/ XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, wid - _RIGHTWIDTH_(w->decoHash), _TOPHEIGHT_(w->decoHash), 0, 0, x + wid - _RIGHTWIDTH_(w->decoHash), y + _TOPHEIGHT_(w->decoHash), _RIGHTWIDTH_(w->decoHash), hei - _TOPHEIGHT_(w->decoHash)); /*bottom*/ XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, 0, hei - _BOTTOMHEIGHT_(w->decoHash), 0, 0, x, y + hei - _BOTTOMHEIGHT_(w->decoHash), wid - _RIGHTWIDTH_(w->decoHash), _BOTTOMHEIGHT_(w->decoHash)); /*left*/ XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, 0, _TOPHEIGHT_(w->decoHash), 0, 0, x, y + _TOPHEIGHT_(w->decoHash), _LEFTWIDTH_(w->decoHash), hei - _TOPHEIGHT_(w->decoHash) - _BOTTOMHEIGHT_(w->decoHash)); } } else if (w->mode == WINDOW_ARGB) { int x, y, wid, hei; XFixesIntersectRegion (dpy, w->borderClip, w->borderClip, w->borderSize); XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, w->borderClip); #if HAS_NAME_WINDOW_PIXMAP x = w->a.x; y = w->a.y; wid = w->a.width + w->a.border_width * 2; hei = w->a.height + w->a.border_width * 2; #else x = w->a.x + w->a.border_width; y = w->a.y + w->a.border_width; wid = w->a.width; hei = w->a.height; #endif set_ignore (dpy, NextRequest (dpy)); /* 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 rootBuffer before continuing! */ if (w->isInFade == False) { if (w->show_root_tile == True) { XRenderComposite (dpy, PictOpSrc, rootTile, None, rootBuffer, x, y, x, y, x, y, wid, hei); } } XRenderComposite (dpy, PictOpOver, w->picture, w->alphaPict, rootBuffer, 0, 0, 0, 0, x, y, wid, hei); } XFixesDestroyRegion (dpy, w->borderClip); w->borderClip = None; } XFixesDestroyRegion (dpy, region); if (rootBuffer != rootPicture) { #if 0 XTransform t; t.matrix[0][0] = XDoubleToFixed (3.0 /*/ scale*/); t.matrix[0][1] = 0.0; t.matrix[0][2] = 0.0; t.matrix[1][0] = 0.0; t.matrix[1][1] = XDoubleToFixed (1.0 /*/ scale*/); t.matrix[1][2] = 0.0; t.matrix[2][0] = 0.0; t.matrix[2][1] = 0.0; t.matrix[2][2] = XDoubleToFixed (1.0); XRenderSetPictureTransform (dpy, rootBuffer, &t); #endif XFixesSetPictureClipRegion (dpy, rootBuffer, 0, 0, None); XRenderComposite (dpy, PictOpSrc, rootBuffer, None, rootPicture, 0, 0, 0, 0, 0, 0, root_width, root_height); } screen_damaged = False; } static void add_damage (Display *dpy, XserverRegion damage) { if (allDamage) { XFixesUnionRegion (dpy, allDamage, allDamage, damage); XFixesDestroyRegion (dpy, damage); } else allDamage = damage; } static void damage_win (Display *dpy, XDamageNotifyEvent *de); static void repair_win (Display *dpy, win *w) { XserverRegion parts; if (!w->damaged) { parts = win_extents (dpy, w); set_ignore (dpy, NextRequest (dpy)); XDamageSubtract (dpy, w->damage, None, None); } else { XserverRegion o; parts = XFixesCreateRegion (dpy, 0, 0); set_ignore (dpy, NextRequest (dpy)); XDamageSubtract (dpy, w->damage, None, parts); XFixesTranslateRegion (dpy, parts, w->a.x + w->a.border_width, w->a.y + w->a.border_width); if (compMode == CompServerShadows) { o = XFixesCreateRegion (dpy, 0, 0); XFixesCopyRegion (dpy, o, parts); XFixesTranslateRegion (dpy, o, w->shadow_dx, w->shadow_dy); XFixesUnionRegion (dpy, parts, parts, o); XFixesDestroyRegion (dpy, o); } } add_damage (dpy, parts); w->damaged = 1; } static 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; } 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)-1; off = 0; do { set_ignore (dpy, NextRequest (dpy)); int result = XGetWindowProperty (dpy, w, winTypeAtom, off, 1L, False, XA_ATOM, &actual, &format, &n, &left, &data); if (result != Success) break; if (data != None) { int i; for (i = 0; i < NUM_WINTYPES; ++i) { Atom a; memcpy (&a, data, sizeof (Atom)); if (a == winType[i]) { /* known type */ ret = i; break; } } XFree ( (void *) data); } ++off; } while (left >= 4 && ret == (wintype)-1); 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)-1) 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)-1; } for (i = 0;i < nchildren;i++) { type = determine_wintype (dpy, children[i], top); if (type != (wintype)-1) return type; } if (children) XFree ((void *)children); if (w != top) return (wintype)-1; else return WINTYPE_NORMAL; } static unsigned int get_opacity_prop(Display *dpy, win *w, unsigned int def); static void map_win (Display *dpy, Window id, unsigned long sequence, Bool fade) { win *w = find_win (dpy, id); Drawable back; if (!w) return; w->a.map_state = IsViewable; /* This needs to be here or else we lose transparency messages */ XSelectInput (dpy, id, PropertyChangeMask); /* This needs to be here since we don't get PropertyNotify when unmapped */ w->opacity = get_opacity_prop (dpy, w, OPAQUE); determine_mode (dpy, w); w->windowType = determine_wintype (dpy, w->id, w->id); if ((w->windowType < 0) || (w->windowType > NUM_WINTYPES)) w->windowType = WINTYPE_NORMAL; #if 0 printf("window 0x%x type %s\n", w->id, wintype_name(w->windowType)); #endif #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 WORK_AROUND_FGLRX if (w->a.x != 0) { XserverRegion extents = win_extents (dpy, w); XDamageNotifyEvent de; de.drawable = w->id; de.area.x = 0; de.area.y = 0; de.area.width = w->a.width + w->a.border_width * 2; de.area.height = w->a.height + w->a.border_width * 2; damage_win(dpy, &de); XFixesDestroyRegion (dpy, extents); } #endif w->a_prev = w->a; if (fade && winTypeFade[w->windowType]) set_fade (dpy, w, 0, get_opacity_prop(dpy, w, OPAQUE)*1.0/OPAQUE, fade_in_step, 0, False, True, True, True); } static void finish_unmap_win (Display *dpy, win *w) { w->damaged = 0; #if CAN_DO_USABLE w->usable = False; #endif if (w->extents != None) { add_damage (dpy, w->extents); /* destroys region */ w->extents = None; } #if HAS_NAME_WINDOW_PIXMAP if (w->pixmap) { XFreePixmap (dpy, w->pixmap); w->pixmap = None; } #endif if (w->picture) { set_ignore (dpy, NextRequest (dpy)); XRenderFreePicture (dpy, w->picture); w->picture = None; } /* don't care about properties anymore */ set_ignore (dpy, NextRequest (dpy)); XSelectInput(dpy, w->id, 0); if (w->borderSize) { set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, w->borderSize); w->borderSize = None; } if (w->decoRegion) { set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, w->decoRegion); w->decoRegion = None; } if (w->contentRegion) { set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, w->contentRegion); w->contentRegion = None; } if (w->shadow) { XRenderFreePicture (dpy, w->shadow); w->shadow = None; } if (w->borderClip) { XFixesDestroyRegion (dpy, w->borderClip); w->borderClip = None; } clipChanged = True; } #if HAS_NAME_WINDOW_PIXMAP static void unmap_callback (Display *dpy, win *w, Bool gone) { finish_unmap_win (dpy, w); } #endif static void unmap_win (Display *dpy, Window id, Bool fade) { win *w = find_win (dpy, id); if (!w) return; if (w->a.map_state != IsUnmapped) { w->a.map_state = IsUnmapped; #if HAS_NAME_WINDOW_PIXMAP if (w->pixmap && fade && winTypeFade[w->windowType]) { set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, unmap_callback, False, False, True, True); } else #endif finish_unmap_win (dpy, w); } } /* Get the opacity prop from window not found: default otherwise the value */ static unsigned int get_opacity_prop(Display *dpy, win *w, unsigned int def) { Atom actual; int format; unsigned long n, left; unsigned char *data; int result = XGetWindowProperty(dpy, w->id, opacityAtom, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL && format == 32 ) { unsigned int i; i = *(long*)data; XFree( (void *) data); return i; } return def; } static unsigned int get_shadow_prop(Display *dpy, win *w) { Atom actual; int format; unsigned long n, left; unsigned char *data = NULL; int result = XGetWindowProperty(dpy, w->id, shadowAtom, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL && format == 32 ) { unsigned int i; i = *(long*)data; XFree( (void *) data); /*i added this for security reaons but limiting a value to 200% is somewhat indiscriminate if (i > 200) return 200; else*/ return i; } return 100; } static unsigned int get_shade_prop(Display *dpy, win *w) { Atom actual; int format; unsigned long n, left; unsigned char *data = NULL; int result = XGetWindowProperty(dpy, w->id, shadeAtom, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL && format == 32 ) { unsigned int i; i = *(long*)data; XFree( (void *) data); return i; } return 0; } static Bool get_shapable_prop(Display *dpy, win *w) { Atom actual; int format; unsigned long n, left; unsigned char *data = NULL; int result = XGetWindowProperty(dpy, w->id, shapableAtom, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL && format == 32 ) { unsigned int i; i = *(long*)data; XFree( (void *) data); return i==1; } return True; /*in general, the window should be shapable*/ } static unsigned int get_decoHash_prop(Display *dpy, win *w) { Atom actual; int format; unsigned long n, left; unsigned char *data = NULL; int result = XGetWindowProperty(dpy, w->id, decoHashAtom, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if (result == Success && data != NULL && format == 32 ) { unsigned int i; i = *(long*)data; XFree( (void *) data); return i; } return 0; /*no titlebar*/ } static unsigned int get_dim_prop(Display *dpy, win *w) { Atom actual; int format; unsigned long n, left; unsigned char *data = NULL; int result = XGetWindowProperty(dpy, w->id, dimAtom, 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); if (i == 0) i = 1; return i; } return OPAQUE; /*in general, the window is not dimmed*/ } static unsigned int get_deskchange_prop(Display *dpy, Window id) { Atom actual; int format; unsigned long n, left; unsigned char *data = NULL; int result = XGetWindowProperty(dpy, id, deskChangeAtom, 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); if (i < 3) return i; } return 0; /*no valid change state*/ } /* Get the opacity property from the window in a percent format not found: default otherwise: the value */ static double get_opacity_percent(Display *dpy, win *w) { if (w && w->isInFade) { fade *f = find_fade(w); return f->finish; } else { double def = winTypeOpacity[w->windowType]; unsigned int opacity = get_opacity_prop (dpy, w, (unsigned int)(OPAQUE*def)); return opacity*1.0/OPAQUE; } } #if 0 static void damage_shape(Display *dpy, win *w, XRectangle *shape_damage) { set_ignore (dpy, NextRequest (dpy)); XserverRegion region = XFixesCreateRegion (dpy, shape_damage, 1); set_ignore (dpy, NextRequest (dpy)); XserverRegion tmpRegion; add_damage(dpy, region); win *i; XRectangle *rect; int n; for (i = w; i; i = i->next) { XFixesIntersectRegion (dpy, tmpRegion, region, w->extents); rect = XFixesFetchRegion (dpy, region, &n); free(rect); printf("%d\n",n); if (n != 1) { w->damage = True; XFixesSubtractRegion (dpy, region, region, w->extents); } else break; } set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, tmpRegion); set_ignore (dpy, NextRequest (dpy)); XFixesDestroyRegion (dpy, region); } #endif static Bool get_window_transparent_to_desktop(Display * dpy, Window w) { Atom actual; int format; unsigned long n, left; unsigned char *data; int result = XGetWindowProperty (dpy, w, winTDETTDAtom, 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 void determine_mode(Display *dpy, win *w) { int mode; XRenderPictFormat *format; unsigned int default_opacity; /* if trans prop == -1 fall back on previous tests*/ if (w->alphaPict) { XRenderFreePicture (dpy, w->alphaPict); w->alphaPict = None; } if (w->shadowPict) { XRenderFreePicture (dpy, w->shadowPict); w->shadowPict = None; } if (w->a.class == InputOnly) { format = 0; } else { format = XRenderFindVisualFormat (dpy, w->a.visual); } if (!disable_argb && 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 Bool determine_window_transparent_to_desktop (Display *dpy, Window w) { Window root_return, parent_return; Window *children = NULL; unsigned int nchildren, i; Bool type; type = get_window_transparent_to_desktop (dpy, w); if (type == True) return True; if (!XQueryTree (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 (dpy, children[i]); if (type == True) return True; } if (children) XFree ((void *)children); return False; } static void add_win (Display *dpy, Window id, Window prev) { win *new = malloc (sizeof (win)); win **p; unsigned int tmp; 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->shaped = False; new->shape_bounds.x = new->a.x; new->shape_bounds.y = new->a.y; new->shape_bounds_prev = new->shape_bounds; new->shape_bounds.width = new->a.width; new->shape_bounds.height = new->a.height; new->a_prev = new->a; new->damaged = 0; #if CAN_DO_USABLE new->usable = False; #endif #if HAS_NAME_WINDOW_PIXMAP new->pixmap = None; #endif new->picture = None; if (new->a.class == InputOnly) { new->damage_sequence = 0; new->damage = None; } else { new->damage_sequence = NextRequest (dpy); new->damage = XDamageCreate (dpy, id, XDamageReportNonEmpty); XShapeSelectInput (dpy, id, ShapeNotifyMask); } new->isInFade = False; new->alphaPict = None; new->shadowPict = None; new->borderSize = None; new->decoRegion = None; new->contentRegion = 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->destruct_queued = False; new->destruct_requested = False; new->destruct_request_time = 0; new->shadowSize = 100; new->decoHash = 0; new->show_root_tile = determine_window_transparent_to_desktop(dpy, id); new->windowType = determine_wintype (dpy, new->id, new->id); if ((new->windowType < 0) || (new->windowType > NUM_WINTYPES)) new->windowType = WINTYPE_NORMAL; new->borderClip = None; new->prev_trans = 0; XShapeSelectInput( dpy, id, ShapeNotifyMask ); new->shadowSize = get_shadow_prop (dpy, new); new->shapable = get_shapable_prop(dpy, new); new->decoHash = get_decoHash_prop(dpy, new); tmp = get_dim_prop(dpy, new); new->dimPicture = (tmp < OPAQUE) ? solid_picture (dpy, True, (double)tmp/OPAQUE, 0.1, 0.1, 0.1) : None; new->next = *p; *p = new; if (new->a.map_state == IsViewable) map_win (dpy, id, new->damage_sequence - 1, True); } void restack_win (Display *dpy, win *w, Window new_above) { Window old_above; if (w->next) old_above = w->next->id; else old_above = None; if (old_above != new_above) { win **prev; /* unhook */ for (prev = &list; *prev; prev = &(*prev)->next) if ((*prev) == w) break; *prev = w->next; /* rehook */ for (prev = &list; *prev; prev = &(*prev)->next) { if ((!(*prev)->destroyed) && ((*prev)->id == new_above)) break; } w->next = *prev; *prev = w; } } static void configure_win (Display *dpy, XConfigureEvent *ce) { win *w = find_win (dpy, ce->window); Window above; XserverRegion damage = None; if (!w) { if (ce->window == root) { if (rootBuffer) { XRenderFreePicture (dpy, rootBuffer); rootBuffer = None; } root_width = ce->width; root_height = ce->height; } return; } #if CAN_DO_USABLE if (w->usable) #endif { damage = XFixesCreateRegion (dpy, 0, 0); if (w->extents != None) XFixesCopyRegion (dpy, damage, w->extents); } w->shape_bounds.x -= w->a.x; w->shape_bounds.y -= w->a.y; w->a.x = ce->x; w->a.y = ce->y; /* Only destroy the pixmap if the window is mapped */ if (w->a.map_state != IsUnmapped && (w->a.width != ce->width || w->a.height != ce->height)) { #if HAS_NAME_WINDOW_PIXMAP if (w->pixmap) { XFreePixmap (dpy, w->pixmap); w->pixmap = None; if (w->picture) { XRenderFreePicture (dpy, w->picture); w->picture = None; } } #endif if (w->shadow) { XRenderFreePicture (dpy, w->shadow); w->shadow = None; } } w->a.width = ce->width; w->a.height = ce->height; w->a.border_width = ce->border_width; w->a.override_redirect = ce->override_redirect; restack_win (dpy, w, ce->above); if (w->a.map_state != IsUnmapped && damage) { XserverRegion extents = win_extents (dpy, w); XFixesUnionRegion (dpy, damage, damage, extents); XFixesDestroyRegion (dpy, extents); add_damage (dpy, damage); } w->shape_bounds.x += w->a.x; w->shape_bounds.y += w->a.y; if (!w->shaped) { w->shape_bounds.width = w->a.width; w->shape_bounds.height = w->a.height; } if (w->a.map_state != IsUnmapped) clipChanged = True; } static void circulate_win (Display *dpy, XCirculateEvent *ce) { win *w = find_win (dpy, ce->window); Window new_above; if (!w) return; if (ce->place == PlaceOnTop) new_above = list->id; else new_above = None; restack_win (dpy, w, new_above); clipChanged = True; } static void finish_destroy_win (Display *dpy, Window id, Bool gone) { win **prev, *w; for (prev = &list; (w = *prev); prev = &w->next) if (w->id == id && w->destroyed) { if (gone) finish_unmap_win (dpy, w); *prev = w->next; if (w->picture) { set_ignore (dpy, NextRequest (dpy)); XRenderFreePicture (dpy, w->picture); w->picture = None; } if (w->alphaPict) { XRenderFreePicture (dpy, w->alphaPict); w->alphaPict = None; } if (w->shadowPict) { XRenderFreePicture (dpy, w->shadowPict); w->shadowPict = None; } if (w->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, Bool gone) { finish_destroy_win (dpy, w->id, gone); } #endif static void destroy_win (Display *dpy, Window id, Bool gone, Bool fadeout) { fade *f; win *w = find_win (dpy, id); if (w && w->destruct_queued == False) { f = find_fade (w); if (f) { w->destruct_queued = True; f->callback = destroy_callback; w->destroyed = True; } else { w->destroyed = True; #if HAS_NAME_WINDOW_PIXMAP if (w->pixmap && fadeout && winTypeFade[w->windowType]) { set_fade (dpy, w, w->opacity*1.0/OPAQUE, 0.0, fade_out_step, destroy_callback, gone, False, True, True); } else #endif { if (!gone) { finish_destroy_win (dpy, id, gone); } else { w->destruct_queued = True; w->destruct_requested = True; w->destruct_request_time = get_time_in_milliseconds(); } } } } } /* static void dump_win (win *w) { printf ("\t%08lx: %d x %d + %d + %d (%d)\n", w->id, w->a.width, w->a.height, w->a.x, w->a.y, w->a.border_width); } static void dump_wins (void) { win *w; printf ("windows:\n"); for (w = list; w; w = w->next) dump_win (w); } */ static void damage_win (Display *dpy, XDamageNotifyEvent *de) { win *w = find_win (dpy, de->drawable); if (!w) return; #if WORK_AROUND_FGLRX if (w->a.map_state != IsViewable) return; #endif #if CAN_DO_USABLE if (!w->usable) { if (w->damage_bounds.width == 0 || w->damage_bounds.height == 0) { w->damage_bounds = de->area; } else { if (de->area.x < w->damage_bounds.x) { w->damage_bounds.width += (w->damage_bounds.x - de->area.x); w->damage_bounds.x = de->area.x; } if (de->area.y < w->damage_bounds.y) { w->damage_bounds.height += (w->damage_bounds.y - de->area.y); w->damage_bounds.y = de->area.y; } if (de->area.x + de->area.width > w->damage_bounds.x + w->damage_bounds.width) w->damage_bounds.width = de->area.x + de->area.width - w->damage_bounds.x; if (de->area.y + de->area.height > w->damage_bounds.y + w->damage_bounds.height) w->damage_bounds.height = de->area.y + de->area.height - w->damage_bounds.y; } #if 0 printf ("unusable damage [%d] %d, %d: %d x %d bounds %d, %d: %d x %d\n", de->drawable, de->area.x, de->area.y, de->area.width, de->area.height, w->damage_bounds.x, w->damage_bounds.y, w->damage_bounds.width, w->damage_bounds.height); #endif if (w->damage_bounds.x <= 0 && w->damage_bounds.y <= 0 && w->a.width <= w->damage_bounds.x + w->damage_bounds.width && w->a.height <= w->damage_bounds.y + w->damage_bounds.height) { clipChanged = True; if (winTypeFade[w->windowType]) { set_fade (dpy, w, 0, get_opacity_percent (dpy, w), fade_in_step, 0, False, True, True, False); } w->usable = True; } } if (w->usable) #endif repair_win (dpy, w); } static const char * shape_kind(int kind) { static char buf[128]; switch(kind){ case ShapeBounding: return "ShapeBounding"; case ShapeClip: return "ShapeClip"; case ShapeInput: return "ShapeInput"; default: sprintf (buf, "Shape %d", kind); return buf; } } static void shape_win (Display *dpy, XShapeEvent *se) { win *w = find_win (dpy, se->window); if (!w) return; if (w->a.map_state == IsUnmapped) return; if (w->isInFade) return; if (se->kind == ShapeClip || se->kind == ShapeBounding) { XserverRegion region0; XserverRegion region1; #if 0 printf("win 0x%lx %s:%s %ux%u+%d+%d (@%d+%d)\n", (unsigned long) se->window, shape_kind(se->kind), (se->shaped == True) ? "true" : "false", se->width, se->height, se->x, se->y, w->a.x, w->a.y); printf("\told %s %d+%d (@%d+%d)\n", (w->shaped == True) ? "true" : "false", w->shape_bounds_prev.width, w->shape_bounds_prev.height, w->shape_bounds_prev.x, w->shape_bounds_prev.y); #endif clipChanged = True; region0 = XFixesCreateRegion (dpy, &w->shape_bounds_prev, 1); if (se->shaped == True) { w->shaped = True; w->shape_bounds.x = w->a.x + se->x; w->shape_bounds.y = w->a.y + se->y; w->shape_bounds.width = se->width; w->shape_bounds.height = se->height; } else { w->shaped = False; w->shape_bounds.x = w->a.x; w->shape_bounds.y = w->a.y; w->shape_bounds.width = w->a.width; w->shape_bounds.height = w->a.height; } region1 = XFixesCreateRegion (dpy, &w->shape_bounds, 1); XFixesUnionRegion (dpy, region0, region0, region1); XFixesDestroyRegion (dpy, region1); /* ask for repaint of the old and new region */ paint_all (dpy, region0); } w->shape_bounds_prev = w->shape_bounds; } static void damage_screen (Display *dpy) { XserverRegion region; XRectangle r; r.x = 0; r.y = 0; r.width = root_width; r.height = root_height; region = XFixesCreateRegion (dpy, &r, 1); add_damage (dpy, region); screen_damaged = True; } static int error (Display *dpy, XErrorEvent *ev) { int o; char *name = 0; if (should_ignore (dpy, ev->serial)) return 0; if (ev->request_code == composite_opcode && ev->minor_code == X_CompositeRedirectSubwindows) { fprintf (stderr, "Another composite manager is already running\n"); my_exit_code=2; exit (2); } 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; } #ifndef NDEBUG fprintf (stderr,"error %d request %d minor %d serial %d\n", ev->error_code, ev->request_code, ev->minor_code, ev->serial); #endif /* abort (); this is just annoying to most people */ return 0; } static void expose_root (Display *dpy, Window root, XRectangle *rects, int nrects) { XserverRegion region = XFixesCreateRegion (dpy, rects, nrects); add_damage (dpy, region); } static int ev_serial (XEvent *ev) { if ((ev->type & 0x7f) != KeymapNotify) return ev->xany.serial; return NextRequest (ev->xany.display); } static char * ev_name (XEvent *ev) { static char buf[128]; switch (ev->type & 0x7f) { case Expose: return "Expose"; case MapNotify: return "Map"; case UnmapNotify: return "Unmap"; case ReparentNotify: return "Reparent"; case CirculateNotify: return "Circulate"; default: if (ev->type == damage_event + XDamageNotify) { return "Damage"; } else if (ev->type == xshape_event + ShapeNotify) { return "Shape"; } sprintf (buf, "Event %d", ev->type); return buf; } } static Window ev_window (XEvent *ev) { switch (ev->type) { case Expose: return ev->xexpose.window; case MapNotify: return ev->xmap.window; case UnmapNotify: return ev->xunmap.window; case ReparentNotify: return ev->xreparent.window; case CirculateNotify: return ev->xcirculate.window; default: if (ev->type == damage_event + XDamageNotify) { // fprintf(stderr, "%d", ev->type); return ((XDamageNotifyEvent *) ev)->drawable; } else if (ev->type == xshape_event + ShapeNotify) { // fprintf(stderr, "%d", ev->type); return ((XShapeEvent *) ev)->window; } return 0; } } void setShadowColor(char *value){ /*format nach #xxxxxx (html) �ndern?*/ unsigned int tmp; char **res = NULL; tmp = strtoul(value, res, 16); if( !value || strlen(value) < 6 || strlen(value) > 8 || (*(value+1) == 'x' && strlen(value) < 8) || res != NULL ){ shadowColor.red = 0; shadowColor.green = 0; shadowColor.blue = 0; printf("wrong hexadecimal (use 0xXXXXXX or XXXXXX)! defaulting to black...\n"); return; } shadowColor.blue = tmp&0xff; tmp >>= 8; shadowColor.green = tmp&0xff; tmp >>= 8; shadowColor.red = tmp&0xff; } typedef enum _option{ Display_=0, Compmode, ExcludeDockShadows, FadeWindows, FadeTrans, AutoRedirect, Synchronize, ShadowColor, ShadowRadius, ShadowOpacity, ShadowOffsetX, ShadowOffsetY, FadeOutStep, FadeInStep, FadeDelta, DisableARGB, FadeMenuWindows, NUMBEROFOPTIONS } Option; const char * options[NUMBEROFOPTIONS] = { "Display", /*0*/ "Compmode", /*1*/ "ExcludeDockShadows", /*2*/ "FadeWindows", /*3*/ "FadeTrans", /*4*/ "AutoRedirect", /*5*/ "Synchronize", /*6*/ "ShadowColor", /*7*/ "ShadowRadius", /*8*/ "ShadowOpacity", /*9*/ "ShadowOffsetX", /*10*/ "ShadowOffsetY", /*11*/ "FadeOutStep", /*12*/ "FadeInStep", /*13*/ "FadeDelta", /*14*/ "DisableARGB", /*15*/ "FadeMenuWindows", /*16*/ /*put your thingy in here...*/ }; void setValue(Option option, char *value ){ switch(option){ /*please keep that upside-down, because this way adding a new option is easier (all in one view)*/ case FadeDelta: fade_delta = atoi(value); if (fade_delta < 1) fade_delta = 10; break; case FadeInStep: fade_in_step = atof(value); if (fade_in_step <= 0) fade_in_step = 0.01; break; case FadeOutStep: fade_out_step = atof(value); if (fade_out_step <= 0) fade_out_step = 0.01; break; case ShadowOffsetY: shadowOffsetY = atoi(value); break; case ShadowOffsetX: shadowOffsetX = atoi(value); break; case ShadowOpacity: shadowOpacity = atof(value); break; case ShadowRadius: shadowRadius = atoi(value); break; case ShadowColor: setShadowColor(value); break; case Synchronize: synchronize = ( strcasecmp(value, "true") == 0 ); break; case AutoRedirect: autoRedirect = ( strcasecmp(value, "true") == 0 ); break; case FadeTrans: fadeTrans = ( strcasecmp(value, "true") == 0 ); break; case FadeWindows: if ( strcasecmp(value, "true") == 0 ) { int i; for (i = 0; i < NUM_WINTYPES; ++i) { if (i != WINTYPE_POPUP_MENU) winTypeFade[i] = True; } } break; case FadeMenuWindows: if ( strcasecmp(value, "true") == 0 ) { winTypeFade[WINTYPE_POPUP_MENU] = True; } break; case ExcludeDockShadows: if ( strcasecmp(value, "true") == 0 ) { winTypeShadow[WINTYPE_DOCK] = False; } break; case Compmode: if( strcasecmp(value, "CompClientShadows") == 0 ){ compMode = CompClientShadows; } else if( strcasecmp(value, "CompServerShadows") == 0 ){ compMode = CompServerShadows; } else{ compMode = CompSimple; /*default*/ } break; case Display_: break; display = strdup(value); break; case DisableARGB: disable_argb = ( strcasecmp(value, "true") == 0 ); break; default: break; } } int setParameter(char *line){ char *name = strtok(line, "="); char *value = line+strlen(name)+1; Option i; for(i=Display_; i < NUMBEROFOPTIONS; i++){ if( strcasecmp(name, *(options+i) ) == 0 ){ setValue(i, value); name = value = NULL; return 1; } } printf("ignored unknown option: <%s>\n", name); name = value = NULL; return 0; } void loadConfig(char *filename){ FILE *file = NULL; char line[ 1024 ]; size_t length = 0; Bool wasNull = False; Bool section = False; if( filename == NULL ){ #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 *configfile = "/.xcompmgrrc"; int n = strlen(home)+strlen(configfile)+1; filename = (char*)malloc(n*sizeof(char)); memset(filename,0,n); wasNull = True; strcat(filename, home); strcat(filename, configfile); } printf("trying '%s' as configfile\n\n", filename); if( (file = fopen(filename, "r")) == NULL ){ printf("failed to open config file. does it exist?\n"); if( wasNull ){ free(filename); filename = NULL; } return; } /*find section*/ while( !section && fgets(line, 1023, file) != NULL ){ if( strcmp(line, "[xcompmgr]\n") == 0 ) section = True; } /*read and set values*/ while( section && fgets(line, 1023, file) != NULL ){ int ret = strlen( line ); if( ret > 1 ){ if( *line == '[' )/*found new section - maybe check for '\n'?*/ break; *(line+ret-1) = '\0'; setParameter(line); } } printf("\nfinished parsing the config file\n"); fclose(file); if( wasNull ){ free(filename); filename = NULL; } } void usage (char *program) { fprintf (stderr, "%s v1.0\n", program); fprintf (stderr, "usage: %s [options]\n", program); fprintf (stderr, "Options\n"); fprintf (stderr, " -d display\n Specifies which display should be managed.\n"); fprintf (stderr, " -r radius\n Specifies the blur radius for client-side shadows. (default 12)\n"); fprintf (stderr, " -o opacity\n Specifies the translucency for client-side shadows. (default .75)\n"); fprintf (stderr, " -l left-offset\n Specifies the left offset for client-side shadows. (default -15)\n"); fprintf (stderr, " -t top-offset\n Specifies the top offset for clinet-side shadows. (default -15)\n"); fprintf (stderr, " -b color\n Specifies the background color to use if no root pixmap is set. (default is black)\n"); fprintf (stderr, " -I fade-in-step\n Specifies the opacity change between steps while fading in. (default 0.028)\n"); fprintf (stderr, " -O fade-out-step\n Specifies the opacity change between steps while fading out. (default 0.03)\n"); fprintf (stderr, " -D fade-delta-time\n Specifies the time between steps in a fade in milliseconds. (default 10)\n"); fprintf (stderr, " -a\n Use automatic server-side compositing. Faster, but no special effects.\n"); fprintf (stderr, " -c\n Draw client-side shadows with fuzzy edges.\n"); fprintf (stderr, " -C\n Avoid drawing shadows on dock/panel windows.\n"); fprintf (stderr, " -f\n Fade windows in/out when opening/closing.\n"); fprintf (stderr, " -F\n Fade windows during opacity changes.\n"); fprintf (stderr, " -n\n Normal client-side compositing with transparency support\n"); fprintf (stderr, " -s\n Draw server-side shadows with sharp edges.\n"); fprintf (stderr, " -S\n Enable synchronous operation (for debugging).\n"); fprintf (stderr, " -x [0x]XXXXXX\n Choose Custom Color in hex format\n"); fprintf (stderr, " -v\n Print version Number and exit\n"); fprintf (stderr, " -h\n Print this help\n"); my_exit_code=2; exit (2); } static Bool register_cm (void) { 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", scr); a = XInternAtom (dpy, net_wm_cm, False); /* w = XGetSelectionOwner (dpy, a); if (w != None) { XTextProperty tp; char **strs; int count; Atom winNameAtom = XInternAtom (dpy, "_NET_WM_NAME", False); if (!XGetTextProperty (dpy, w, &tp, winNameAtom) && !XGetTextProperty (dpy, w, &tp, XA_WM_NAME)) { fprintf (stderr, "Another composite manager is already running (0x%lx)\n", (unsigned long) w); return False; } if (XmbTextPropertyToTextList (dpy, &tp, &strs, &count) == Success) { fprintf (stderr, "Another composite manager is already running (%s)\n", strs[0]); XFreeStringList (strs); } XFree (tp.value); return False; }*/ w = XCreateSimpleWindow (dpy, RootWindow (dpy, scr), 0, 0, 1, 1, 0, None, None); Xutf8SetWMProperties(dpy, w, "kcompmgr", "kcompmgr", NULL, 0, NULL, NULL, NULL); /* setting this causes kompmgr to abort on TDE login */ /* XSetSelectionOwner (dpy, a, w, 0); */ return True; } int main (int argc, char **argv) { XEvent ev; Window root_return, parent_return; Window *children; Pixmap transPixmap; Pixmap blackPixmap; unsigned int nchildren; int i; XRenderPictureAttributes pa; XRenderColor c; XRectangle *expose_rects = 0; int size_expose = 0; int n_expose = 0; struct pollfd ufd; int n; int last_update; int now; int p; int composite_major, composite_minor; Bool noDockShadow = False; for (i = 0; i < NUM_WINTYPES; ++i) { winTypeFade[i] = False; winTypeShadow[i] = False; winTypeOpacity[i] = 1.0; } int o; char *fill_color_name = NULL; char **res = NULL; shadowColor.red = 0; shadowColor.green = 0; shadowColor.blue = 0; // 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); loadConfig(NULL); /*we do that before cmdline-parsing, so config-values can be overridden*/ /*used for shadow colors*/ while ((o = getopt (argc, argv, "D:I:O:d:r:o:l:t:b:scnfFmCaSx:vhk")) != -1) { switch (o) { case 'd': display = optarg; break; case 'D': fade_delta = atoi (optarg); if (fade_delta < 1) fade_delta = 10; break; case 'I': fade_in_step = atof (optarg); if (fade_in_step <= 0) fade_in_step = 0.01; break; case 'O': fade_out_step = atof (optarg); if (fade_out_step <= 0) fade_out_step = 0.01; break; case 's': compMode = CompServerShadows; for (i = 0; i < NUM_WINTYPES; ++i) winTypeShadow[i] = True; break; case 'c': compMode = CompClientShadows; for (i = 0; i < NUM_WINTYPES; ++i) winTypeShadow[i] = True; break; case 'C': winTypeShadow[WINTYPE_DOCK] = False; break; case 'n': compMode = CompSimple; for (i = 0; i < NUM_WINTYPES; ++i) winTypeShadow[i] = False; break; case 'f': for (i = 0; i < NUM_WINTYPES; ++i) { if (i != WINTYPE_POPUP_MENU) winTypeFade[i] = True; } break; case 'm': winTypeFade[WINTYPE_POPUP_MENU] = True; break; case 'F': fadeTrans = True; break; case 'a': autoRedirect = True; break; case 'S': synchronize = True; break; case 'r': shadowRadius = atoi (optarg); break; case 'o': shadowOpacity = atof (optarg); break; case 'l': shadowOffsetX = atoi (optarg); break; case 't': shadowOffsetY = atoi (optarg); break; case 'b': fill_color_name = optarg; break; case 'x': if( compMode != CompClientShadows ){ fprintf(stderr, "sorry, but we need ClientShadows (-c) for coloring to work properly!\ndefaulting to black...\n"); break; } setShadowColor(optarg); break; case 'v': fprintf (stderr, "%s v%-3.2f\n", argv[0], _VERSION_); my_exit_code=0; exit (0); case 'k': restartOnSigterm = False; break; case 'h': default: usage (argv[0]); break; } } /* don't bother to do anything for the desktop */ winTypeOpacity[WINTYPE_DESKTOP] = 1.0; winTypeShadow[WINTYPE_DESKTOP] = False; winTypeFade[WINTYPE_DESKTOP] = False; dpy = XOpenDisplay (display); if (!dpy) { fprintf (stderr, "Can't open display\n"); my_exit_code=2; exit (2); } 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"); my_exit_code=2; exit (2); } if (!XQueryExtension (dpy, COMPOSITE_NAME, &composite_opcode, &composite_event, &composite_error)) { fprintf (stderr, "No composite extension\n"); my_exit_code=2; exit (2); } XCompositeQueryVersion (dpy, &composite_major, &composite_minor); #if HAS_NAME_WINDOW_PIXMAP if (composite_major > 0 || composite_minor >= 2) hasNamePixmap = True; #endif if (!XDamageQueryExtension (dpy, &damage_event, &damage_error)) { fprintf (stderr, "No damage extension\n"); my_exit_code=2; exit (2); } if (!XFixesQueryExtension (dpy, &xfixes_event, &xfixes_error)) { fprintf (stderr, "No XFixes extension\n"); my_exit_code=2; exit (2); } if (!XShapeQueryExtension (dpy, &xshape_event, &xshape_error)) { fprintf (stderr, "No XShape extension\n"); my_exit_code=2; exit (2); } fprintf(stderr, "Started\n"); if (!register_cm()) { my_exit_code=2; exit (2); } /* get atoms */ shadowAtom = XInternAtom (dpy, SHADOW_PROP, False); opacityAtom = XInternAtom (dpy, OPACITY_PROP, False); shadeAtom = XInternAtom (dpy, SHADE_PROP, False); shapableAtom = XInternAtom (dpy, SHAPABLE_PROP, False); decoHashAtom = XInternAtom (dpy, DECOHASH_PROP, False); dimAtom = XInternAtom (dpy, DIM_PROP, False); deskChangeAtom = XInternAtom (dpy, DESKCHANGE_PROP, False); winTypeAtom = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE", False); winTDETTDAtom = XInternAtom (dpy, "_KDE_TRANSPARENT_TO_DESKTOP", False); winType[WINTYPE_DESKTOP] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); winType[WINTYPE_DOCK] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); winType[WINTYPE_TOOLBAR] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); winType[WINTYPE_MENU] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_MENU", False); winType[WINTYPE_UTILITY] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False); winType[WINTYPE_SPLASH] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); winType[WINTYPE_DIALOG] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); winType[WINTYPE_NORMAL] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); winType[WINTYPE_DROPDOWN_MENU] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False); winType[WINTYPE_POPUP_MENU] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False); winType[WINTYPE_TOOLTIP] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_TOOLTIP", False); winType[WINTYPE_NOTIFY] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); winType[WINTYPE_COMBO] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_COMBO", False); winType[WINTYPE_DND] = XInternAtom (dpy, "_NET_WM_WINDOW_TYPE_DND", False); pa.subwindow_mode = IncludeInferiors; if (compMode == CompClientShadows) { gaussianMap = make_gaussian_map(dpy, shadowRadius); presum_gaussian (gaussianMap); } if (fill_color_name) { XColor c; if (! XParseColor (dpy, DefaultColormap (dpy, scr), fill_color_name, &c)) { fprintf (stderr, "Could not parse fill color.\n"); my_exit_code=2; exit (2); } if (! XAllocColor (dpy, DefaultColormap (dpy, scr), &c)) { fprintf (stderr, "Could not allocate color.\n"); my_exit_code=2; exit (2); } fill_color.red = c.red; fill_color.green = c.green; fill_color.blue = c.blue; } else { fill_color.red = fill_color.green = fill_color.blue = 0x0; } fill_color.alpha = 0xffff; root_width = DisplayWidth (dpy, scr); root_height = DisplayHeight (dpy, scr); rootPicture = XRenderCreatePicture (dpy, root, sXRenderFindVisualFormat (dpy, DefaultVisual (dpy, scr)), CPSubwindowMode, &pa); blackPicture = solid_picture (dpy, True, 1, (double)(shadowColor.red)/0xff, (double)(shadowColor.green)/0xff, (double)(shadowColor.blue)/0xff); if (compMode == CompServerShadows) transBlackPicture = solid_picture (dpy, True, 0.3, 0, 0, 0); allDamage = None; clipChanged = True; XGrabServer (dpy); if (autoRedirect) XCompositeRedirectSubwindows (dpy, root, CompositeRedirectAutomatic); else { int dummy; XCompositeRedirectSubwindows (dpy, root, CompositeRedirectManual); XSelectInput (dpy, root, SubstructureNotifyMask| ExposureMask| StructureNotifyMask| PropertyChangeMask | VisibilityChangeMask); /*shaping stuff*/ XShapeQueryExtension(dpy, &shapeEvent, &dummy); XShapeSelectInput (dpy, root, ShapeNotifyMask); XQueryTree (dpy, root, &root_return, &parent_return, &children, &nchildren); for (i = 0; i < nchildren; i++) add_win (dpy, children[i], i ? children[i-1] : None); XFree (children); } XUngrabServer (dpy); ufd.fd = ConnectionNumber (dpy); ufd.events = POLLIN; if (!autoRedirect) paint_all (dpy, None); /* Under no circumstances should these two lines EVER be moved earlier in main() than this point */ atexit(delete_pid_file); write_pid_file(getpid()); for (;;) { /* dump_wins (); */ do { if (autoRedirect) XFlush (dpy); if (!QLength (dpy)) { if (poll (&ufd, 1, fade_timeout()) == 0) { run_fades (dpy); break; } } XNextEvent (dpy, &ev); if ((ev.type & 0x7f) != KeymapNotify) discard_ignore (dpy, ev.xany.serial); #if DEBUG_EVENTS printf ("event %10.10s serial 0x%08x window 0x%08x\n", ev_name(&ev), ev_serial (&ev), ev_window (&ev)); #endif if (!autoRedirect) switch (ev.type) { case CreateNotify: add_win (dpy, ev.xcreatewindow.window, 0); break; case ConfigureNotify: configure_win (dpy, &ev.xconfigure); break; case DestroyNotify: destroy_win (dpy, ev.xdestroywindow.window, True, True); break; case MapNotify: map_win (dpy, ev.xmap.window, ev.xmap.serial, True); break; case UnmapNotify: unmap_win (dpy, ev.xunmap.window, True); break; case ReparentNotify: if (ev.xreparent.parent == root) add_win (dpy, ev.xreparent.window, 0); else destroy_win (dpy, ev.xreparent.window, False, True); break; case CirculateNotify: circulate_win (dpy, &ev.xcirculate); break; case Expose: if (ev.xexpose.window == root) { int more = ev.xexpose.count + 1; if (n_expose == size_expose) { if (expose_rects) { expose_rects = realloc (expose_rects, (size_expose + more) * sizeof (XRectangle)); size_expose += more; } else { expose_rects = malloc (more * sizeof (XRectangle)); size_expose = more; } } expose_rects[n_expose].x = ev.xexpose.x; expose_rects[n_expose].y = ev.xexpose.y; expose_rects[n_expose].width = ev.xexpose.width; expose_rects[n_expose].height = ev.xexpose.height; n_expose++; if (ev.xexpose.count == 0) { expose_root (dpy, root, expose_rects, n_expose); n_expose = 0; } } break; case PropertyNotify: for (p = 0; backgroundProps[p]; p++) { if (ev.xproperty.atom == XInternAtom (dpy, backgroundProps[p], False)) { if (rootTile) { XRenderFreePicture (dpy, rootTile); rootTile = None; damage_screen (dpy); break; } } } /* Window set shade? */ if (ev.xproperty.atom == shadeAtom) { win * w = find_win(dpy, ev.xproperty.window); if (w){ unsigned int tmp = get_shade_prop(dpy, w); if (tmp) { if (tmp == 1) { w->preShadeOpacity = w->opacity; w->opacity = w->opacity-1; /*assuming that no human being will ever be able to shade an invisable window ;) */ determine_mode(dpy, w); } else if (tmp == 2) { w->opacity = w->preShadeOpacity; determine_mode(dpy, w); } } break; } } else if (ev.xproperty.atom == shapableAtom) { win * w = find_win(dpy, ev.xproperty.window); if (w) { w->shapable = get_shapable_prop(dpy, w); /* printf("%u is %s shapable\n",w->id,w->shapable?"":"not");*/ } else printf("arrrg, window not found\n"); } else if (ev.xproperty.atom == decoHashAtom) { win * w = find_win(dpy, ev.xproperty.window); if (w) { w->decoHash = get_decoHash_prop(dpy, w); } else printf("arrrg, window not found\n"); } else if (ev.xproperty.atom == dimAtom) { win * w = find_win(dpy, ev.xproperty.window); if (w) { unsigned int tmp = get_dim_prop(dpy, w); if (w->dimPicture) { XRenderFreePicture (dpy, w->dimPicture); w->dimPicture = None; } if (tmp < OPAQUE) w->dimPicture = solid_picture (dpy, True, (double)tmp/OPAQUE, 0.1, 0.1, 0.1); } else printf("arrrg, window not found\n"); } /* check if Trans or Shadow property was changed */ else if (ev.xproperty.atom == opacityAtom || ev.xproperty.atom == shadowAtom) { /* reset mode and redraw window */ win * w = find_win(dpy, ev.xproperty.window); if (w) { unsigned int tmp; unsigned int oldShadowSize = w->shadowSize; if (ev.xproperty.atom == opacityAtom) { tmp = get_opacity_prop(dpy, w, OPAQUE); /*This will most probably happen if window is in fade - resulting in that the fade process isn't updated or broken -> we may have a wrong opacity in the future*/ /*if (tmp == w->opacity) break;*/ /*skip if opacity does not change*/ if (fadeTrans) { static double start, finish, step; start = w->opacity*1.0/OPAQUE; finish = (tmp*1.0)/OPAQUE; if ( start > finish ) step = fade_out_step; else step = fade_in_step; set_fade (dpy, w, start, finish, step, 0, False, True, True, False); break; } else { w->opacity = tmp; } } else { tmp = get_shadow_prop(dpy, w); if (tmp == w->shadowSize) break; /*skip if shadow does not change*/ w->shadowSize = tmp; /* if (w->isInFade) break; */ } if (w->shadow) { XRenderFreePicture (dpy, w->shadow); w->shadow = None; } if (oldShadowSize < w->shadowSize) /* this is important to catch size changes on cleanup with determine_mode*/ { if( w->extents != None ) XFixesDestroyRegion( dpy, w->extents ); w->extents = win_extents (dpy, w); determine_mode(dpy, w); } else { determine_mode(dpy, w); if( w->extents != None ) XFixesDestroyRegion( dpy, w->extents ); w->extents = win_extents (dpy, w); } } } else if (ev.xproperty.atom == deskChangeAtom) { /*just set global variable*/ unsigned int tmp = get_deskchange_prop(dpy, ev.xproperty.window); printf("desk change, state:%d\n",tmp); } break; default: if (ev.type == damage_event + XDamageNotify) { /* printf("damaging win: %u\n",ev.xany.window);*/ damage_win (dpy, (XDamageNotifyEvent *) &ev); } if (ev.type == xshape_event + ShapeNotify) { shape_win (dpy, (XShapeEvent *) &ev); } if (ev.type == shapeEvent) { win * w = find_win(dpy, ev.xany.window); #if 1 if (w && w->shapable) #endif #if 0 if (w) #endif { #if 0 XRectangle rect; rect.x = ((XShapeEvent*)&ev)->x; rect.y = ((XShapeEvent*)&ev)->y; rect.width = ((XShapeEvent*)&ev)->width; rect.height = ((XShapeEvent*)&ev)->height; damage_shape(dpy, w, &rect); #endif #if 0 if (w->shadowSize != 0) { w->shadowSize = 0; XRenderFreePicture (dpy, w->shadow); w->shadow = None; determine_mode(dpy, w); if( w->extents != None ) XFixesDestroyRegion( dpy, w->extents ); w->extents = win_extents (dpy, w); } #endif /*this is hardly efficient, but a current workaraound shaping support isn't that good so far (e.g. we lack shaped shadows) IDEA: use XRender to scale/shift a copy of the window and then blur it*/ #if 1 if (w->picture) { clipChanged = True; repair_win (dpy, w); } #endif } } break; } } while (QLength (dpy)); if (allDamage && !autoRedirect) { paint_all (dpy, allDamage); XSync (dpy, False); allDamage = None; clipChanged = False; } } XClearArea (dpy, root, 0, 0, 0, 0, True); XSync (dpy, False); }