i've reimplemented glXChooseFBConfig for xcb
[dana/dcompmgr.git] / window.c
1 #include "efence.h"
2
3 #include "window.h"
4 #include "screen.h"
5 #include "plugin.h"
6 #include "list.h"
7 #include "display.h"
8 #include <stdlib.h>
9 #include <assert.h>
10 #include <stdio.h>
11 #include <xcb/composite.h>
12 #include <xcb/damage.h>
13
14 #define TOPLEVEL_WINDOW_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE)
15
16 typedef struct {
17     /* public stuff */
18     xcb_window_t     id;
19     struct d_screen *sc;
20
21     /* private stuff */
22     int              ref;
23     int              zombieref;
24
25     /* queried things, don't read them directly from the struct */
26     int                 x, y, w, h, bw;
27     gboolean            attr_mapped;
28     gboolean            input_only;
29     uint8_t             depth;
30     xcb_visualid_t      visual;
31     xcb_pixmap_t        pixmap;
32     xcb_xfixes_region_t region;
33     d_window_type_t     type;
34     /* the window id where we got the window type from.  further property
35        queries should go to this window */
36     xcb_window_t        client;
37
38     /* opacity is from 0xffff to 0, these 2 are combined to give the final
39        result */
40     uint16_t            opacity;      /* this one is set by plugins/settings */
41     uint16_t            user_opacity; /* this one is set by the user */
42
43     gboolean            mapped;
44     gboolean            zombie;
45
46     d_list_t           *plugin_data;
47
48     xcb_damage_damage_t damage;
49
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 } d_window_priv_t;
57
58 static void window_get_attributes_reply(d_window_priv_t *w);
59 static void window_get_geometry_reply(d_window_priv_t *w);
60 static void window_update_pixmap(d_window_priv_t *w);
61 static void window_update_region(d_window_priv_t *w);
62 static void window_update_type(d_window_priv_t *w);
63
64 d_window_t*
65 window_new(xcb_window_t id, struct d_screen *sc)
66 {
67     d_window_priv_t *w;
68
69     w = malloc(sizeof(d_window_priv_t));
70     w->id = id;
71     w->ref = 1;
72     w->zombieref = 0;
73     w->sc = sc;
74     w->zombie = FALSE;
75     w->mapped = FALSE;
76     w->pixmap = XCB_NONE;
77     w->damage = XCB_NONE;
78     w->region = XCB_NONE;
79     w->type = DC_WINDOW_TYPE_INVALID;
80     w->opacity = 0xffff;
81     w->user_opacity = 0xffff;
82
83     screen_stacking_add(sc, (d_window_t*)w);
84
85     w->ck_get_attr = xcb_get_window_attributes(sc->dpy->conn, id);
86     w->waiting_attr = TRUE;
87
88     w->ck_get_geom = xcb_get_geometry(sc->dpy->conn, id);
89     w->waiting_geom = TRUE;
90
91     w->plugin_data = list_new();
92
93     //printf("new window 0x%x\n", w->id);
94
95     return (d_window_t*)w;
96 }
97
98 void
99 window_ref(d_window_t *pubw)
100 {
101     d_window_priv_t *w = (d_window_priv_t*)pubw;
102
103     ++w->ref;
104 }
105
106 void
107 window_unref(d_window_t *pubw)
108 {
109     d_window_priv_t *w = (d_window_priv_t*)pubw;
110
111     if (w && --w->ref == 0) {
112         screen_stacking_remove(w->sc, (d_window_t*)w);
113
114         assert(w->zombieref == 0);
115         while (w->zombieref)
116             window_zombie_unref(pubw);
117
118         list_unref(w->plugin_data);
119         free(w);
120     }
121 }
122
123 xcb_pixmap_t
124 window_get_pixmap(d_window_t *pubw)
125 {
126     d_window_priv_t *w = (d_window_priv_t*)pubw;
127
128     return w->pixmap;
129 }
130
131 void
132 window_update_user_opacity(d_window_t *pubw)
133 {
134     d_window_priv_t *w = (d_window_priv_t*)pubw;
135
136     w->ck_get_opac =
137         xcb_get_property(w->sc->dpy->conn, FALSE, w->id,
138                          w->sc->dpy->a.net_wm_window_opacity,
139                          w->sc->dpy->a.cardinal, 0, 1);
140     w->waiting_opac = TRUE;
141 }
142
143 static void
144 window_update_region(d_window_priv_t *w)
145 {
146     int x, y, wi, hei, bw;
147
148     if (window_is_zombie((d_window_t*)w)) return;
149
150     if (w->region) {
151         xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
152         w->region = XCB_NONE;
153     }
154
155     w->region = xcb_generate_id(w->sc->dpy->conn);
156     xcb_xfixes_create_region_from_window(w->sc->dpy->conn, w->region,
157                                          w->id, XCB_SHAPE_SK_BOUNDING);
158     window_get_area((d_window_t*)w, &x, &y, &wi, &hei, &bw);
159     xcb_xfixes_translate_region(w->sc->dpy->conn, w->region, x+bw, y+bw);
160 }
161
162 static void
163 window_update_pixmap(d_window_priv_t *w)
164 {
165     if (window_is_zombie((d_window_t*)w)) return;
166
167     /* the pixmap may not be valid even though it is non-zero, but
168        we can free it anyways and let it fail.  we don't need to wait
169        for a response from the server */
170     if (w->pixmap) {
171         xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
172         w->pixmap = XCB_NONE;
173     }
174
175     //printf("updating pixmap for 0x%x\n", w->id);
176
177     /* we don't check the result of this call, because it seems that sometimes
178        the X server just doesn't reply.  if we check it, we end up hanging
179        sometimes waiting for the reply */
180     w->pixmap = xcb_generate_id(w->sc->dpy->conn);
181     xcb_composite_name_window_pixmap(w->sc->dpy->conn, w->id, w->pixmap);
182     //printf("requested pixmap sequence %u\n", w->ck_get_pixmap.sequence);
183     //fflush(stdout);
184     xcb_flush(w->sc->dpy->conn);
185 }
186
187 void
188 window_show(d_window_t *pubw)
189 {
190     d_window_priv_t *w = (d_window_priv_t*)pubw;
191     unsigned int mask;
192
193     assert(!w->mapped);
194
195     //printf("show window 0x%x\n", w->id);
196
197     /* make sure this is before we update the window's region */
198     if (w->sc->dpy->shape.present)
199         xcb_shape_select_input(w->sc->dpy->conn, w->id, TRUE);
200
201     mask = TOPLEVEL_WINDOW_EVENT_MASK;
202     xcb_change_window_attributes(w->sc->dpy->conn, w->id,
203                                  XCB_CW_EVENT_MASK, &mask);
204
205     assert(w->zombieref == 0);
206
207     window_update_pixmap(w);
208     window_update_region(w);
209     window_update_user_opacity(pubw);
210     window_update_type(w);
211     w->mapped = TRUE;
212
213     /* hold one reference for ourselves */
214     window_zombie_ref(pubw);
215 }
216
217 void
218 window_hide(d_window_t *pubw)
219 {
220     d_window_priv_t *w = (d_window_priv_t*)pubw;
221     unsigned int mask;
222
223     assert(w->mapped);
224
225     //printf("hide window 0x%x\n", w->id);
226     if (w->sc->dpy->shape.present)
227         xcb_shape_select_input(w->sc->dpy->conn, w->id, FALSE);
228     mask = 0;
229     xcb_change_window_attributes(w->sc->dpy->conn, w->id,
230                                  XCB_CW_EVENT_MASK, &mask);
231
232     w->mapped = FALSE;
233
234     /* try to free zombie things */
235     window_zombie_unref(pubw);
236 }
237
238 void
239 window_fake_unmapped(d_window_t *pubw)
240 {
241     d_window_priv_t *w = (d_window_priv_t*)pubw;
242
243     w->mapped = FALSE;
244 }
245
246 void
247 window_become_zombie(d_window_t *pubw)
248 {
249     d_window_priv_t *w = (d_window_priv_t*)pubw;
250
251     if (w->zombie) return;
252
253     w->zombie = TRUE;
254 }
255
256 void
257 window_zombie_dead(d_window_t *pubw)
258 {
259     d_window_priv_t *w = (d_window_priv_t*)pubw;
260
261     if (!w->zombie) return;
262
263     w->zombie = FALSE;
264 }
265
266 gboolean
267 window_is_zombie(d_window_t *pubw)
268 {
269     d_window_priv_t *w = (d_window_priv_t*)pubw;
270     return w->zombie;
271 }
272
273 gboolean
274 window_is_input_only(d_window_t *pubw)
275 {
276     d_window_priv_t *w = (d_window_priv_t*)pubw;
277     if (w->waiting_attr)
278         window_get_attributes_reply(w);
279     return w->input_only;
280 }
281
282 void
283 window_get_area(d_window_t *pubw, int *x, int *y, int *width, int *height,
284                 int *border_width)
285 {
286     d_window_priv_t *w = (d_window_priv_t*)pubw;
287     if (w->waiting_geom)
288         window_get_geometry_reply(w);
289     *x = w->x;
290     *y = w->y;
291     *width = w->w;
292     *height = w->h;
293     *border_width = w->bw;
294 }
295
296 static void
297 window_get_attributes_reply(d_window_priv_t *w)
298 {
299     xcb_get_window_attributes_reply_t *rep;
300     xcb_generic_error_t *err = NULL;
301
302     rep = xcb_get_window_attributes_reply(w->sc->dpy->conn,
303                                           w->ck_get_attr,
304                                           &err);
305
306     if (rep) {
307         w->input_only = rep->_class == XCB_WINDOW_CLASS_INPUT_ONLY;
308         w->attr_mapped = rep->map_state != XCB_MAP_STATE_UNMAPPED;
309         w->visual = rep->visual;
310         //printf("0x%x attributes mapped %d\n", w->id, w->mapped);
311         free(rep);
312     }
313     else {
314         w->input_only = TRUE;
315         w->attr_mapped = FALSE;
316         w->visual = XCB_NONE;
317     }
318     if (err) {
319         printf("error getting attributes for window 0x%x\n", w->id);
320         free(err);
321     }
322     w->waiting_attr = FALSE;
323 }
324
325 static void
326 window_get_geometry_reply(d_window_priv_t *w)
327 {
328     xcb_get_geometry_reply_t *rep;
329     xcb_generic_error_t *err = NULL;
330
331     rep = xcb_get_geometry_reply(w->sc->dpy->conn,
332                                  w->ck_get_geom,
333                                  &err);
334
335     if (rep) {
336         w->x = rep->x;
337         w->y = rep->y;
338         w->w = rep->width;
339         w->h = rep->height;
340         w->bw = rep->border_width;
341         w->depth = rep->depth;
342         free(rep);
343     }
344     else {
345         w->x = w->y = -1;
346         w->w = w->h = 1;
347         w->bw = 0;
348         w->depth = 0;
349     }
350     if (err) {
351         printf("error getting geometry for window 0x%x\n", w->id);
352         free(err);
353     }
354     w->waiting_geom = FALSE;
355 }
356
357 gboolean
358 window_is_mapped(d_window_t *pubw)
359 {
360     d_window_priv_t *w = (d_window_priv_t*)pubw;
361     return w->mapped;
362 }
363
364 gboolean
365 window_is_attr_mapped(d_window_t *pubw)
366 {
367     d_window_priv_t *w = (d_window_priv_t*)pubw;
368     if (w->waiting_attr)
369         window_get_attributes_reply(w);
370     return w->attr_mapped;
371 }
372
373 gboolean
374 window_is_argb(d_window_t *pubw)
375 {
376     uint8_t depth = window_get_depth(pubw);
377     return depth == 32;
378 }
379
380 uint8_t
381 window_get_depth(d_window_t *pubw)
382 {
383     d_window_priv_t *w = (d_window_priv_t*)pubw;
384     if (w->waiting_geom)
385         window_get_geometry_reply(w);
386     return w->depth;
387 }
388
389 xcb_visualid_t
390 window_get_visual(d_window_t *pubw)
391 {
392     d_window_priv_t *w = (d_window_priv_t*)pubw;
393     if (w->waiting_attr)
394         window_get_attributes_reply(w);
395     return w->visual;
396 }
397
398 xcb_xfixes_region_t
399 window_get_region(d_window_t *pubw)
400 {
401     d_window_priv_t *w = (d_window_priv_t*)pubw;
402
403     return w->region;
404 }
405
406 void
407 window_configure(d_window_t *pubw, int x, int y, int width, int height,
408                  int border_width)
409 {
410     d_window_priv_t *w = (d_window_priv_t*)pubw;
411
412     /* this overrides any reply from our get_geometry call */
413     if (w->waiting_geom)
414         w->waiting_geom = FALSE;
415     w->x = x;
416     w->y = y;
417     w->w = width;
418     w->h = height;
419     w->bw = border_width;
420 }
421
422 void
423 window_move(d_window_t *pubw)
424 {
425     //d_window_priv_t *w = (d_window_priv_t*)pubw;
426     window_update_region((d_window_priv_t*)pubw);
427 }
428
429 void
430 window_resize(d_window_t *w)
431 {
432     window_update_pixmap((d_window_priv_t*)w);
433     window_update_region((d_window_priv_t*)w);
434 }
435
436 void
437 window_reshape(d_window_t *w)
438 {
439     window_update_region((d_window_priv_t*)w);
440 }
441
442 void window_opacity_change(d_window_t *w)
443 {
444     screen_refresh(w->sc);
445 }
446
447 void
448 window_add_plugin_data(d_window_t *pubw, int id, void *data)
449 {
450     d_window_priv_t *w = (d_window_priv_t*)pubw;
451     plugin_data_add(w->plugin_data, id, data);
452 }
453
454 void*
455 window_find_plugin_data(d_window_t *pubw, int id)
456 {
457     d_window_priv_t *w = (d_window_priv_t*)pubw;
458     return plugin_data_find(w->plugin_data, id);
459 }
460
461 void
462 window_remove_plugin_data(d_window_t *pubw, int id)
463 {
464     d_window_priv_t *w = (d_window_priv_t*)pubw;
465     plugin_data_remove(w->plugin_data, id);
466 }
467
468 void
469 window_create_damage(d_window_t *pubw)
470 {
471     d_window_priv_t *w = (d_window_priv_t*)pubw;
472
473     if (!window_is_input_only(pubw)) {
474         assert(w->damage == XCB_NONE);
475         w->damage = xcb_generate_id(w->sc->dpy->conn);
476         //printf("creating damage 0x%x\n", w->damage);
477         xcb_damage_create(w->sc->dpy->conn, w->damage, w->id,
478                           XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
479     }
480 }
481
482 void
483 window_destroy_damage(d_window_t *pubw)
484 {
485     d_window_priv_t *w = (d_window_priv_t*)pubw;
486
487     if (w->damage) {
488         //printf("destroying damage 0x%x\n", w->damage);
489         xcb_damage_destroy(w->sc->dpy->conn, w->damage);
490         w->damage = XCB_NONE;
491     }
492 }
493
494 static void
495 type_request(d_display_t *dpy, d_window_priv_t *w, xcb_window_t *id, int nid)
496 {
497     xcb_get_property_cookie_t *cks;
498     int i;
499
500     cks = malloc(sizeof(xcb_get_property_cookie_t) * nid * 2);
501     for (i = 0; i < nid; ++i) {
502         cks[i*2+0] =
503             xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
504                                        dpy->a.net_wm_window_type,
505                                        dpy->a.atom, 0, 20);
506         cks[i*2+1] =
507             xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
508                                        dpy->a.wm_transient_for,
509                                        dpy->a.window, 0, 1);
510     }
511
512     for (i = 0; i < nid; ++i) {
513         xcb_get_property_reply_t *rep;
514         d_window_type_t type = DC_WINDOW_TYPE_INVALID;
515
516         rep = xcb_get_property_reply(dpy->conn, cks[i*2+0], NULL);
517         if (rep) {
518             if (!w->type && rep->type == dpy->a.atom && rep->length >= 1) {
519                 xcb_atom_t *a = (xcb_atom_t*)(xcb_get_property_value(rep));
520                 uint32_t j;
521                 for (j = 0; j < rep->length; ++j) {
522                     if (a[j] == dpy->a.net_wm_window_type_normal) {
523                         type = DC_WINDOW_TYPE_NORMAL; break;
524                     }
525                     if (a[j] == dpy->a.net_wm_window_type_desktop) {
526                         type = DC_WINDOW_TYPE_DESKTOP; break;
527                     }
528                     if (a[j] == dpy->a.net_wm_window_type_dock) {
529                         type = DC_WINDOW_TYPE_DOCK; break;
530                     }
531                     if (a[j] == dpy->a.net_wm_window_type_dialog) {
532                         type = DC_WINDOW_TYPE_DIALOG; break;
533                     }
534                     if (a[j] == dpy->a.net_wm_window_type_toolbar) {
535                         type = DC_WINDOW_TYPE_TOOLBAR; break;
536                     }
537                     if (a[j] == dpy->a.net_wm_window_type_menu) {
538                         type = DC_WINDOW_TYPE_MENU; break;
539                     }
540                     if (a[j] == dpy->a.net_wm_window_type_utility) {
541                         type = DC_WINDOW_TYPE_UTILITY; break;
542                     }
543                     if (a[j] == dpy->a.net_wm_window_type_splash) {
544                         type = DC_WINDOW_TYPE_SPLASH; break;
545                     }
546                     if (a[j] == dpy->a.net_wm_window_type_dropdown_menu) {
547                         type = DC_WINDOW_TYPE_DROPDOWN_MENU; break;
548                     }
549                     if (a[j] == dpy->a.net_wm_window_type_popup_menu) {
550                         type = DC_WINDOW_TYPE_POPUP_MENU; break;
551                     }
552                     if (a[j] == dpy->a.net_wm_window_type_tooltip) {
553                         type = DC_WINDOW_TYPE_TOOLTIP; break;
554                     }
555                     if (a[j] == dpy->a.net_wm_window_type_notification) {
556                         type = DC_WINDOW_TYPE_NOTIFICATION; break;
557                     }
558                     if (a[j] == dpy->a.net_wm_window_type_combo) {
559                         type = DC_WINDOW_TYPE_COMBO; break;
560                     }
561                     if (a[j] == dpy->a.net_wm_window_type_dnd) {
562                         type = DC_WINDOW_TYPE_DND; break;
563                     }
564                 }
565             }
566             free(rep);
567         }
568         rep = xcb_get_property_reply(dpy->conn, cks[i*2+1], NULL);
569         if (rep) {
570             if (!w->type && rep->type == dpy->a.window && rep->length == 1)
571                 type = DC_WINDOW_TYPE_DIALOG;
572             free(rep);
573         }
574
575         /* also save the window id that we got the type/transient hint from */
576         if (!w->type && type) {
577             w->type = type;
578             w->client = id[i];
579         }
580     }
581
582
583     for (i = 0; i < nid && !w->type; ++i) {
584         xcb_query_tree_cookie_t ck;
585         xcb_query_tree_reply_t *rep;
586
587         ck = xcb_query_tree(dpy->conn, id[i]);
588         rep = xcb_query_tree_reply(dpy->conn, ck, NULL);
589         if (rep) {
590             int num = xcb_query_tree_children_length(rep);
591             xcb_window_t *ch = xcb_query_tree_children(rep);
592
593             type_request(dpy, w, ch, num);
594             free(rep);
595         }
596     }
597 }
598
599 static void
600 window_update_type(d_window_priv_t *w)
601 {
602     w->type = DC_WINDOW_TYPE_INVALID;
603     type_request(w->sc->dpy, w, &w->id, 1);
604     if (!w->type) {
605         w->type = DC_WINDOW_TYPE_NORMAL;
606         w->client = w->id;
607     }
608     //printf("window 0x%x type %d\n", w->id, w->type);
609 }
610
611 d_window_type_t
612 window_get_type(d_window_t *pubw)
613 {
614     d_window_priv_t *w = (d_window_priv_t*)pubw;
615
616     return w->type;
617 }
618
619 uint16_t
620 window_get_opacity(d_window_t *pubw)
621 {
622     d_window_priv_t *w = (d_window_priv_t*)pubw;
623     unsigned long long l;
624
625     if (w->waiting_opac) {
626         xcb_get_property_reply_t *rep;
627
628         w->user_opacity = 0xffff;
629         rep = xcb_get_property_reply(w->sc->dpy->conn, w->ck_get_opac, NULL);
630         if (rep) {
631             if (rep->type == w->sc->dpy->a.cardinal && rep->length >= 1) {
632                 l = ((uint32_t*)xcb_get_property_value(rep))[0];
633                 l = 0xffff * l / 0xffffffff;
634                 w->user_opacity = l;
635             }
636             free(rep);
637         }
638         w->waiting_opac = FALSE;
639     }
640
641     l = w->opacity;
642     l = l * w->user_opacity / 0xffff;
643     return l;
644 }
645
646 void
647 window_set_opacity(d_window_t *pubw, uint16_t o)
648 {
649     d_window_priv_t *w = (d_window_priv_t*)pubw;
650
651     w->opacity = o;
652     if (w->mapped || w->zombie)
653         w->sc->window_opacity_change(pubw);
654     //printf("mapped %d opacity 0x%x\n", w->mapped, w->opacity);
655 }
656
657 void
658 window_zombie_ref(d_window_t *pubw)
659 {
660     d_window_priv_t *w = (d_window_priv_t*)pubw;
661
662     ++w->zombieref;
663 }
664
665 void
666 window_zombie_unref(d_window_t *pubw)
667 {
668     d_window_priv_t *w = (d_window_priv_t*)pubw;
669
670     if (--w->zombieref == 0) {
671         w->sc->window_zombie_dead(pubw);
672
673         w->zombie = FALSE;
674
675         if (w->region) {
676             xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
677             w->region = XCB_NONE;
678         }
679
680         if (w->pixmap) {
681             /* this may cause an error if the pixmap was never valid, but
682                that's fine */
683             xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
684             w->pixmap = XCB_NONE;
685         }
686
687         /* reset the opacity */
688         w->opacity = 0xffff;
689     }
690 }
691
692 void
693 window_damage(d_window_t *w)
694 {
695     (void)w;
696 }
697
698 void
699 window_restack(d_window_t *w, d_window_t *above)
700 {
701     screen_stacking_move_above(w->sc, w, above);
702 }