39685a81764d0739e514d7a726bb1c3c00186086
[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     gboolean            argb;
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->argb = rep->depth == 32;
342         free(rep);
343     }
344     else {
345         w->x = w->y = -1;
346         w->w = w->h = 1;
347         w->bw = 0;
348         w->argb = FALSE;
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     d_window_priv_t *w = (d_window_priv_t*)pubw;
377     if (w->waiting_geom)
378         window_get_geometry_reply(w);
379     return w->argb;
380 }
381
382 xcb_visualid_t
383 window_get_visual(d_window_t *pubw)
384 {
385     d_window_priv_t *w = (d_window_priv_t*)pubw;
386     if (w->waiting_attr)
387         window_get_attributes_reply(w);
388     return w->visual;
389 }
390
391 xcb_xfixes_region_t
392 window_get_region(d_window_t *pubw)
393 {
394     d_window_priv_t *w = (d_window_priv_t*)pubw;
395
396     return w->region;
397 }
398
399 void
400 window_configure(d_window_t *pubw, int x, int y, int width, int height,
401                  int border_width)
402 {
403     d_window_priv_t *w = (d_window_priv_t*)pubw;
404
405     /* this overrides any reply from our get_geometry call */
406     if (w->waiting_geom)
407         w->waiting_geom = FALSE;
408     w->x = x;
409     w->y = y;
410     w->w = width;
411     w->h = height;
412     w->bw = border_width;
413 }
414
415 void
416 window_move(d_window_t *pubw)
417 {
418     //d_window_priv_t *w = (d_window_priv_t*)pubw;
419     window_update_region((d_window_priv_t*)pubw);
420 }
421
422 void
423 window_resize(d_window_t *w)
424 {
425     window_update_pixmap((d_window_priv_t*)w);
426     window_update_region((d_window_priv_t*)w);
427 }
428
429 void
430 window_reshape(d_window_t *w)
431 {
432     window_update_region((d_window_priv_t*)w);
433 }
434
435 void window_opacity_change(d_window_t *w)
436 {
437     screen_refresh(w->sc);
438 }
439
440 void
441 window_add_plugin_data(d_window_t *pubw, int id, void *data)
442 {
443     d_window_priv_t *w = (d_window_priv_t*)pubw;
444     plugin_data_add(w->plugin_data, id, data);
445 }
446
447 void*
448 window_find_plugin_data(d_window_t *pubw, int id)
449 {
450     d_window_priv_t *w = (d_window_priv_t*)pubw;
451     return plugin_data_find(w->plugin_data, id);
452 }
453
454 void
455 window_remove_plugin_data(d_window_t *pubw, int id)
456 {
457     d_window_priv_t *w = (d_window_priv_t*)pubw;
458     plugin_data_remove(w->plugin_data, id);
459 }
460
461 void
462 window_create_damage(d_window_t *pubw)
463 {
464     d_window_priv_t *w = (d_window_priv_t*)pubw;
465
466     if (!window_is_input_only(pubw)) {
467         assert(w->damage == XCB_NONE);
468         w->damage = xcb_generate_id(w->sc->dpy->conn);
469         //printf("creating damage 0x%x\n", w->damage);
470         xcb_damage_create(w->sc->dpy->conn, w->damage, w->id,
471                           XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
472     }
473 }
474
475 void
476 window_destroy_damage(d_window_t *pubw)
477 {
478     d_window_priv_t *w = (d_window_priv_t*)pubw;
479
480     if (w->damage) {
481         //printf("destroying damage 0x%x\n", w->damage);
482         xcb_damage_destroy(w->sc->dpy->conn, w->damage);
483         w->damage = XCB_NONE;
484     }
485 }
486
487 static void
488 type_request(d_display_t *dpy, d_window_priv_t *w, xcb_window_t *id, int nid)
489 {
490     xcb_get_property_cookie_t *cks;
491     int i;
492
493     cks = malloc(sizeof(xcb_get_property_cookie_t) * nid * 2);
494     for (i = 0; i < nid; ++i) {
495         cks[i*2+0] =
496             xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
497                                        dpy->a.net_wm_window_type,
498                                        dpy->a.atom, 0, 20);
499         cks[i*2+1] =
500             xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
501                                        dpy->a.wm_transient_for,
502                                        dpy->a.window, 0, 1);
503     }
504
505     for (i = 0; i < nid; ++i) {
506         xcb_get_property_reply_t *rep;
507         d_window_type_t type = DC_WINDOW_TYPE_INVALID;
508
509         rep = xcb_get_property_reply(dpy->conn, cks[i*2+0], NULL);
510         if (rep) {
511             if (!w->type && rep->type == dpy->a.atom && rep->length >= 1) {
512                 xcb_atom_t *a = (xcb_atom_t*)(xcb_get_property_value(rep));
513                 uint32_t j;
514                 for (j = 0; j < rep->length; ++j) {
515                     if (a[j] == dpy->a.net_wm_window_type_normal) {
516                         type = DC_WINDOW_TYPE_NORMAL; break;
517                     }
518                     if (a[j] == dpy->a.net_wm_window_type_desktop) {
519                         type = DC_WINDOW_TYPE_DESKTOP; break;
520                     }
521                     if (a[j] == dpy->a.net_wm_window_type_dock) {
522                         type = DC_WINDOW_TYPE_DOCK; break;
523                     }
524                     if (a[j] == dpy->a.net_wm_window_type_dialog) {
525                         type = DC_WINDOW_TYPE_DIALOG; break;
526                     }
527                     if (a[j] == dpy->a.net_wm_window_type_toolbar) {
528                         type = DC_WINDOW_TYPE_TOOLBAR; break;
529                     }
530                     if (a[j] == dpy->a.net_wm_window_type_menu) {
531                         type = DC_WINDOW_TYPE_MENU; break;
532                     }
533                     if (a[j] == dpy->a.net_wm_window_type_utility) {
534                         type = DC_WINDOW_TYPE_UTILITY; break;
535                     }
536                     if (a[j] == dpy->a.net_wm_window_type_splash) {
537                         type = DC_WINDOW_TYPE_SPLASH; break;
538                     }
539                     if (a[j] == dpy->a.net_wm_window_type_dropdown_menu) {
540                         type = DC_WINDOW_TYPE_DROPDOWN_MENU; break;
541                     }
542                     if (a[j] == dpy->a.net_wm_window_type_popup_menu) {
543                         type = DC_WINDOW_TYPE_POPUP_MENU; break;
544                     }
545                     if (a[j] == dpy->a.net_wm_window_type_tooltip) {
546                         type = DC_WINDOW_TYPE_TOOLTIP; break;
547                     }
548                     if (a[j] == dpy->a.net_wm_window_type_notification) {
549                         type = DC_WINDOW_TYPE_NOTIFICATION; break;
550                     }
551                     if (a[j] == dpy->a.net_wm_window_type_combo) {
552                         type = DC_WINDOW_TYPE_COMBO; break;
553                     }
554                     if (a[j] == dpy->a.net_wm_window_type_dnd) {
555                         type = DC_WINDOW_TYPE_DND; break;
556                     }
557                 }
558             }
559             free(rep);
560         }
561         rep = xcb_get_property_reply(dpy->conn, cks[i*2+1], NULL);
562         if (rep) {
563             if (!w->type && rep->type == dpy->a.window && rep->length == 1)
564                 type = DC_WINDOW_TYPE_DIALOG;
565             free(rep);
566         }
567
568         /* also save the window id that we got the type/transient hint from */
569         if (!w->type && type) {
570             w->type = type;
571             w->client = id[i];
572         }
573     }
574
575
576     for (i = 0; i < nid && !w->type; ++i) {
577         xcb_query_tree_cookie_t ck;
578         xcb_query_tree_reply_t *rep;
579
580         ck = xcb_query_tree(dpy->conn, id[i]);
581         rep = xcb_query_tree_reply(dpy->conn, ck, NULL);
582         if (rep) {
583             int num = xcb_query_tree_children_length(rep);
584             xcb_window_t *ch = xcb_query_tree_children(rep);
585
586             type_request(dpy, w, ch, num);
587             free(rep);
588         }
589     }
590 }
591
592 static void
593 window_update_type(d_window_priv_t *w)
594 {
595     w->type = DC_WINDOW_TYPE_INVALID;
596     type_request(w->sc->dpy, w, &w->id, 1);
597     if (!w->type) {
598         w->type = DC_WINDOW_TYPE_NORMAL;
599         w->client = w->id;
600     }
601     //printf("window 0x%x type %d\n", w->id, w->type);
602 }
603
604 d_window_type_t
605 window_get_type(d_window_t *pubw)
606 {
607     d_window_priv_t *w = (d_window_priv_t*)pubw;
608
609     return w->type;
610 }
611
612 uint16_t
613 window_get_opacity(d_window_t *pubw)
614 {
615     d_window_priv_t *w = (d_window_priv_t*)pubw;
616     unsigned long long l;
617
618     if (w->waiting_opac) {
619         xcb_get_property_reply_t *rep;
620
621         w->user_opacity = 0xffff;
622         rep = xcb_get_property_reply(w->sc->dpy->conn, w->ck_get_opac, NULL);
623         if (rep) {
624             if (rep->type == w->sc->dpy->a.cardinal && rep->length >= 1) {
625                 l = ((uint32_t*)xcb_get_property_value(rep))[0];
626                 l = 0xffff * l / 0xffffffff;
627                 w->user_opacity = l;
628             }
629             free(rep);
630         }
631         w->waiting_opac = FALSE;
632     }
633
634     l = w->opacity;
635     l = l * w->user_opacity / 0xffff;
636     return l;
637 }
638
639 void
640 window_set_opacity(d_window_t *pubw, uint16_t o)
641 {
642     d_window_priv_t *w = (d_window_priv_t*)pubw;
643
644     w->opacity = o;
645     if (w->mapped || w->zombie)
646         w->sc->window_opacity_change(pubw);
647     //printf("mapped %d opacity 0x%x\n", w->mapped, w->opacity);
648 }
649
650 void
651 window_zombie_ref(d_window_t *pubw)
652 {
653     d_window_priv_t *w = (d_window_priv_t*)pubw;
654
655     ++w->zombieref;
656 }
657
658 void
659 window_zombie_unref(d_window_t *pubw)
660 {
661     d_window_priv_t *w = (d_window_priv_t*)pubw;
662
663     if (--w->zombieref == 0) {
664         w->sc->window_zombie_dead(pubw);
665
666         w->zombie = FALSE;
667
668         if (w->region) {
669             xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
670             w->region = XCB_NONE;
671         }
672
673         if (w->pixmap) {
674             /* this may cause an error if the pixmap was never valid, but
675                that's fine */
676             xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
677             w->pixmap = XCB_NONE;
678         }
679
680         /* reset the opacity */
681         w->opacity = 0xffff;
682     }
683 }
684
685 void
686 window_damage(d_window_t *w)
687 {
688     (void)w;
689 }
690
691 void
692 window_restack(d_window_t *w, d_window_t *above)
693 {
694     screen_stacking_move_above(w->sc, w, above);
695 }