super fading
authorDana Jansens <danakj@orodu.net>
Thu, 6 Mar 2008 14:27:18 +0000 (09:27 -0500)
committerDana Jansens <danakj@orodu.net>
Thu, 6 Mar 2008 14:28:59 +0000 (09:28 -0500)
dcompmgr.c
fade.c [new file with mode: 0644]
fade.h [new file with mode: 0644]
render.c
render.h
screen.c
screen.h
time.c
time.h
window.c
window.h

index f6b7019..71f3eee 100644 (file)
@@ -6,7 +6,10 @@
 #include "display.h"
 #include "gettext.h"
 #include "time.h"
+
+/* these can be plugins */
 #include "render.h"
+#include "fade.h"
 
 #include <glib.h>
 #include <stdio.h>
@@ -76,8 +79,10 @@ event(d_display_t *dpy)
             if (!sc) break;
             w = screen_find_window(sc, dev->window);
             vis = window_is_mapped(w);
-            if (vis)
+            if (vis) {
+                sc->window_become_zombie(w);
                 sc->window_hide(w);
+            }
             screen_remove_window(sc, w);
             if (vis) screen_refresh(sc);
             break;
@@ -95,8 +100,10 @@ event(d_display_t *dpy)
             if (rev->parent == sc->super.root)
                 screen_add_window(sc, rev->window);
             else {
-                if (window_is_mapped(w))
+                if (window_is_mapped(w)) {
+                    sc->window_become_zombie(w);
                     sc->window_hide(w);
+                }
                 screen_remove_window(sc, w);
             }
             screen_refresh(sc);
@@ -126,6 +133,7 @@ event(d_display_t *dpy)
             sc = display_screen_from_root(dpy, mev->event);
             if (!sc) break;
             w = screen_find_window(sc, mev->window);
+            sc->window_become_zombie(w);
             sc->window_hide(w);
             screen_refresh(w->sc);
             break;
@@ -258,6 +266,22 @@ event(d_display_t *dpy)
 }
 
 static void
+timeouts(d_display_t *dpy)
+{
+    d_list_it_t *it;
+    struct timeval now;
+
+    gettimeofday(&now, NULL);
+    
+    for (it = list_top(dpy->screens); it; it = it->next) {
+        d_screen_t *sc = it->data;
+
+        render_timeout(sc, &now);
+        fade_timeout(sc, &now);
+    }
+}
+
+static void
 paint(d_display_t *dpy)
 {
     d_list_it_t *it;
@@ -278,17 +302,38 @@ run(d_display_t *dpy)
 {
     while (!quit) {
         struct timeval next, now, *wait;
-        int            r, npaint;
+        int            r, npaint, ntime;
         d_list_it_t   *it;
         fd_set         fds;
 
         event(dpy);
 
         npaint = 0;
+        ntime = 0;
         for (it = list_top(dpy->screens); it; it = it->next) {
             d_screen_t *sc = it->data;
+            struct timeval next_timeout;
+
+            if (render_next_timeout(sc, &next_timeout)) {
+                if ((!npaint && !ntime) ||
+                    time_compare(&next_timeout, &next) < 0)
+                {
+                    next = next_timeout;
+                    ++ntime;
+                }
+            }
+            if (fade_next_timeout(sc, &next_timeout)) {
+                if ((!npaint && !ntime) ||
+                    time_compare(&next_timeout, &next) < 0)
+                {
+                    next = next_timeout;
+                    ++ntime;
+                }
+            }
+
             if (sc->need_repaint &&
-                (!npaint || time_compare(&sc->next_repaint, &next) < 0))
+                ((!npaint && !ntime) ||
+                 time_compare(&sc->next_repaint, &next) < 0))
             {
                 next = sc->next_repaint;
                 ++npaint;
@@ -297,7 +342,7 @@ run(d_display_t *dpy)
 
         gettimeofday(&now, 0);
 
-        if (!npaint)
+        if (!npaint && !ntime)
             /* wait forever, there is nothing that needs drawing */
             wait = NULL;
         else if (time_compare(&next, &now) > 0) {
@@ -306,9 +351,9 @@ run(d_display_t *dpy)
             wait = &next;
         }
         else {
-            /* don't wait cuz a redraw is due now already */
+            /* don't wait cuz a timer is due now already */
             next.tv_sec = 0;
-            next.tv_usec = 100;
+            next.tv_usec = 0;
             wait = &next;
         }
 
@@ -320,6 +365,7 @@ run(d_display_t *dpy)
         r = select(dpy->fd+1, &fds, NULL, NULL, wait);
         if (r == 0) {
             //printf("select timeout\n");
+            timeouts(dpy);
             paint(dpy);
             xcb_flush(dpy->conn);
         }
@@ -342,6 +388,7 @@ setup_functions(d_display_t *dpy)
         /* these can be plugins.. */
         id = 1;
         render_init(sc, id++);
+        fade_init(sc, id++);
     }
 }
 
@@ -354,6 +401,7 @@ cleanup_functions(d_display_t *dpy)
         d_screen_t *sc = it->data;
 
         /* these can be plugins.. */
+        fade_free(sc);
         render_free(sc);
     }
 }
