diff options
author | Richard Grenville <pyxlcy@gmail.com> | 2012-09-19 20:49:16 +0800 |
---|---|---|
committer | Richard Grenville <pyxlcy@gmail.com> | 2012-09-19 21:14:28 +0800 |
commit | a08c22945a825eb43607edc7317611c6b6049232 (patch) | |
tree | 2985826ec9f4628f9e0935b26ae9b522b46243c8 /compton.c | |
parent | fb18759b02d274602fdc75ee7f00a2f8bc42676b (diff) | |
download | tdebase-a08c22945a825eb43607edc7317611c6b6049232.tar.gz tdebase-a08c22945a825eb43607edc7317611c6b6049232.zip |
Improvement: Change fading mechanism
- Change fading mechanism for better modularity. Remove fade queue and
use members in struct _win to record fading data. In contrast to
previous commits, this one actually could make the program slower (but
very slightly, hardly noticeable if your CPU is anywhere close to AMD
K7). As this commit changes lots of things, bugs are to be expected.
- Currently -F does not do its job. -f actually equals -fF. (While in
the past -F equals nothing and -f is just -f.) A fix will be made
soon. I suppose it isn't hard.
- Add a preprocessor function paint_preprocess() and move all
preprocessing code in paint_all() to it.
- Add window flag support but currently unused.
- Add DamageNotify handling to ev_window().
- I'm considering removing HAS_NAME_WINDOW_PIXMAP = 0 support as I
couldn't see what it is good for. Nor do I know what CAN_DO_USABLE
does. Basically all my changes ignore these cases.
Diffstat (limited to 'compton.c')
-rw-r--r-- | compton.c | 449 |
1 files changed, 200 insertions, 249 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; |