diff options
-rw-r--r-- | compton.c | 449 | ||||
-rw-r--r-- | compton.h | 97 |
2 files changed, 259 insertions, 287 deletions
@@ -17,7 +17,6 @@ struct timeval time_start = { 0, 0 }; win *list; -fade *fades; Display *dpy; int scr; @@ -97,10 +96,12 @@ int shadow_offset_x = -15; int shadow_offset_y = -15; double shadow_opacity = .75; -double fade_in_step = 0.028; -double fade_out_step = 0.03; -int fade_delta = 10; -int fade_time = 0; +/// How much to fade in in a single fading step. +opacity_t fade_in_step = 0.028 * OPAQUE; +/// How much to fade out in a single fading step. +opacity_t fade_out_step = 0.03 * OPAQUE; +unsigned long fade_delta = 10; +unsigned long fade_time = 0; Bool fade_trans = False; Bool clear_shadow = False; @@ -125,7 +126,13 @@ Bool synchronize = False; * Fades */ -static int +/** + * Get current system clock in milliseconds. + * + * The return type must be unsigned long because so many milliseconds have + * passed since the epoch. + */ +static unsigned long get_time_in_milliseconds() { struct timeval tv; @@ -134,153 +141,70 @@ get_time_in_milliseconds() { return tv.tv_sec * 1000 + tv.tv_usec / 1000; } -static fade * -find_fade(win *w) { - fade *f; - - for (f = fades; f; f = f->next) { - if (f->w == w) return f; - } +/** + * Get the time left before next fading point. + * + * In milliseconds. + */ +static int +fade_timeout(void) { + int diff = fade_delta - get_time_in_milliseconds() + fade_time; - return 0; -} + if (diff < 0) + diff = 0; -static void -dequeue_fade(Display *dpy, fade *f) { - fade **prev; - - for (prev = &fades; *prev; prev = &(*prev)->next) { - if (*prev == f) { - *prev = f->next; - if (f->callback) { - (*f->callback)(dpy, f->w); - } - free(f); - break; - } - } + return diff; } +/** + * Run fading on a window. + * + * @param steps steps of fading + */ static void -cleanup_fade(Display *dpy, win *w) { - fade *f = find_fade (w); - if (f) { - dequeue_fade(dpy, f); +run_fade(Display *dpy, win *w, unsigned steps) { + // If we reach target opacity, set fade_fin so the callback gets + // executed + if (w->opacity == w->opacity_tgt) { + w->fade_fin = True; + return; } -} -static void -enqueue_fade(Display *dpy, fade *f) { - if (!fades) { - fade_time = get_time_in_milliseconds() + fade_delta; + if (!w->fade) + w->opacity = w->opacity_tgt; + else if (steps) { + // Use double below because opacity_t will probably overflow during + // calculations + if (w->opacity < w->opacity_tgt) + w->opacity = normalize_d_range( + (double) w->opacity + (double) fade_in_step * steps, + 0.0, w->opacity_tgt); + else + w->opacity = normalize_d_range( + (double) w->opacity - (double) fade_out_step * steps, + w->opacity_tgt, OPAQUE); } - f->next = fades; - fades = f; -} -static void -set_fade(Display *dpy, win *w, double start, - double finish, double step, - void(*callback) (Display *dpy, win *w), - Bool exec_callback, Bool override) { - fade *f; - - f = find_fade(w); - if (!f) { - f = malloc(sizeof(fade)); - f->next = 0; - f->w = w; - f->cur = start; - enqueue_fade(dpy, f); - } else if (!override) { + if (w->opacity == w->opacity_tgt) { + w->fade_fin = True; return; - } else { - if (exec_callback && f->callback) { - (*f->callback)(dpy, f->w); - } - } - - if (finish < 0) finish = 0; - if (finish > 1) finish = 1; - f->finish = finish; - - if (f->cur < finish) { - f->step = step; - } else if (f->cur > finish) { - f->step = -step; } - f->callback = callback; - set_opacity(dpy, w, f->cur * OPAQUE); - - /* fading windows need to be drawn, mark - them as damaged. when a window maps, - if it tries to fade in but it already - at the right opacity (map/unmap/map fast) - then it will never get drawn without this - until it repaints */ - w->damaged = 1; -} - -static int -fade_timeout(void) { - int now; - int delta; - - if (!fades) return -1; - - now = get_time_in_milliseconds(); - delta = fade_time - now; - - if (delta < 0) delta = 0; - - return delta; + w->fade_fin = False; } +/** + * Set fade callback of a window, and possibly execute the previous + * callback. + * + * @param exec_callback whether the previous callback is to be executed + */ static void -run_fades(Display *dpy) { - int now = get_time_in_milliseconds(); - fade *next = fades; - int steps; - Bool need_dequeue; - - if (fade_time - now > 0) return; - steps = 1 + (now - fade_time) / fade_delta; - - while (next) { - fade *f = next; - win *w = f->w; - next = f->next; - - f->cur += f->step * steps; - if (f->cur >= 1) { - f->cur = 1; - } else if (f->cur < 0) { - f->cur = 0; - } - - w->opacity = f->cur * OPAQUE; - need_dequeue = False; - if (f->step > 0) { - if (f->cur >= f->finish) { - w->opacity = f->finish * OPAQUE; - need_dequeue = True; - } - } else { - if (f->cur <= f->finish) { - w->opacity = f->finish * OPAQUE; - need_dequeue = True; - } - } - - determine_mode(dpy, w); - - /* Must do this last as it might - destroy f->w in callbacks */ - if (need_dequeue) dequeue_fade(dpy, f); - } - - fade_time = now + fade_delta; +set_fade_callback(Display *dpy, win *w, + void (*callback) (Display *dpy, win *w), Bool exec_callback) { + if (exec_callback && w->fade_callback) + (w->fade_callback)(dpy, w); + w->fade_callback = callback; } /** @@ -1003,62 +927,39 @@ get_frame_extents(Display *dpy, Window w, } } -static void -paint_all(Display *dpy, XserverRegion region) { +static win * +paint_preprocess(Display *dpy, win *list) { win *w; - win *t = 0; - - if (!region) { - region = get_screen_region(dpy); - } - -#ifdef MONITOR_REPAINT - root_buffer = root_picture; -#else - if (!root_buffer) { - Pixmap root_pixmap = XCreatePixmap( - dpy, root, root_width, root_height, - DefaultDepth(dpy, scr)); - - root_buffer = XRenderCreatePicture(dpy, root_pixmap, - XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), - 0, 0); + win *t = NULL, *next = NULL; + // Sounds like the timeout in poll() frequently does not work + // accurately, asking it to wait to 20ms, and often it would wait for + // 19ms, so the step value has to be rounded. + unsigned steps = roundl((double) (get_time_in_milliseconds() - fade_time) / fade_delta); - XFreePixmap(dpy, root_pixmap); - } -#endif + // Reset fade_time + fade_time = get_time_in_milliseconds(); - XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); + for (w = list; w; w = next) { + // In case calling the fade callback function destroys this window + next = w->next; + opacity_t opacity_old = w->opacity; -#ifdef MONITOR_REPAINT - XRenderComposite( - dpy, PictOpSrc, black_picture, None, - root_picture, 0, 0, 0, 0, 0, 0, - root_width, root_height); -#endif - -#ifdef DEBUG_REPAINT - printf("paint:"); -#endif - - for (w = list; w; w = w->next) { #if CAN_DO_USABLE if (!w->usable) continue; #endif - /* never painted, ignore it */ - if (!w->damaged) continue; + // Run fading + run_fade(dpy, w, steps); - /* if invisible, ignore it */ - if (w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 + // Give up if it's not damaged or invisible + if (!w->damaged + || w->a.x + w->a.width < 1 || w->a.y + w->a.height < 1 || w->a.x >= root_width || w->a.y >= root_height) { + check_fade_fin(dpy, w); continue; } -#ifdef DEBUG_REPAINT - printf(" %#010lx", w->id); -#endif - + // Fetch the picture and pixmap if needed if (!w->picture) { XRenderPictureAttributes pa; XRenderPictFormat *format; @@ -1078,6 +979,7 @@ paint_all(Display *dpy, XserverRegion region) { dpy, draw, format, CPSubwindowMode, &pa); } + // Fetch bounding region and extents if needed if (!w->border_size) { w->border_size = border_size(dpy, w); } @@ -1086,6 +988,17 @@ paint_all(Display *dpy, XserverRegion region) { w->extents = win_extents(dpy, w); } + // If opacity changes + if (w->opacity != opacity_old) { + determine_mode(dpy, w); + add_damage_win(dpy, w); + } + + if (!w->opacity) { + check_fade_fin(dpy, w); + continue; + } + // Rebuild alpha_pict only if necessary if (OPAQUE != w->opacity && (!w->alpha_pict || w->opacity != w->opacity_cur)) { @@ -1137,15 +1050,48 @@ paint_all(Display *dpy, XserverRegion region) { t = w; } -#ifdef DEBUG_REPAINT - printf("\n"); - fflush(stdout); + return t; +} + +static void +paint_all(Display *dpy, XserverRegion region, win *t) { + win *w; + + if (!region) { + region = get_screen_region(dpy); + } + +#ifdef MONITOR_REPAINT + root_buffer = root_picture; +#else + if (!root_buffer) { + Pixmap root_pixmap = XCreatePixmap( + dpy, root, root_width, root_height, + DefaultDepth(dpy, scr)); + + root_buffer = XRenderCreatePicture(dpy, root_pixmap, + XRenderFindVisualFormat(dpy, DefaultVisual(dpy, scr)), + 0, 0); + + XFreePixmap(dpy, root_pixmap); + } #endif - XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); + XFixesSetPictureClipRegion(dpy, root_picture, 0, 0, region); + +#ifdef MONITOR_REPAINT + XRenderComposite( + dpy, PictOpSrc, black_picture, None, + root_picture, 0, 0, 0, 0, 0, 0, + root_width, root_height); +#endif paint_root(dpy); +#ifdef DEBUG_REPAINT + printf("paint:"); +#endif + for (w = t; w; w = w->prev_trans) { int x, y, wid, hei; @@ -1161,6 +1107,10 @@ paint_all(Display *dpy, XserverRegion region) { hei = w->a.height; #endif +#ifdef DEBUG_REPAINT + printf(" %#010lx", w->id); +#endif + // Allow shadow to be painted anywhere in the damaged region XFixesSetPictureClipRegion(dpy, root_buffer, 0, 0, region); @@ -1226,8 +1176,15 @@ paint_all(Display *dpy, XserverRegion region) { } XFixesDestroyRegion(dpy, paint_reg); + + check_fade_fin(dpy, w); } +#ifdef DEBUG_REPAINT + printf("\n"); + fflush(stdout); +#endif + XFixesDestroyRegion(dpy, region); if (root_buffer != root_picture) { @@ -1415,18 +1372,12 @@ map_win(Display *dpy, Window id, w->a.map_state = IsViewable; w->window_type = determine_wintype(dpy, w->id, w->id); - // Window type change could affect shadow - { - Bool shadow_old = w->shadow; - determine_shadow(dpy, w); - if (w->shadow != shadow_old) { - calc_shadow_geometry(dpy, w); - if (w->extents) { - free_region(dpy, &w->extents); - w->extents = win_extents(dpy, w); - } - } - } + // Window type change could affect shadow and fade + determine_shadow(dpy, w); + determine_fade(dpy, w); + + // Determine mode here just in case the colormap changes + determine_mode(dpy, w); #ifdef DEBUG_WINTYPE printf("map_win(): window %#010lx type %s\n", @@ -1461,22 +1412,18 @@ map_win(Display *dpy, Window id, recheck_focus(dpy); } + // Fading in calc_opacity(dpy, w, True); - calc_dim(dpy, w); + set_fade_callback(dpy, w, NULL, True); - determine_mode(dpy, w); + calc_dim(dpy, w); #if CAN_DO_USABLE w->damage_bounds.x = w->damage_bounds.y = 0; w->damage_bounds.width = w->damage_bounds.height = 0; #endif - w->damaged = 0; + w->damaged = 1; - if (fade && win_type_fade[w->window_type]) { - set_fade( - dpy, w, 0, get_opacity_percent(dpy, w), - fade_in_step, 0, True, True); - } /* if any configure events happened while the window was unmapped, then configure @@ -1508,12 +1455,10 @@ finish_unmap_win(Display *dpy, win *w) { free_picture(dpy, &w->shadow_pict); } -#if HAS_NAME_WINDOW_PIXMAP static void unmap_callback(Display *dpy, win *w) { finish_unmap_win(dpy, w); } -#endif static void unmap_win(Display *dpy, Window id, Bool fade) { @@ -1523,6 +1468,10 @@ unmap_win(Display *dpy, Window id, Bool fade) { w->a.map_state = IsUnmapped; + // Fading out + w->opacity_tgt = 0; + set_fade_callback(dpy, w, unmap_callback, False); + // don't care about properties anymore // Will get BadWindow if the window is destroyed set_ignore(dpy, NextRequest(dpy)); @@ -1532,14 +1481,6 @@ unmap_win(Display *dpy, Window id, Bool fade) { set_ignore(dpy, NextRequest(dpy)); XSelectInput(dpy, w->client_win, 0); } - -#if HAS_NAME_WINDOW_PIXMAP - if (w->pixmap && fade && win_type_fade[w->window_type]) { - set_fade(dpy, w, get_opacity_percent(dpy, w), 0.0, - fade_out_step, unmap_callback, False, True); - } else -#endif - finish_unmap_win(dpy, w); } static opacity_t @@ -1594,15 +1535,6 @@ determine_mode(Display *dpy, win *w) { add_damage_win(dpy, w); } -static void -set_opacity(Display *dpy, win *w, opacity_t opacity) { - // Do nothing if the opacity does not change - if (w->opacity == opacity) return; - - w->opacity = opacity; - determine_mode(dpy, w); -} - /** * Calculate and set the opacity of a window. * @@ -1650,7 +1582,7 @@ calc_opacity(Display *dpy, win *w, Bool refetch_prop) { opacity = inactive_opacity; } - set_opacity(dpy, w, opacity); + w->opacity_tgt = opacity; } static void @@ -1670,11 +1602,32 @@ calc_dim(Display *dpy, win *w) { } /** - * Determine if a window should have shadow. + * Determine if a window should fade on opacity change. + */ +static void +determine_fade(Display *dpy, win *w) { + w->fade = win_type_fade[w->window_type]; +} + +/** + * Determine if a window should have shadow, and update things depending + * on shadow state. */ static void determine_shadow(Display *dpy, win *w) { + Bool shadow_old = w->shadow; + w->shadow = win_type_shadow[w->window_type]; + + // Window extents need update on shadow state change + if (w->shadow != shadow_old) { + // Shadow geometry currently doesn't change on shadow state change + // calc_shadow_geometry(dpy, w); + if (w->extents) { + free_region(dpy, &w->extents); + w->extents = win_extents(dpy, w); + } + } } /** @@ -1776,9 +1729,13 @@ add_win(Display *dpy, Window id, Window prev, Bool override_redirect) { new->shadow_dy = 0; new->shadow_width = 0; new->shadow_height = 0; - new->opacity = OPAQUE; + new->opacity = 0; + new->opacity_tgt = 0; new->opacity_cur = OPAQUE; new->opacity_prop = OPAQUE; + new->fade = False; + new->fade_callback = NULL; + new->fade_fin = False; new->alpha_pict = None; new->frame_opacity = 1.0; new->frame_opacity_cur = 1.0; @@ -1976,7 +1933,6 @@ finish_destroy_win(Display *dpy, Window id) { free_picture(dpy, &w->shadow_pict); free_damage(dpy, &w->damage); - cleanup_fade(dpy, w); free(w); break; } @@ -1994,17 +1950,12 @@ static void destroy_win(Display *dpy, Window id, Bool fade) { win *w = find_win(dpy, id); - if (w) w->destroyed = True; + if (w) { + w->destroyed = True; -#if HAS_NAME_WINDOW_PIXMAP - if (w && w->pixmap && fade && win_type_fade[w->window_type]) { - set_fade(dpy, w, get_opacity_percent(dpy, w), - 0.0, fade_out_step, destroy_callback, - False, True); - } else -#endif - { - finish_destroy_win(dpy, id); + // Fading out the window + w->opacity_tgt = 0; + set_fade_callback(dpy, w, destroy_callback, False); } } @@ -2273,6 +2224,8 @@ ev_window(XEvent *ev) { return ev->xcreatewindow.window; case ConfigureNotify: return ev->xconfigure.window; + case DestroyNotify: + return ev->xdestroywindow.window; case MapNotify: return ev->xmap.window; case UnmapNotify: @@ -2748,6 +2701,7 @@ main(int argc, char **argv) { double shadow_red = 0.0; double shadow_green = 0.0; double shadow_blue = 0.0; + win *t; gettimeofday(&time_start, NULL); @@ -2792,16 +2746,10 @@ main(int argc, char **argv) { } break; case 'I': - fade_in_step = atof(optarg); - if (fade_in_step <= 0) { - fade_in_step = 0.01; - } + fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'O': - fade_out_step = atof(optarg); - if (fade_out_step <= 0) { - fade_out_step = 0.01; - } + fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break; case 'c': for (i = 0; i < NUM_WINTYPES; ++i) { @@ -2882,6 +2830,8 @@ main(int argc, char **argv) { track_focus = True; } + fade_time = get_time_in_milliseconds(); + dpy = XOpenDisplay(display); if (!dpy) { fprintf(stderr, "Can't open display\n"); @@ -2994,13 +2944,13 @@ main(int argc, char **argv) { ufd.fd = ConnectionNumber(dpy); ufd.events = POLLIN; - paint_all(dpy, None); + t = paint_preprocess(dpy, list); + paint_all(dpy, None, t); for (;;) { do { if (!QLength(dpy)) { if (poll(&ufd, 1, fade_timeout()) == 0) { - run_fades(dpy); break; } } @@ -3009,9 +2959,10 @@ main(int argc, char **argv) { ev_handle((XEvent *)&ev); } while (QLength(dpy)); + t = paint_preprocess(dpy, list); if (all_damage) { static int paint; - paint_all(dpy, all_damage); + paint_all(dpy, all_damage, t); paint++; XSync(dpy, False); all_damage = None; @@ -107,13 +107,12 @@ typedef struct _win { Bool destroyed; /// Cached width/height of the window including border. int widthb, heightb; - unsigned int left_width; - unsigned int right_width; - unsigned int top_width; - unsigned int bottom_width; + // Opacity-related members /// Current window opacity. opacity_t opacity; + /// Target window opacity. + opacity_t opacity_tgt; /// Opacity of current alpha_pict. opacity_t opacity_cur; /// Cached value of opacity window attribute. @@ -121,13 +120,26 @@ typedef struct _win { /// Alpha mask Picture to render window with opacity. Picture alpha_pict; + // Fading-related members + /// Do not fade if it's false. Change on window type change. + /// Used by fading blacklist in the future. + Bool fade; + /// Callback to be called after fading completed. + void (*fade_callback) (Display *dpy, struct _win *w); + /// Whether fading is finished. + Bool fade_fin; + + // Frame-opacity-related members /// Current window frame opacity. Affected by window opacity. double frame_opacity; /// Opacity of current frame_alpha_pict. opacity_t frame_opacity_cur; /// Alpha mask Picture to render window frame with opacity. Picture frame_alpha_pict; + /// Frame widths. Determined by client window attributes. + unsigned int left_width, right_width, top_width, bottom_width; + // Shadow-related members /// Whether a window has shadow. Affected by window type. Bool shadow; /// Opacity of the shadow. Affected by window opacity and frame opacity. @@ -146,6 +158,7 @@ typedef struct _win { /// shadow opacity. Picture shadow_pict; + // Dim-related members /// Whether the window is to be dimmed. Bool dim; @@ -165,16 +178,6 @@ typedef struct _conv { double *data; } conv; -typedef struct _fade { - struct _fade *next; - win *w; - double cur; - double finish; - double step; - void (*callback) (Display *dpy, win *w); - Display *dpy; -} fade; - typedef enum { WIN_EVMODE_UNKNOWN, WIN_EVMODE_FRAME, @@ -218,15 +221,29 @@ normalize_i_range(int i, int min, int max) { } /** + * Normalize a double value to a specific range. + * + * @param d double value to normalize + * @param min minimal value + * @param max maximum value + * @return normalized value + */ +static inline double +normalize_d_range(double d, double min, double max) { + if (d > max) return max; + if (d < min) return min; + return d; +} + +/** * Normalize a double value to 0.\ 0 - 1.\ 0. * * @param d double value to normalize + * @return normalized value */ static inline double normalize_d(double d) { - if (d > 1.0) return 1.0; - if (d < 0.0) return 0.0; - return d; + return normalize_d_range(d, 0.0, 1.0); } /** @@ -388,32 +405,33 @@ win_get_children(Display *dpy, Window w, return True; } -static int +static unsigned long get_time_in_milliseconds(void); -static fade * -find_fade(win *w); - -static void -dequeue_fade(Display *dpy, fade *f); - -static void -cleanup_fade(Display *dpy, win *w); +static int +fade_timeout(void); static void -enqueue_fade(Display *dpy, fade *f); +run_fade(Display *dpy, win *w, unsigned steps); static void -set_fade(Display *dpy, win *w, double start, - double finish, double step, - void(*callback) (Display *dpy, win *w), - Bool exec_callback, Bool override); +set_fade_callback(Display *dpy, win *w, + void (*callback) (Display *dpy, win *w), Bool exec_callback); -static int -fade_timeout(void); +/** + * Execute fade callback of a window if fading finished. + */ +static inline void +check_fade_fin(Display *dpy, win *w) { + if (w->fade_fin) { + set_fade_callback(dpy, w, NULL, True); + w->fade_fin = False; + } +} static void -run_fades(Display *dpy); +set_fade_callback(Display *dpy, win *w, + void (*callback) (Display *dpy, win *w), Bool exec_callback); static double gaussian(double r, double x, double y); @@ -476,8 +494,11 @@ get_frame_extents(Display *dpy, Window w, unsigned int *top, unsigned int *bottom); +static win * +paint_preprocess(Display *dpy, win *list); + static void -paint_all(Display *dpy, XserverRegion region); +paint_all(Display *dpy, XserverRegion region, win *t); static void add_damage(Display *dpy, XserverRegion damage); @@ -517,15 +538,15 @@ static void determine_mode(Display *dpy, win *w); static void -set_opacity(Display *dpy, win *w, opacity_t opacity); - -static void calc_opacity(Display *dpy, win *w, Bool refetch_prop); static void calc_dim(Display *dpy, win *w); static void +determine_fade(Display *dpy, win *w); + +static void determine_shadow(Display *dpy, win *w); static void |