diff --git a/fade.c b/fade.c
new file mode 100644 (file)
index 0000000..40e698e
--- /dev/null
+++ b/fade.c
@@ -0,0 +1,229 @@
+#include "efence.h"
+
+#include "render.h"
+#include "screen.h"
+#include "window.h"
+#include "time.h"
+#include "display.h"
+#include "list.h"
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <xcb/render.h>
+
+static int plugin_id;
+
+typedef struct {
+    void (*window_show)(d_window_t *w);
+    void (*window_hide)(d_window_t *w);
+    d_list_t *fades;
+
+//    unsigned int fade_step_size;
+    unsigned int fade_step_time;
+    unsigned int fade_total_time;
+
+    struct timeval next_timeout;
+} data_t;
+
+typedef struct {
+    d_window_t *w;
+    uint16_t    start;
+    uint16_t    end;
+    uint16_t    current;
+    struct timeval start_time;
+    struct timeval end_time;
+} fade_t;
+
+static void fade_window_show(d_window_t *w);
+static void fade_window_hide(d_window_t *w);
+static void start_fade(d_window_t *w, uint16_t start, uint16_t end);
+
+void
+fade_init(d_screen_t *sc, int id)
+{
+    plugin_id = id;
+
+    data_t *d = malloc(sizeof(data_t));
+    d->window_show = sc->window_show;
+    d->window_hide = sc->window_hide;
+    screen_add_plugin_data(sc, plugin_id, d);
+
+    sc->window_show = fade_window_show;
+    sc->window_hide = fade_window_hide;
+
+    d->fades = list_new();
+    d->fade_step_time = 10000;     /* 10 milliseconds */
+    d->fade_total_time = 200000;  /* 0.15 seconds */
+}
+
+void
+fade_free(d_screen_t *sc)
+{
+    data_t *d;
+    d_list_it_t *it;
+
+    d = screen_find_plugin_data(sc, plugin_id);
+    screen_remove_plugin_data(sc, plugin_id);
+
+    for (it = list_top(d->fades); it; it = it->next) {
+        fade_t *fade = it->data;
+        window_set_opacity(fade->w, 0xffff);
+        window_zombie_unref(fade->w);
+        window_unref(fade->w);
+        free(fade);
+    }
+    list_unref(d->fades);
+    free(d);
+}
+
+static void
+fade_window_show(d_window_t *w)
+{
+    data_t *d;
+
+    if (!window_is_input_only(w))
+        start_fade(w, 0, 0xffff);
+
+    d = screen_find_plugin_data(w->sc, plugin_id);
+    d->window_show(w);    
+}
+
+static void
+fade_window_hide(d_window_t *w)
+{
+    data_t *d;
+
+    if (!window_is_input_only(w))
+        start_fade(w, 0xffff, 0);
+
+    d = screen_find_plugin_data(w->sc, plugin_id);
+    d->window_hide(w);
+}
+
+static void
+start_fade(d_window_t *w, uint16_t start, uint16_t end)
+{
+    data_t *d;
+    d_list_it_t *it;
+    fade_t *fade;
+    gboolean newfade;
+    struct timeval now;
+
+    d = screen_find_plugin_data(w->sc, plugin_id);
+    gettimeofday(&now, NULL);
+
+    if (list_length(d->fades) == 0) {
+        d->next_timeout = now;
+        time_add(&d->next_timeout, d->fade_step_time);
+    }
+
+    /* look for an existing one */
+    fade = NULL;
+    for (it = list_top(d->fades); it; it = it->next) {
+        fade = it->data;
+        if (fade->w == w) break;
+    }
+    if (!it) {
+        fade = malloc(sizeof(fade_t));
+        fade->w = w;
+        list_append(d->fades, fade);
+        newfade = TRUE;
+    }
+    else
+        newfade = FALSE;
+
+    fade->start = start;
+    fade->end = end;
+
+    if (newfade) {
+        fade->start_time = now;
+        fade->end_time = now;
+        time_add(&fade->end_time, d->fade_total_time);
+        fade->current = start;
+
+        window_set_opacity(w, fade->current);
+
+        window_ref(w);
+        window_zombie_ref(w);
+    }
+    else {
+        /* figure out how far we have to go to finish the fade from where
+           we are from the previous fade */
+        long remain, total;
+        double percent;
+
+        total = ABS(fade->end - fade->start);
+        remain = ABS(fade->current - fade->end);
+        percent = (double)remain / total;
+
+        //printf("start %d end %d current %d\n", fade->start, fade->end,
+        //       fade->current);
+        //printf("remain %lu total %lu percent %f\n", remain, total, percent);
+
+        fade->start_time = now;
+        time_add(&fade->start_time, -(1-percent)*d->fade_total_time);
+        fade->end_time = now;
+        time_add(&fade->end_time, percent*d->fade_total_time);
+    }
+}
+
+int
+fade_next_timeout(struct d_screen *sc, struct timeval *tv)
+{
+    data_t *d;
+
+    d = screen_find_plugin_data(sc, plugin_id);
+
+    if (list_length(d->fades) == 0)
+        return FALSE;
+    else {
+        *tv = d->next_timeout;
+        return TRUE;
+    }
+}
+
+void
+fade_timeout(struct d_screen *sc, const struct timeval *now)
+{
+    data_t *d;
+    d_list_it_t *it, *next;
+
+    d = screen_find_plugin_data(sc, plugin_id);
+
+    for (it = list_top(d->fades); it; it = next) {
+        fade_t *fade = it->data;
+        struct timeval time_done, total_time;
+        unsigned long done, total;
+        double percent;
+
+        next = it->next;
+
+        time_difference(now, &fade->start_time, &time_done);
+        time_difference(&fade->end_time, &fade->start_time, &total_time);
+
+        /* count milliseconds */
+        done = time_done.tv_sec * 1000 + time_done.tv_usec / 1000;
+        total = total_time.tv_sec * 1000 + total_time.tv_usec / 1000;
+        percent = (double)done / total;
+
+        //printf("done %lu total %lu percent %f\n", done, total, percent);
+
+        if (percent >= 1)
+            fade->current = fade->end;
+        else if (fade->end > fade->start)
+            fade->current = fade->start + (fade->end - fade->start) * percent;
+        else
+            fade->current = fade->start - (fade->start - fade->end) * percent;
+
+        window_set_opacity(fade->w, fade->current);
+
+        if (fade->current == fade->end) {
+            list_delete_link(d->fades, it);
+            window_zombie_unref(fade->w);
+            window_unref(fade->w);
+
+            g_free(fade);
+        }
+    }
+    time_add(&d->next_timeout, d->fade_step_time);
+}
diff --git a/fade.h b/fade.h
new file mode 100644 (file)
index 0000000..3acf46d
--- /dev/null
+++ b/fade.h
@@ -0,0 +1,13 @@
+#ifndef dc__fade_h
+#define dc__fade_h
+
+#include <sys/time.h>
+
+struct d_screen;
+
+void fade_init(struct d_screen *sc, int id);
+void fade_free(struct d_screen *sc);
+int fade_next_timeout(struct d_screen *sc, struct timeval *tv);
+void fade_timeout(struct d_screen *sc, const struct timeval *now);
+
+#endif
index 795d824..113070e 100644 (file)
--- a/render.c
+++ b/render.c
@@ -16,7 +16,7 @@ typedef struct {
     void (*screen_paint)(d_screen_t *sc);
     void (*screen_root_pixmap_change)(d_screen_t *sc);
     void (*window_show)(d_window_t *w);
-    void (*window_hide)(d_window_t *w);
+    void (*window_zombie_dead)(d_window_t *w);
     void (*window_resize)(d_window_t *w);
     void (*window_opacity_change)(d_window_t *w);
 
@@ -64,7 +64,7 @@ static xcb_render_picture_t solid_picture(data_t *d, d_screen_t *sc,
                                           uint16_t g, uint16_t b);
 
 static void render_window_show(d_window_t *window);
-static void render_window_hide(d_window_t *window);
+static void render_window_zombie_dead(d_window_t *window);
 static void render_window_resize(d_window_t *window);
 static void render_window_opacity_change(d_window_t *w);
 
@@ -81,7 +81,7 @@ render_init(d_screen_t *sc, int id)
     d->screen_paint = sc->screen_paint;
     d->screen_root_pixmap_change = sc->screen_root_pixmap_change;
     d->window_show = sc->window_show;
-    d->window_hide = sc->window_hide;
+    d->window_zombie_dead = sc->window_zombie_dead;
     d->window_resize = sc->window_resize;
     d->window_opacity_change = sc->window_opacity_change;
     screen_add_plugin_data(sc, plugin_id, d);
@@ -89,7 +89,7 @@ render_init(d_screen_t *sc, int id)
     sc->screen_paint = render_paint;
     sc->screen_root_pixmap_change = render_root_pixmap_change;
     sc->window_show = render_window_show;
-    sc->window_hide = render_window_hide;
+    sc->window_zombie_dead = render_window_zombie_dead;
     sc->window_resize = render_window_resize;
     sc->window_opacity_change = render_window_opacity_change;
 
@@ -150,6 +150,20 @@ render_free(d_screen_t *sc)
     screen_remove_plugin_data(sc, plugin_id);
 }
 
