the code is more or less there. have to figure out how to find the fbconfigs right...
[dana/dcompmgr.git] / window.c
index 138c1fe..b14de39 100644 (file)
--- a/window.c
+++ b/window.c
@@ -11,6 +11,8 @@
 #include <xcb/composite.h>
 #include <xcb/damage.h>
 
+#define TOPLEVEL_WINDOW_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE)
+
 typedef struct {
     /* public stuff */
     xcb_window_t     id;
@@ -18,20 +20,30 @@ typedef struct {
 
     /* private stuff */
     int              ref;
+    int              zombieref;
 
     /* queried things, don't read them directly from the struct */
-    int              x, y, w, h, bw;
-    gboolean         attr_mapped;
-    gboolean         input_only;
-    xcb_visualid_t   visual;
-    xcb_pixmap_t     pixmap;
-
-    double           opacity;
-
-    gboolean         mapped;
-    gboolean         zombie;
-
-    d_list_t        *plugin_data;
+    int                 x, y, w, h, bw;
+    gboolean            attr_mapped;
+    gboolean            input_only;
+    uint8_t             depth;
+    xcb_visualid_t      visual;
+    xcb_pixmap_t        pixmap;
+    xcb_xfixes_region_t region;
+    d_window_type_t     type;
+    /* the window id where we got the window type from.  further property
+       queries should go to this window */
+    xcb_window_t        client;
+
+    /* opacity is from 0xffff to 0, these 2 are combined to give the final
+       result */
+    uint16_t            opacity;      /* this one is set by plugins/settings */
+    uint16_t            user_opacity; /* this one is set by the user */
+
+    gboolean            mapped;
+    gboolean            zombie;
+
+    d_list_t           *plugin_data;
 
     xcb_damage_damage_t damage;
 
@@ -39,12 +51,15 @@ typedef struct {
     xcb_get_window_attributes_cookie_t ck_get_attr;
     gboolean waiting_geom;
     xcb_get_geometry_cookie_t          ck_get_geom;
-    gboolean waiting_pixmap;
-    xcb_void_cookie_t                  ck_get_pixmap;
+    gboolean waiting_opac;
+    xcb_get_property_cookie_t          ck_get_opac;
 } d_window_priv_t;
 
 static void window_get_attributes_reply(d_window_priv_t *w);
 static void window_get_geometry_reply(d_window_priv_t *w);
+static void window_update_pixmap(d_window_priv_t *w);
+static void window_update_region(d_window_priv_t *w);
+static void window_update_type(d_window_priv_t *w);
 
 d_window_t*
 window_new(xcb_window_t id, struct d_screen *sc)
@@ -54,11 +69,16 @@ 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;
     w->pixmap = XCB_NONE;
     w->damage = XCB_NONE;
+    w->region = XCB_NONE;
+    w->type = DC_WINDOW_TYPE_INVALID;
+    w->opacity = 0xffff;
+    w->user_opacity = 0xffff;
 
     screen_stacking_add(sc, (d_window_t*)w);
 
@@ -68,8 +88,6 @@ window_new(xcb_window_t id, struct d_screen *sc)
     w->ck_get_geom = xcb_get_geometry(sc->dpy->conn, id);
     w->waiting_geom = TRUE;
 
-    w->waiting_pixmap = FALSE;
-
     w->plugin_data = list_new();
 
     //printf("new window 0x%x\n", w->id);
@@ -91,40 +109,78 @@ window_unref(d_window_t *pubw)
     d_window_priv_t *w = (d_window_priv_t*)pubw;
 
     if (w && --w->ref == 0) {
-        xcb_pixmap_t p;
-
         screen_stacking_remove(w->sc, (d_window_t*)w);
 
-        if ((p = window_get_pixmap(pubw))) {
-            xcb_free_pixmap(w->sc->dpy->conn, p);
-            w->pixmap = XCB_NONE;
-        }
+        assert(w->zombieref == 0);
+        while (w->zombieref)
+            window_zombie_unref(pubw);
 
         list_unref(w->plugin_data);
         free(w);
     }
 }
 
+xcb_pixmap_t
+window_get_pixmap(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+    return w->pixmap;
+}
+
+void
+window_update_user_opacity(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+    w->ck_get_opac =
+        xcb_get_property(w->sc->dpy->conn, FALSE, w->id,
+                         w->sc->dpy->a.net_wm_window_opacity,
+                         w->sc->dpy->a.cardinal, 0, 1);
+    w->waiting_opac = TRUE;
+}
+
+static void
+window_update_region(d_window_priv_t *w)
+{
+    int x, y, wi, hei, bw;
+
+    if (window_is_zombie((d_window_t*)w)) return;
+
+    if (w->region) {
+        xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
+        w->region = XCB_NONE;
+    }
+
+    w->region = xcb_generate_id(w->sc->dpy->conn);
+    xcb_xfixes_create_region_from_window(w->sc->dpy->conn, w->region,
+                                         w->id, XCB_SHAPE_SK_BOUNDING);
+    window_get_area((d_window_t*)w, &x, &y, &wi, &hei, &bw);
+    xcb_xfixes_translate_region(w->sc->dpy->conn, w->region, x+bw, y+bw);
+}
+
 static void
 window_update_pixmap(d_window_priv_t *w)
 {
-    xcb_pixmap_t p;
-
     if (window_is_zombie((d_window_t*)w)) return;
 
-    /* XXX can we save it for until we get the new pixmap? */
-    if ((p = window_get_pixmap((d_window_t*)w))) {
-        xcb_free_pixmap(w->sc->dpy->conn, p);
+    /* the pixmap may not be valid even though it is non-zero, but
+       we can free it anyways and let it fail.  we don't need to wait
+       for a response from the server */
+    if (w->pixmap) {
+        xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
         w->pixmap = XCB_NONE;
     }
 
     //printf("updating pixmap for 0x%x\n", w->id);
 
+    /* we don't check the result of this call, because it seems that sometimes
+       the X server just doesn't reply.  if we check it, we end up hanging
+       sometimes waiting for the reply */
     w->pixmap = xcb_generate_id(w->sc->dpy->conn);
-    w->ck_get_pixmap = 
-        xcb_composite_name_window_pixmap_checked(w->sc->dpy->conn,
-                                                 w->id, w->pixmap);
-    w->waiting_pixmap = TRUE;
+    xcb_composite_name_window_pixmap(w->sc->dpy->conn, w->id, w->pixmap);
+    //printf("requested pixmap sequence %u\n", w->ck_get_pixmap.sequence);
+    //fflush(stdout);
     xcb_flush(w->sc->dpy->conn);
 }
 
@@ -132,25 +188,51 @@ void
 window_show(d_window_t *pubw)
 {
     d_window_priv_t *w = (d_window_priv_t*)pubw;
+    unsigned int mask;
 
     assert(!w->mapped);
 
     //printf("show window 0x%x\n", w->id);
 
+    /* make sure this is before we update the window's region */
+    if (w->sc->dpy->shape.present)
+        xcb_shape_select_input(w->sc->dpy->conn, w->id, TRUE);
+
+    mask = TOPLEVEL_WINDOW_EVENT_MASK;
+    xcb_change_window_attributes(w->sc->dpy->conn, w->id,
+                                 XCB_CW_EVENT_MASK, &mask);
+
+    assert(w->zombieref == 0);
+
     window_update_pixmap(w);
+    window_update_region(w);
+    window_update_user_opacity(pubw);
+    window_update_type(w);
     w->mapped = TRUE;
+
+    /* hold one reference for ourselves */
+    window_zombie_ref(pubw);
 }
 
 void
 window_hide(d_window_t *pubw)
 {
     d_window_priv_t *w = (d_window_priv_t*)pubw;
+    unsigned int mask;
 
     assert(w->mapped);
 
     //printf("hide window 0x%x\n", w->id);
+    if (w->sc->dpy->shape.present)
+        xcb_shape_select_input(w->sc->dpy->conn, w->id, FALSE);
+    mask = 0;
+    xcb_change_window_attributes(w->sc->dpy->conn, w->id,
+                                 XCB_CW_EVENT_MASK, &mask);
 
     w->mapped = FALSE;
+
+    /* try to free zombie things */
+    window_zombie_unref(pubw);
 }
 
 void
@@ -171,6 +253,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)
 {
@@ -246,12 +338,14 @@ window_get_geometry_reply(d_window_priv_t *w)
         w->w = rep->width;
         w->h = rep->height;
         w->bw = rep->border_width;
+        w->depth = rep->depth;
         free(rep);
     }
     else {
         w->x = w->y = -1;
         w->w = w->h = 1;
         w->bw = 0;
+        w->depth = 0;
     }
     if (err) {
         printf("error getting geometry for window 0x%x\n", w->id);
@@ -276,24 +370,20 @@ window_is_attr_mapped(d_window_t *pubw)
     return w->attr_mapped;
 }
 
-xcb_pixmap_t
-window_get_pixmap(d_window_t *pubw)
+gboolean
+window_is_argb(d_window_t *pubw)
 {
-    d_window_priv_t *w = (d_window_priv_t*)pubw;
+    uint8_t depth = window_get_depth(pubw);
+    return depth == 32;
+}
 
-    if (w->waiting_pixmap) {
-        xcb_generic_error_t *err;
-        //printf("** checking get pixmap 0x%x\n", w->id);
-        err = xcb_request_check(w->sc->dpy->conn, w->ck_get_pixmap);
-        if (err) {
-            w->pixmap = XCB_NONE;
-            printf("error getting named pixmap for window 0x%x\n", w->id);
-            free(err);
-        }
-        w->waiting_pixmap = FALSE;
-    }
-    //printf("returning pixmap 0x%x for window 0x%x\n", w->pixmap, w->id);
-    return w->pixmap;
+uint8_t
+window_get_depth(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+    if (w->waiting_geom)
+        window_get_geometry_reply(w);
+    return w->depth;
 }
 
 xcb_visualid_t
@@ -305,6 +395,14 @@ window_get_visual(d_window_t *pubw)
     return w->visual;
 }
 
