11 #include <xcb/composite.h>
12 #include <xcb/damage.h>
14 #define TOPLEVEL_WINDOW_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE)
25 /* queried things, don't read them directly from the struct */
30 xcb_visualid_t visual;
32 xcb_xfixes_region_t region;
34 /* the window id where we got the window type from. further property
35 queries should go to this window */
38 /* opacity is from 0xffff to 0, these 2 are combined to give the final
40 uint16_t opacity; /* this one is set by plugins/settings */
41 uint16_t user_opacity; /* this one is set by the user */
46 d_list_t *plugin_data;
48 xcb_damage_damage_t damage;
50 gboolean waiting_attr;
51 xcb_get_window_attributes_cookie_t ck_get_attr;
52 gboolean waiting_geom;
53 xcb_get_geometry_cookie_t ck_get_geom;
54 gboolean waiting_opac;
55 xcb_get_property_cookie_t ck_get_opac;
56 gboolean waiting_pixmap;
57 xcb_void_cookie_t ck_pixmap;
58 gboolean waiting_region;
59 xcb_void_cookie_t ck_region;
62 static void window_get_attributes_reply(d_window_priv_t *w);
63 static void window_get_geometry_reply(d_window_priv_t *w);
64 static void window_update_pixmap(d_window_priv_t *w);
65 static void window_update_region(d_window_priv_t *w);
66 static void window_update_type(d_window_priv_t *w);
69 window_new(xcb_window_t id, struct d_screen *sc)
73 w = malloc(sizeof(d_window_priv_t));
83 w->type = DC_WINDOW_TYPE_INVALID;
85 w->user_opacity = 0xffff;
86 w->waiting_pixmap = FALSE;
88 screen_stacking_add(sc, (d_window_t*)w);
90 w->ck_get_attr = xcb_get_window_attributes(sc->dpy->conn, id);
91 w->waiting_attr = TRUE;
93 w->ck_get_geom = xcb_get_geometry(sc->dpy->conn, id);
94 w->waiting_geom = TRUE;
96 w->plugin_data = list_new();
98 //printf("new window 0x%x\n", w->id);
100 return (d_window_t*)w;
104 window_ref(d_window_t *pubw)
106 d_window_priv_t *w = (d_window_priv_t*)pubw;
112 window_unref(d_window_t *pubw)
114 d_window_priv_t *w = (d_window_priv_t*)pubw;
116 if (w && --w->ref == 0) {
117 screen_stacking_remove(w->sc, (d_window_t*)w);
119 assert(w->zombieref == 0);
121 window_zombie_unref(pubw);
123 list_unref(w->plugin_data);
129 window_get_pixmap(d_window_t *pubw)
131 d_window_priv_t *w = (d_window_priv_t*)pubw;
133 if (w->waiting_pixmap) {
134 xcb_generic_error_t *err;
136 err = xcb_request_check(w->sc->dpy->conn, w->ck_pixmap);
138 w->pixmap = XCB_NONE;
139 display_error(w->sc->dpy, err);
142 w->waiting_pixmap = FALSE;
149 window_update_user_opacity(d_window_t *pubw)
151 d_window_priv_t *w = (d_window_priv_t*)pubw;
154 xcb_get_property(w->sc->dpy->conn, FALSE, w->id,
155 w->sc->dpy->a.net_wm_window_opacity,
156 w->sc->dpy->a.cardinal, 0, 1);
157 w->waiting_opac = TRUE;
158 xcb_flush(w->sc->dpy->conn);
162 window_update_region(d_window_priv_t *w)
164 int x, y, wi, hei, bw;
166 if (window_is_zombie((d_window_t*)w)) return;
169 xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
170 w->region = XCB_NONE;
173 w->region = xcb_generate_id(w->sc->dpy->conn);
175 xcb_xfixes_create_region_from_window_checked(w->sc->dpy->conn,
178 XCB_SHAPE_SK_BOUNDING);
179 window_get_area((d_window_t*)w, &x, &y, &wi, &hei, &bw);
180 xcb_xfixes_translate_region(w->sc->dpy->conn, w->region, x+bw, y+bw);
181 xcb_flush(w->sc->dpy->conn);
182 w->waiting_region = TRUE;
186 window_update_pixmap(d_window_priv_t *w)
188 if (window_is_zombie((d_window_t*)w)) return;
190 /* the pixmap may not be valid even though it is non-zero, but
191 we can free it anyways and let it fail. we don't need to wait
192 for a response from the server */
194 xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
195 w->pixmap = XCB_NONE;
198 //printf("updating pixmap for 0x%x\n", w->id);
200 /* we don't check the result of this call, because it seems that sometimes
201 the X server just doesn't reply. if we check it, we end up hanging
202 sometimes waiting for the reply */
203 w->pixmap = xcb_generate_id(w->sc->dpy->conn);
205 xcb_composite_name_window_pixmap_checked(w->sc->dpy->conn,
207 w->waiting_pixmap = TRUE;
208 //printf("requested pixmap sequence %u\n", w->ck_get_pixmap.sequence);
210 xcb_flush(w->sc->dpy->conn);
214 window_show(d_window_t *pubw)
216 d_window_priv_t *w = (d_window_priv_t*)pubw;
221 //printf("show window 0x%x\n", w->id);
223 /* make sure this is before we update the window's region */
224 if (w->sc->dpy->shape.present)
225 xcb_shape_select_input(w->sc->dpy->conn, w->id, TRUE);
227 mask = TOPLEVEL_WINDOW_EVENT_MASK;
228 xcb_change_window_attributes(w->sc->dpy->conn, w->id,
229 XCB_CW_EVENT_MASK, &mask);
231 assert(w->zombieref == 0);
233 window_update_pixmap(w);
234 window_update_region(w);
235 window_update_user_opacity(pubw);
236 window_update_type(w);
239 /* hold one reference for ourselves */
240 window_zombie_ref(pubw);
244 window_hide(d_window_t *pubw)
246 d_window_priv_t *w = (d_window_priv_t*)pubw;
251 //printf("hide window 0x%x\n", w->id);
252 if (w->sc->dpy->shape.present)
253 xcb_shape_select_input(w->sc->dpy->conn, w->id, FALSE);
255 xcb_change_window_attributes(w->sc->dpy->conn, w->id,
256 XCB_CW_EVENT_MASK, &mask);
260 /* try to free zombie things */
261 window_zombie_unref(pubw);
265 window_fake_unmapped(d_window_t *pubw)
267 d_window_priv_t *w = (d_window_priv_t*)pubw;
273 window_become_zombie(d_window_t *pubw)
275 d_window_priv_t *w = (d_window_priv_t*)pubw;
277 if (w->zombie) return;
283 window_zombie_dead(d_window_t *pubw)
285 d_window_priv_t *w = (d_window_priv_t*)pubw;
287 if (!w->zombie) return;
293 window_is_zombie(d_window_t *pubw)
295 d_window_priv_t *w = (d_window_priv_t*)pubw;
300 window_is_input_only(d_window_t *pubw)
302 d_window_priv_t *w = (d_window_priv_t*)pubw;
304 window_get_attributes_reply(w);
305 return w->input_only;
309 window_get_area(d_window_t *pubw, int *x, int *y, int *width, int *height,
312 d_window_priv_t *w = (d_window_priv_t*)pubw;
314 window_get_geometry_reply(w);
319 *border_width = w->bw;
323 window_get_attributes_reply(d_window_priv_t *w)
325 xcb_get_window_attributes_reply_t *rep;
326 xcb_generic_error_t *err = NULL;
328 rep = xcb_get_window_attributes_reply(w->sc->dpy->conn,
333 w->input_only = rep->_class == XCB_WINDOW_CLASS_INPUT_ONLY;
334 w->attr_mapped = rep->map_state != XCB_MAP_STATE_UNMAPPED;
335 w->visual = rep->visual;
336 //printf("0x%x attributes mapped %d\n", w->id, w->mapped);
340 w->input_only = TRUE;
341 w->attr_mapped = FALSE;
342 w->visual = XCB_NONE;
345 printf("error getting attributes for window 0x%x\n", w->id);
348 w->waiting_attr = FALSE;
352 window_get_geometry_reply(d_window_priv_t *w)
354 xcb_get_geometry_reply_t *rep;
355 xcb_generic_error_t *err = NULL;
357 rep = xcb_get_geometry_reply(w->sc->dpy->conn,
366 w->bw = rep->border_width;
367 w->depth = rep->depth;
377 printf("error getting geometry for window 0x%x\n", w->id);
380 w->waiting_geom = FALSE;
384 window_is_mapped(d_window_t *pubw)
386 d_window_priv_t *w = (d_window_priv_t*)pubw;
391 window_is_attr_mapped(d_window_t *pubw)
393 d_window_priv_t *w = (d_window_priv_t*)pubw;
395 window_get_attributes_reply(w);
396 return w->attr_mapped;
400 window_is_argb(d_window_t *pubw)
402 uint8_t depth = window_get_depth(pubw);
407 window_get_depth(d_window_t *pubw)
409 d_window_priv_t *w = (d_window_priv_t*)pubw;
411 window_get_geometry_reply(w);
416 window_get_visual(d_window_t *pubw)
418 d_window_priv_t *w = (d_window_priv_t*)pubw;
420 window_get_attributes_reply(w);
425 window_get_region(d_window_t *pubw)
427 d_window_priv_t *w = (d_window_priv_t*)pubw;
429 if (w->waiting_region) {
430 xcb_generic_error_t *err = xcb_request_check(w->sc->dpy->conn,
433 display_error(w->sc->dpy, err);
434 w->region = XCB_NONE;
437 w->waiting_region = FALSE;
444 window_configure(d_window_t *pubw, int x, int y, int width, int height,
447 d_window_priv_t *w = (d_window_priv_t*)pubw;
449 /* this overrides any reply from our get_geometry call */
451 w->waiting_geom = FALSE;
456 w->bw = border_width;
460 window_move(d_window_t *pubw)
462 //d_window_priv_t *w = (d_window_priv_t*)pubw;
463 window_update_region((d_window_priv_t*)pubw);
467 window_resize(d_window_t *w)
469 window_update_pixmap((d_window_priv_t*)w);
470 window_update_region((d_window_priv_t*)w);
474 window_reshape(d_window_t *w)
476 window_update_region((d_window_priv_t*)w);
479 void window_opacity_change(d_window_t *w)
481 screen_refresh(w->sc);
485 window_add_plugin_data(d_window_t *pubw, int id, void *data)
487 d_window_priv_t *w = (d_window_priv_t*)pubw;
488 plugin_data_add(w->plugin_data, id, data);
492 window_find_plugin_data(d_window_t *pubw, int id)
494 d_window_priv_t *w = (d_window_priv_t*)pubw;
495 return plugin_data_find(w->plugin_data, id);
499 window_remove_plugin_data(d_window_t *pubw, int id)
501 d_window_priv_t *w = (d_window_priv_t*)pubw;
502 plugin_data_remove(w->plugin_data, id);
506 window_create_damage(d_window_t *pubw)
508 d_window_priv_t *w = (d_window_priv_t*)pubw;
510 if (!window_is_input_only(pubw)) {
511 assert(w->damage == XCB_NONE);
512 w->damage = xcb_generate_id(w->sc->dpy->conn);
513 //printf("creating damage 0x%x\n", w->damage);
514 xcb_damage_create(w->sc->dpy->conn, w->damage, w->id,
515 XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
520 window_destroy_damage(d_window_t *pubw)
522 d_window_priv_t *w = (d_window_priv_t*)pubw;
525 //printf("destroying damage 0x%x\n", w->damage);
526 xcb_damage_destroy(w->sc->dpy->conn, w->damage);
527 w->damage = XCB_NONE;
532 type_request(d_display_t *dpy, d_window_priv_t *w, xcb_window_t *id, int nid)
534 xcb_get_property_cookie_t *cks;
537 cks = malloc(sizeof(xcb_get_property_cookie_t) * nid * 2);
538 for (i = 0; i < nid; ++i) {
540 xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
541 dpy->a.net_wm_window_type,
544 xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
545 dpy->a.wm_transient_for,
546 dpy->a.window, 0, 1);
549 for (i = 0; i < nid; ++i) {
550 xcb_get_property_reply_t *rep;
551 d_window_type_t type = DC_WINDOW_TYPE_INVALID;
553 rep = xcb_get_property_reply(dpy->conn, cks[i*2+0], NULL);
555 if (!w->type && rep->type == dpy->a.atom && rep->length >= 1) {
556 xcb_atom_t *a = (xcb_atom_t*)(xcb_get_property_value(rep));
558 for (j = 0; j < rep->length; ++j) {
559 if (a[j] == dpy->a.net_wm_window_type_normal) {
560 type = DC_WINDOW_TYPE_NORMAL; break;
562 if (a[j] == dpy->a.net_wm_window_type_desktop) {
563 type = DC_WINDOW_TYPE_DESKTOP; break;
565 if (a[j] == dpy->a.net_wm_window_type_dock) {
566 type = DC_WINDOW_TYPE_DOCK; break;
568 if (a[j] == dpy->a.net_wm_window_type_dialog) {
569 type = DC_WINDOW_TYPE_DIALOG; break;
571 if (a[j] == dpy->a.net_wm_window_type_toolbar) {
572 type = DC_WINDOW_TYPE_TOOLBAR; break;
574 if (a[j] == dpy->a.net_wm_window_type_menu) {
575 type = DC_WINDOW_TYPE_MENU; break;
577 if (a[j] == dpy->a.net_wm_window_type_utility) {
578 type = DC_WINDOW_TYPE_UTILITY; break;
580 if (a[j] == dpy->a.net_wm_window_type_splash) {
581 type = DC_WINDOW_TYPE_SPLASH; break;
583 if (a[j] == dpy->a.net_wm_window_type_dropdown_menu) {
584 type = DC_WINDOW_TYPE_DROPDOWN_MENU; break;
586 if (a[j] == dpy->a.net_wm_window_type_popup_menu) {
587 type = DC_WINDOW_TYPE_POPUP_MENU; break;
589 if (a[j] == dpy->a.net_wm_window_type_tooltip) {
590 type = DC_WINDOW_TYPE_TOOLTIP; break;
592 if (a[j] == dpy->a.net_wm_window_type_notification) {
593 type = DC_WINDOW_TYPE_NOTIFICATION; break;
595 if (a[j] == dpy->a.net_wm_window_type_combo) {
596 type = DC_WINDOW_TYPE_COMBO; break;
598 if (a[j] == dpy->a.net_wm_window_type_dnd) {
599 type = DC_WINDOW_TYPE_DND; break;
605 rep = xcb_get_property_reply(dpy->conn, cks[i*2+1], NULL);
607 if (!w->type && rep->type == dpy->a.window && rep->length == 1)
608 type = DC_WINDOW_TYPE_DIALOG;
612 /* also save the window id that we got the type/transient hint from */
613 if (!w->type && type) {
620 for (i = 0; i < nid && !w->type; ++i) {
621 xcb_query_tree_cookie_t ck;
622 xcb_query_tree_reply_t *rep;
624 ck = xcb_query_tree(dpy->conn, id[i]);
625 rep = xcb_query_tree_reply(dpy->conn, ck, NULL);
627 int num = xcb_query_tree_children_length(rep);
628 xcb_window_t *ch = xcb_query_tree_children(rep);
630 type_request(dpy, w, ch, num);
637 window_update_type(d_window_priv_t *w)
639 w->type = DC_WINDOW_TYPE_INVALID;
640 type_request(w->sc->dpy, w, &w->id, 1);
642 w->type = DC_WINDOW_TYPE_NORMAL;
645 //printf("window 0x%x type %d\n", w->id, w->type);
649 window_get_type(d_window_t *pubw)
651 d_window_priv_t *w = (d_window_priv_t*)pubw;
657 window_get_opacity(d_window_t *pubw)
659 d_window_priv_t *w = (d_window_priv_t*)pubw;
660 unsigned long long l;
662 if (w->waiting_opac) {
663 xcb_get_property_reply_t *rep;
665 w->user_opacity = 0xffff;
666 rep = xcb_get_property_reply(w->sc->dpy->conn, w->ck_get_opac, NULL);
668 if (rep->type == w->sc->dpy->a.cardinal && rep->length >= 1) {
669 l = ((uint32_t*)xcb_get_property_value(rep))[0];
670 l = 0xffff * l / 0xffffffff;
675 w->waiting_opac = FALSE;
679 l = l * w->user_opacity / 0xffff;
684 window_set_opacity(d_window_t *pubw, uint16_t o)
686 d_window_priv_t *w = (d_window_priv_t*)pubw;
689 if (w->mapped || w->zombie)
690 w->sc->window_opacity_change(pubw);
691 //printf("mapped %d opacity 0x%x\n", w->mapped, w->opacity);
695 window_zombie_ref(d_window_t *pubw)
697 d_window_priv_t *w = (d_window_priv_t*)pubw;
703 window_zombie_unref(d_window_t *pubw)
705 d_window_priv_t *w = (d_window_priv_t*)pubw;
707 if (--w->zombieref == 0) {
708 w->sc->window_zombie_dead(pubw);
713 xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
714 w->region = XCB_NONE;
718 /* this may cause an error if the pixmap was never valid, but
720 xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
721 w->pixmap = XCB_NONE;
724 /* reset the opacity */
730 window_damage(d_window_t *w)
736 window_restack(d_window_t *w, d_window_t *above)
738 screen_stacking_move_above(w->sc, w, above);