+int
+render_next_timeout(struct d_screen *sc, struct timeval *tv)
+{
+    (void)sc;
+    (void)tv;
+    return FALSE;
+}
+
+void
+render_timeout(struct d_screen *sc, const struct timeval *now)
+{
+    (void)sc; (void)now;
+}
+
 void
 render_window_free(d_window_t *w, window_data_t *wd)
 {
@@ -188,12 +202,10 @@ render_window_show(d_window_t *w)
     render_update_shadow_picture(w, d, wd);
 
     window_add_plugin_data(w, plugin_id, wd);
-
-    window_ref(w);
 }
 
 static void
-render_window_hide(d_window_t *w)
+render_window_zombie_dead(d_window_t *w)
 {
     data_t *d;
     window_data_t *wd;
@@ -207,7 +219,7 @@ render_window_hide(d_window_t *w)
     }
 
     /* pass it on */
-    d->window_hide(w);
+    d->window_zombie_dead(w);
 }
 
 static xcb_render_picture_t
@@ -410,7 +422,8 @@ render_paint(d_screen_t *sc)
 
         window_get_area(w, &x, &y, &width, &height, &bwidth);
 
-        if (!window_is_input_only(w) && window_is_mapped(w) &&
+        if (!window_is_input_only(w) &&
+            (window_is_mapped(w) || window_is_zombie(w)) &&
             x < sc->super.width_in_pixels &&
             y < sc->super.height_in_pixels &&
             x + width > 0 && y + height > 0)
@@ -455,7 +468,8 @@ render_paint(d_screen_t *sc)
 
         window_get_area(w, &x, &y, &width, &height, &bwidth);
 
-        if (!window_is_input_only(w) && window_is_mapped(w) &&
+        if (!window_is_input_only(w) &&
+            (window_is_mapped(w) || window_is_zombie(w)) &&
             x < sc->super.width_in_pixels &&
             y < sc->super.height_in_pixels &&
             (x + width > 0 || x + width + d->xshadowoff > 0) &&
index 0885883..48a84b1 100644 (file)
--- a/render.h
+++ b/render.h
@@ -1,9 +1,13 @@
 #ifndef dc__render_h
 #define dc__render_h
 
+#include <sys/time.h>
+
 struct d_screen;
 
 void render_init(struct d_screen *sc, int id);
 void render_free(struct d_screen *sc);
+int render_next_timeout(struct d_screen *sc, struct timeval *tv);
+void render_timeout(struct d_screen *sc, const struct timeval *now);
 
 #endif
index 1633f6a..af7772d 100644 (file)
--- a/screen.c
+++ b/screen.c
@@ -284,7 +284,6 @@ screen_remove_window(d_screen_t *sc, struct d_window *w)
 
     window_destroy_damage(w);
     g_hash_table_remove(sc->winhash, &w->id);
-    sc->window_become_zombie(w);
     window_unref(w);
 }
 
@@ -327,7 +326,7 @@ screen_timestamp(d_screen_t *sc)
             break;
         }
     }