+xcb_xfixes_region_t
+window_get_region(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+    return w->region;
+}
+
 void
 window_configure(d_window_t *pubw, int x, int y, int width, int height,
                  int border_width)
@@ -322,15 +420,28 @@ window_configure(d_window_t *pubw, int x, int y, int width, int height,
 }
 
 void
-window_move(d_window_t *w)
+window_move(d_window_t *pubw)
 {
-    (void)w;
+    //d_window_priv_t *w = (d_window_priv_t*)pubw;
+    window_update_region((d_window_priv_t*)pubw);
 }
 
 void
 window_resize(d_window_t *w)
 {
     window_update_pixmap((d_window_priv_t*)w);
+    window_update_region((d_window_priv_t*)w);
+}
+
+void
+window_reshape(d_window_t *w)
+{
+    window_update_region((d_window_priv_t*)w);
+}
+
+void window_opacity_change(d_window_t *w)
+{
+    screen_refresh(w->sc);
 }
 
 void
@@ -368,7 +479,8 @@ window_create_damage(d_window_t *pubw)
     }
 }
 
-void window_destroy_damage(d_window_t *pubw)
+void
+window_destroy_damage(d_window_t *pubw)
 {
     d_window_priv_t *w = (d_window_priv_t*)pubw;
 
@@ -378,3 +490,213 @@ void window_destroy_damage(d_window_t *pubw)
         w->damage = XCB_NONE;
     }
 }
