+#include "efence.h"
+
#include "window.h"
#include "screen.h"
+#include "plugin.h"
+#include "list.h"
#include "display.h"
#include <stdlib.h>
+#include <assert.h>
#include <stdio.h>
#include <xcb/composite.h>
+#include <xcb/damage.h>
+
+#define TOPLEVEL_WINDOW_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE)
typedef struct {
/* public stuff */
/* private stuff */
int ref;
+ int zombieref;
/* queried things, don't read them directly from the struct */
- int x, y, w, h;
- gboolean mapped;
- gboolean input_only;
-
- gboolean zombie;
-
- int opacity;
-
- xcb_pixmap_t pixmap;
-
+ 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;
+
+ gboolean waiting_attr;
xcb_get_window_attributes_cookie_t ck_get_attr;
+ gboolean waiting_geom;
xcb_get_geometry_cookie_t ck_get_geom;
- 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)
w = malloc(sizeof(d_window_priv_t));
w->id = id;
w->ref = 1;
+ w->zombieref = 0;
w->sc = sc;
w->zombie = FALSE;
- w->opacity = WINDOW_OPACITY_MAX;
+ 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);
w->ck_get_attr = xcb_get_window_attributes(sc->dpy->conn, id);
+ w->waiting_attr = TRUE;
+
w->ck_get_geom = xcb_get_geometry(sc->dpy->conn, id);
+ w->waiting_geom = TRUE;
- w->ck_get_pixmap.sequence = 0;
+ w->plugin_data = list_new();
//printf("new window 0x%x\n", w->id);
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_show(d_window_t *pubw)
+window_update_user_opacity(d_window_t *pubw)
{
d_window_priv_t *w = (d_window_priv_t*)pubw;
- xcb_pixmap_t p;
- if (window_is_mapped(pubw)) return;
+ 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;
- printf("show window 0x%x\n", w->id);
+ if (w->region) {
+ xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
+ w->region = XCB_NONE;
+ }
- /* XXX can we save it for until we get the new pixmap? */
- if ((p = window_get_pixmap(pubw))) {
- xcb_free_pixmap(w->sc->dpy->conn, p);
+ 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)
+{
+ if (window_is_zombie((d_window_t*)w)) return;
+
+ /* 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;
}
- w->mapped = TRUE;
+ //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);
+ 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);
}
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;
- if (!window_is_mapped(pubw)) return;
+ assert(w->mapped);
- printf("hide window 0x%x\n", w->id);
+ //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
+window_fake_unmapped(d_window_t *pubw)
+{
+ d_window_priv_t *w = (d_window_priv_t*)pubw;
w->mapped = FALSE;
}
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)
{
window_is_input_only(d_window_t *pubw)
{
d_window_priv_t *w = (d_window_priv_t*)pubw;
- if (w->ck_get_attr.sequence)
+ if (w->waiting_attr)
window_get_attributes_reply(w);
return w->input_only;
}
void
-window_get_area(d_window_t *pubw, int *x, int *y, int *width, int *height)
+window_get_area(d_window_t *pubw, int *x, int *y, int *width, int *height,
+ int *border_width)
{
d_window_priv_t *w = (d_window_priv_t*)pubw;
- if (w->ck_get_geom.sequence)
+ if (w->waiting_geom)
window_get_geometry_reply(w);
*x = w->x;
*y = w->y;
*width = w->w;
*height = w->h;
+ *border_width = w->bw;
}
static void
if (rep) {
w->input_only = rep->_class == XCB_WINDOW_CLASS_INPUT_ONLY;
- w->mapped = rep->map_state != XCB_MAP_STATE_UNMAPPED;
+ w->attr_mapped = rep->map_state != XCB_MAP_STATE_UNMAPPED;
+ w->visual = rep->visual;
+ //printf("0x%x attributes mapped %d\n", w->id, w->mapped);
free(rep);
}
else {
w->input_only = TRUE;
- w->mapped = FALSE;
+ w->attr_mapped = FALSE;
+ w->visual = XCB_NONE;
}
if (err) {
printf("error getting attributes for window 0x%x\n", w->id);
free(err);
}
- w->ck_get_attr.sequence = 0;
+ w->waiting_attr = FALSE;
}
static void
if (rep) {
w->x = rep->x;
w->y = rep->y;
- w->w = rep->width + rep->border_width * 2;
- w->h = rep->height + rep->border_width * 2;
+ 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);
free(err);
}
- w->ck_get_geom.sequence = 0;
+ w->waiting_geom = FALSE;
}
gboolean
window_is_mapped(d_window_t *pubw)
{
d_window_priv_t *w = (d_window_priv_t*)pubw;
- if (w->ck_get_attr.sequence)
- window_get_attributes_reply(w);
return w->mapped;
}
-xcb_pixmap_t
-window_get_pixmap(d_window_t *pubw)
+gboolean
+window_is_attr_mapped(d_window_t *pubw)
{
d_window_priv_t *w = (d_window_priv_t*)pubw;
+ if (w->waiting_attr)
+ window_get_attributes_reply(w);
+ return w->attr_mapped;
+}
- if (w->ck_get_pixmap.sequence) {
- 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) {
- printf("error getting named pixmap for window 0x%x\n", w->id);
- free(err);
- }
- w->pixmap = XCB_NONE;
- }
- //printf("returning pixmap 0x%x for window 0x%x\n", w->pixmap, w->id);
- return w->pixmap;
+gboolean
+window_is_argb(d_window_t *pubw)
+{
+ uint8_t depth = window_get_depth(pubw);
+ return depth == 32;
+}
+
+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
+window_get_visual(d_window_t *pubw)
+{
+ d_window_priv_t *w = (d_window_priv_t*)pubw;
+ if (w->waiting_attr)
+ window_get_attributes_reply(w);
+ 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
d_window_priv_t *w = (d_window_priv_t*)pubw;
/* this overrides any reply from our get_geometry call */
- if (w->ck_get_geom.sequence)
- w->ck_get_geom.sequence = 0;
+ if (w->waiting_geom)
+ w->waiting_geom = FALSE;
w->x = x;
w->y = y;
- w->w = width + border_width * 2;
- w->h = height + border_width * 2;
+ w->w = width;
+ w->h = height;
+ w->bw = border_width;
+}
+
+void
+window_move(d_window_t *pubw)
+{
+ //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
+window_add_plugin_data(d_window_t *pubw, int id, void *data)
+{
+ d_window_priv_t *w = (d_window_priv_t*)pubw;
+ plugin_data_add(w->plugin_data, id, data);
+}
+
+void*
+window_find_plugin_data(d_window_t *pubw, int id)
+{
+ d_window_priv_t *w = (d_window_priv_t*)pubw;
+ return plugin_data_find(w->plugin_data, id);
+}
+
+void
+window_remove_plugin_data(d_window_t *pubw, int id)
+{
+ d_window_priv_t *w = (d_window_priv_t*)pubw;
+ plugin_data_remove(w->plugin_data, id);
+}
+
+void
+window_create_damage(d_window_t *pubw)
+{
+ d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+ if (!window_is_input_only(pubw)) {
+ assert(w->damage == XCB_NONE);
+ w->damage = xcb_generate_id(w->sc->dpy->conn);
+ //printf("creating damage 0x%x\n", w->damage);
+ xcb_damage_create(w->sc->dpy->conn, w->damage, w->id,
+ XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
+ }
+}
+
+void
+window_destroy_damage(d_window_t *pubw)
+{
+ d_window_priv_t *w = (d_window_priv_t*)pubw;
+
+ if (w->damage) {
+ //printf("destroying damage 0x%x\n", w->damage);
+ xcb_damage_destroy(w->sc->dpy->conn, w->damage);
+ 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);
}