-    printf("created timestamp %lu\n", (unsigned long) time);
+    //printf("created timestamp %lu\n", (unsigned long) time);
     return time;
 }
 
@@ -381,6 +380,7 @@ screen_setup_default_functions(d_screen_t *sc)
     sc->window_show = window_show;
     sc->window_hide = window_hide;
     sc->window_become_zombie = window_become_zombie;
+    sc->window_zombie_dead = window_zombie_dead;
     sc->window_move = window_move;
     sc->window_resize = window_resize;
     sc->window_reshape = window_reshape;
@@ -428,7 +428,7 @@ screen_get_root_pixmap(d_screen_t *sc)
                 if (rep->type == sc->dpy->a.pixmap && rep->length >= 1) {
                     sc->root_pixmap =
                         ((xcb_pixmap_t*)xcb_get_property_value(rep))[0];
-                    printf("got root pixmap 0x%x\n", sc->root_pixmap);
+                    //printf("got root pixmap 0x%x\n", sc->root_pixmap);
                 }
                 free(rep);
             }
index 16e0ad5..15ef143 100644 (file)
--- a/screen.h
+++ b/screen.h
@@ -37,6 +37,7 @@ typedef struct d_screen {
     void (*window_show)(struct d_window *w);
     void (*window_hide)(struct d_window *w);
     void (*window_become_zombie)(struct d_window *w);
+    void (*window_zombie_dead)(struct d_window *w);
     void (*window_move)(struct d_window *w);
     void (*window_resize)(struct d_window *w);
     void (*window_reshape)(struct d_window *w);
diff --git a/time.c b/time.c
index a90b1cd..889fdd7 100644 (file)
--- a/time.c
+++ b/time.c
@@ -21,7 +21,7 @@ time_add(struct timeval *tv, long microseconds)
 }
 
 long