+
+static void
+type_request(d_display_t *dpy, d_window_priv_t *w, xcb_window_t *id, int nid)
+{
+    xcb_get_property_cookie_t *cks;
+    int i;
+
+    cks = malloc(sizeof(xcb_get_property_cookie_t) * nid * 2);
+    for (i = 0; i < nid; ++i) {
+        cks[i*2+0] =
+            xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
+                                       dpy->a.net_wm_window_type,
+                                       dpy->a.atom, 0, 20);
+        cks[i*2+1] =
+            xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
+                                       dpy->a.wm_transient_for,
+                                       dpy->a.window, 0, 1);
+    }
+
+    for (i = 0; i < nid; ++i) {
+        xcb_get_property_reply_t *rep;
+        d_window_type_t type = DC_WINDOW_TYPE_INVALID;
+
+        rep = xcb_get_property_reply(dpy->conn, cks[i*2+0], NULL);
+        if (rep) {
+            if (!w->type && rep->type == dpy->a.atom && rep->length >= 1) {
+                xcb_atom_t *a = (xcb_atom_t*)(xcb_get_property_value(rep));
+                uint32_t j;
+                for (j = 0; j < rep->length; ++j) {
+                    if (a[j] == dpy->a.net_wm_window_type_normal) {
+                        type = DC_WINDOW_TYPE_NORMAL; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_desktop) {
+                        type = DC_WINDOW_TYPE_DESKTOP; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_dock) {
+                        type = DC_WINDOW_TYPE_DOCK; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_dialog) {
+                        type = DC_WINDOW_TYPE_DIALOG; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_toolbar) {
+                        type = DC_WINDOW_TYPE_TOOLBAR; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_menu) {
+                        type = DC_WINDOW_TYPE_MENU; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_utility) {
+                        type = DC_WINDOW_TYPE_UTILITY; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_splash) {
+                        type = DC_WINDOW_TYPE_SPLASH; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_dropdown_menu) {
+                        type = DC_WINDOW_TYPE_DROPDOWN_MENU; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_popup_menu) {
+                        type = DC_WINDOW_TYPE_POPUP_MENU; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_tooltip) {
+                        type = DC_WINDOW_TYPE_TOOLTIP; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_notification) {
+                        type = DC_WINDOW_TYPE_NOTIFICATION; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_combo) {
+                        type = DC_WINDOW_TYPE_COMBO; break;
+                    }
+                    if (a[j] == dpy->a.net_wm_window_type_dnd) {
+                        type = DC_WINDOW_TYPE_DND; break;
+                    }
+                }
+            }
+            free(rep);
+        }
+        rep = xcb_get_property_reply(dpy->conn, cks[i*2+1], NULL);
+        if (rep) {
+            if (!w->type && rep->type == dpy->a.window && rep->length == 1)
+                type = DC_WINDOW_TYPE_DIALOG;
+            free(rep);
+        }
+
+        /* also save the window id that we got the type/transient hint from */
+        if (!w->type && type) {
+            w->type = type;
+            w->client = id[i];
+        }
+    }
+
+
+    for (i = 0; i < nid && !w->type; ++i) {
+        xcb_query_tree_cookie_t ck;
+        xcb_query_tree_reply_t *rep;
+
+        ck = xcb_query_tree(dpy->conn, id[i]);
+        rep = xcb_query_tree_reply(dpy->conn, ck, NULL);
+        if (rep) {
+            int num = xcb_query_tree_children_length(rep);
+            xcb_window_t *ch = xcb_query_tree_children(rep);
+
+            type_request(dpy, w, ch, num);
+            free(rep);
+        }
+    }
+}
+
+static void
+window_update_type(d_window_priv_t *w)
+{
+    w->type = DC_WINDOW_TYPE_INVALID;
+    type_request(w->sc->dpy, w, &w->id, 1);
+    if (!w->type) {
+        w->type = DC_WINDOW_TYPE_NORMAL;
+        w->client = w->id;
+    }
+    //printf("window 0x%x type %d\n", w->id, w->type);
+}
+
+d_window_type_t
+window_get_type(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+    return w->type;
+}
+
+uint16_t
+window_get_opacity(d_window_t *pubw)
+{
+    d_window_priv_t *w = (d_window_priv_t*)pubw;
+    unsigned long long l;
+
+    if (w->waiting_opac) {
+        xcb_get_property_reply_t *rep;
+
+        w->user_opacity = 0xffff;
+        rep = xcb_get_property_reply(w->sc->dpy->conn, w->ck_get_opac, NULL);
+        if (rep) {
+            if (rep->type == w->sc->dpy->a.cardinal && rep->length >= 1) {
+                l = ((uint32_t*)xcb_get_property_value(rep))[0];
+                l = 0xffff * l / 0xffffffff;
+                w->user_opacity = l;
+            }
+            free(rep);
+        }
+        w->waiting_opac = FALSE;
+    }
+
+    l = w->opacity;
+    l = l * w->user_opacity / 0xffff;
+    return l;
+}
+
+void
+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 || w->zombie)
+        w->sc->window_opacity_change(pubw);
+    //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->sc->window_zombie_dead(pubw);
+
+        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;
+        }
+
+        /* reset the opacity */
+        w->opacity = 0xffff;
+    }
+}
+
+void
+window_damage(d_window_t *w)
+{
+    (void)w;
+}
+
+void
+window_restack(d_window_t *w, d_window_t *above)
+{
+    screen_stacking_move_above(w->sc, w, above);
+}