-time_compare(struct timeval *a, struct timeval *b)
+time_compare(const struct timeval *a, const struct timeval *b)
 {
     long r;
     if ((r = a->tv_sec - b->tv_sec)) return r;
@@ -29,7 +29,8 @@ time_compare(struct timeval *a, struct timeval *b)
 }
 
 void
-time_difference(struct timeval *a, struct timeval *b, struct timeval *r)
+time_difference(const struct timeval *a, const struct timeval *b,
+                struct timeval *r)
 {
     struct timeval v;
     v.tv_sec = a->tv_sec - b->tv_sec;
diff --git a/time.h b/time.h
index 24cd3f0..c487e77 100644 (file)
--- a/time.h
+++ b/time.h
@@ -4,7 +4,8 @@
 #include <sys/time.h>
 
 void time_add(struct timeval *tv, long microseconds);
-long time_compare(struct timeval *a, struct timeval *b);
-void time_difference(struct timeval *a, struct timeval *b, struct timeval *r);
+long time_compare(const struct timeval *a, const struct timeval *b);
+void time_difference(const struct timeval *a, const struct timeval *b,
+                     struct timeval *r);
 
 #endif
index 8d420ee..fb26a59 100644 (file)
--- a/window.c
+++ b/window.c
@@ -20,6 +20,7 @@ typedef struct {
 
     /* private stuff */
     int              ref;
+    int              zombieref;
 
     /* queried things, don't read them directly from the struct */
     int                 x, y, w, h, bw;
@@ -68,6 +69,7 @@ window_new(xcb_window_t id, struct d_screen *sc)
     w = malloc(sizeof(d_window_priv_t));
     w->id = id;
     w->ref = 1;
+    w->zombieref = 0;
     w->sc = sc;
     w->zombie = FALSE;
     w->mapped = FALSE;
@@ -109,17 +111,9 @@ window_unref(d_window_t *pubw)
     if (w && --w->ref == 0) {
         screen_stacking_remove(w->sc, (d_window_t*)w);
 
-        if (w->region) {
-            xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
-            w->region = XCB_NONE;
-        }
-
-        if (w->pixmap) {
-            /* this may cause an error if the pixmap was never valid, but
-               that's fine */
-            xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
-            w->pixmap = XCB_NONE;
-        }
+        assert(w->zombieref == 0);
+        while (w->zombieref)
+            window_zombie_unref(pubw);
 
         list_unref(w->plugin_data);
         free(w);
@@ -213,6 +207,9 @@ window_show(d_window_t *pubw)
     window_update_user_opacity(pubw);
     window_update_type(w);
     w->mapped = TRUE;
+
+    /* hold one reference for ourselves */
+    window_zombie_ref(pubw);
 }
 
 void
@@ -231,6 +228,9 @@ window_hide(d_window_t *pubw)
                                  XCB_CW_EVENT_MASK, &mask);
 
     w->mapped = FALSE;
+
+    /* try to free zombie things */
+    window_zombie_unref(pubw);
 }
 
 void
@@ -251,6 +251,16 @@ window_become_zombie(d_window_t *pubw)
     w->zombie = TRUE;
 }
 
+void
+window_zombie_dead(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+    if (!w->zombie) return;
+
+    w->zombie = FALSE;
+}
+
 gboolean
 window_is_zombie(d_window_t *pubw)
 {
@@ -586,8 +596,7 @@ window_update_type(d_window_priv_t *w)
         w->type = DC_WINDOW_TYPE_NORMAL;
         w->client = w->id;
     }
-
-    printf("window 0x%x type %d\n", w->id, w->type);
+    //printf("window 0x%x type %d\n", w->id, w->type);
 }
 
 d_window_type_t
@@ -631,6 +640,41 @@ window_set_opacity(d_window_t *pubw, uint16_t o)
     d_window_priv_t *w = (d_window_priv_t*)pubw;
 
     w->opacity = o;
-    if (w->mapped)
+    if (w->mapped || w->zombie) {
         w->sc->window_opacity_change(pubw);
+        screen_refresh(w->sc);
+    }
+    //printf("mapped %d opacity 0x%x\n", w->mapped, w->opacity);
+}
+
+void
+window_zombie_ref(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+    ++w->zombieref;
+}
+
+void
+window_zombie_unref(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+    if (--w->zombieref == 0) {
+        w->zombie = FALSE;
+
+        if (w->region) {
+            xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
+            w->region = XCB_NONE;
+        }
+
+        if (w->pixmap) {
+            /* this may cause an error if the pixmap was never valid, but
+               that's fine */
+            xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
+            w->pixmap = XCB_NONE;
+        }
+
+        w->sc->window_zombie_dead(pubw);
+    }
 }
index 6d0bc50..9787f84 100644 (file)
--- a/window.h
+++ b/window.h
@@ -40,6 +40,7 @@ void window_hide(d_window_t *w);
 void window_fake_unmapped(d_window_t *w);
 
 void window_become_zombie(d_window_t *w);
+void window_zombie_dead(d_window_t *w);
 
 void window_configure(d_window_t *w, int x, int y, int width, int height,
                       int border_width);
@@ -75,4 +76,7 @@ d_window_type_t window_get_type(d_window_t *w);
 uint16_t window_get_opacity(d_window_t *w);
 void window_set_opacity(d_window_t *w, uint16_t o);
 
+void window_zombie_ref(d_window_t *w);
+void window_zombie_unref(d_window_t *w);
+
 #endif