make sure the window's region is correct, and if we fail to update the region cuz...
[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     gboolean waiting_pixmap;
57     xcb_void_cookie_t                  ck_pixmap;
58     gboolean waiting_region;
59     xcb_void_cookie_t                  ck_region;
60 } d_window_priv_t;
61
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);
67
68 d_window_t*
69 window_new(xcb_window_t id, struct d_screen *sc)
70 {
71     d_window_priv_t *w;
72
73     w = malloc(sizeof(d_window_priv_t));
74     w->id = id;
75     w->ref = 1;
76     w->zombieref = 0;
77     w->sc = sc;
78     w->zombie = FALSE;
79     w->mapped = FALSE;
80     w->pixmap = XCB_NONE;
81     w->damage = XCB_NONE;
82     w->region = XCB_NONE;
83     w->type = DC_WINDOW_TYPE_INVALID;
84     w->opacity = 0xffff;
85     w->user_opacity = 0xffff;
86     w->waiting_pixmap = FALSE;
87
88     screen_stacking_add(sc, (d_window_t*)w);
89
90     w->ck_get_attr = xcb_get_window_attributes(sc->dpy->conn, id);
91     w->waiting_attr = TRUE;
92
93     w->ck_get_geom = xcb_get_geometry(sc->dpy->conn, id);
94     w->waiting_geom = TRUE;
95
96     w->plugin_data = list_new();
97
98     //printf("new window 0x%x\n", w->id);
99
100     return (d_window_t*)w;
101 }
102
103 void
104 window_ref(d_window_t *pubw)
105 {
106     d_window_priv_t *w = (d_window_priv_t*)pubw;
107
108     ++w->ref;
109 }
110
111 void
112 window_unref(d_window_t *pubw)
113 {
114     d_window_priv_t *w = (d_window_priv_t*)pubw;
115
116     if (w && --w->ref == 0) {
117         screen_stacking_remove(w->sc, (d_window_t*)w);
118
119         assert(w->zombieref == 0);
120         while (w->zombieref)
121             window_zombie_unref(pubw);
122
123         list_unref(w->plugin_data);
124         free(w);
125     }
126 }
127
128 xcb_pixmap_t
129 window_get_pixmap(d_window_t *pubw)
130 {
131     d_window_priv_t *w = (d_window_priv_t*)pubw;
132
133     if (w->waiting_pixmap) {
134         xcb_generic_error_t *err;
135
136         err = xcb_request_check(w->sc->dpy->conn, w->ck_pixmap);
137         if (err) {
138             w->pixmap = XCB_NONE;
139             display_error(w->sc->dpy, err);
140             free(err);
141         }
142         w->waiting_pixmap = FALSE;
143     }
144
145     return w->pixmap;
146 }
147
148 void
149 window_update_user_opacity(d_window_t *pubw)
150 {
151     d_window_priv_t *w = (d_window_priv_t*)pubw;
152
153     w->ck_get_opac =
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);
159 }
160
161 static void
162 window_update_region(d_window_priv_t *w)
163 {
164     int x, y, wi, hei, bw;
165
166     if (window_is_zombie((d_window_t*)w)) return;
167
168     if (w->region) {
169         xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
170         w->region = XCB_NONE;
171     }
172
173     w->region = xcb_generate_id(w->sc->dpy->conn);
174     w->ck_region =
175         xcb_xfixes_create_region_from_window_checked(w->sc->dpy->conn,
176                                                      w->region,
177                                                      w->id,
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;
183 }
184
185 static void
186 window_update_pixmap(d_window_priv_t *w)
187 {
188     if (window_is_zombie((d_window_t*)w)) return;
189
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 */
193     if (w->pixmap) {
194         xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
195         w->pixmap = XCB_NONE;
196     }
197
198     //printf("updating pixmap for 0x%x\n", w->id);
199
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);
204     w->ck_pixmap =
205         xcb_composite_name_window_pixmap_checked(w->sc->dpy->conn,
206                                                  w->id, w->pixmap);
207     w->waiting_pixmap = TRUE;
208     //printf("requested pixmap sequence %u\n", w->ck_get_pixmap.sequence);
209     //fflush(stdout);
210     xcb_flush(w->sc->dpy->conn);
211 }
212
213 void
214 window_show(d_window_t *pubw)
215 {
216     d_window_priv_t *w = (d_window_priv_t*)pubw;
217     unsigned int mask;
218
219     assert(!w->mapped);
220
221     //printf("show window 0x%x\n", w->id);
222
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);
226
227     mask = TOPLEVEL_WINDOW_EVENT_MASK;
228     xcb_change_window_attributes(w->sc->dpy->conn, w->id,
229                                  XCB_CW_EVENT_MASK, &mask);
230
231     assert(w->zombieref == 0);
232
233     window_update_pixmap(w);
234     window_update_region(w);
235     window_update_user_opacity(pubw);
236     window_update_type(w);
237     w->mapped = TRUE;
238
239     /* hold one reference for ourselves */
240     window_zombie_ref(pubw);
241 }
242
243 void
244 window_hide(d_window_t *pubw)
245 {
246     d_window_priv_t *w = (d_window_priv_t*)pubw;
247     unsigned int mask;
248
249     assert(w->mapped);
250
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);
254     mask = 0;
255     xcb_change_window_attributes(w->sc->dpy->conn, w->id,
256                                  XCB_CW_EVENT_MASK, &mask);
257
258     w->mapped = FALSE;
259
260     /* try to free zombie things */
261     window_zombie_unref(pubw);
262 }
263
264 void
265 window_fake_unmapped(d_window_t *pubw)
266 {
267     d_window_priv_t *w = (d_window_priv_t*)pubw;
268
269     w->mapped = FALSE;
270 }
271
272 void
273 window_become_zombie(d_window_t *pubw)
274 {
275     d_window_priv_t *w = (d_window_priv_t*)pubw;
276
277     if (w->zombie) return;
278
279     w->zombie = TRUE;
280 }
281
282 void
283 window_zombie_dead(d_window_t *pubw)
284 {
285     d_window_priv_t *w = (d_window_priv_t*)pubw;
286
287     if (!w->zombie) return;
288
289     w->zombie = FALSE;
290 }
291
292 gboolean
293 window_is_zombie(d_window_t *pubw)
294 {
295     d_window_priv_t *w = (d_window_priv_t*)pubw;
296     return w->zombie;
297 }
298
299 gboolean
300 window_is_input_only(d_window_t *pubw)
301 {
302     d_window_priv_t *w = (d_window_priv_t*)pubw;
303     if (w->waiting_attr)
304         window_get_attributes_reply(w);
305     return w->input_only;
306 }
307
308 void
309 window_get_area(d_window_t *pubw, int *x, int *y, int *width, int *height,
310                 int *border_width)
311 {
312     d_window_priv_t *w = (d_window_priv_t*)pubw;
313     if (w->waiting_geom)
314         window_get_geometry_reply(w);
315     *x = w->x;
316     *y = w->y;
317     *width = w->w;
318     *height = w->h;
319     *border_width = w->bw;
320 }
321
322 static void
323 window_get_attributes_reply(d_window_priv_t *w)
324 {
325     xcb_get_window_attributes_reply_t *rep;
326     xcb_generic_error_t *err = NULL;
327
328     rep = xcb_get_window_attributes_reply(w->sc->dpy->conn,
329                                           w->ck_get_attr,
330                                           &err);
331
332     if (rep) {
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);
337         free(rep);
338     }
339     else {
340         w->input_only = TRUE;
341         w->attr_mapped = FALSE;
342         w->visual = XCB_NONE;
343     }
344     if (err) {
345         printf("error getting attributes for window 0x%x\n", w->id);
346         free(err);
347     }
348     w->waiting_attr = FALSE;
349 }
350
351 static void
352 window_get_geometry_reply(d_window_priv_t *w)
353 {
354     xcb_get_geometry_reply_t *rep;
355     xcb_generic_error_t *err = NULL;
356
357     rep = xcb_get_geometry_reply(w->sc->dpy->conn,
358                                  w->ck_get_geom,
359                                  &err);
360
361     if (rep) {
362         w->x = rep->x;
363         w->y = rep->y;
364         w->w = rep->width;
365         w->h = rep->height;
366         w->bw = rep->border_width;
367         w->depth = rep->depth;
368         free(rep);
369     }
370     else {
371         w->x = w->y = -1;
372         w->w = w->h = 1;
373         w->bw = 0;
374         w->depth = 0;
375     }
376     if (err) {
377         printf("error getting geometry for window 0x%x\n", w->id);
378         free(err);
379     }
380     w->waiting_geom = FALSE;
381 }
382
383 gboolean
384 window_is_mapped(d_window_t *pubw)
385 {
386     d_window_priv_t *w = (d_window_priv_t*)pubw;
387     return w->mapped;
388 }
389
390 gboolean
391 window_is_attr_mapped(d_window_t *pubw)
392 {
393     d_window_priv_t *w = (d_window_priv_t*)pubw;
394     if (w->waiting_attr)
395         window_get_attributes_reply(w);
396     return w->attr_mapped;
397 }
398
399 gboolean
400 window_is_argb(d_window_t *pubw)
401 {
402     uint8_t depth = window_get_depth(pubw);
403     return depth == 32;
404 }
405
406 uint8_t
407 window_get_depth(d_window_t *pubw)
408 {
409     d_window_priv_t *w = (d_window_priv_t*)pubw;
410     if (w->waiting_geom)
411         window_get_geometry_reply(w);
412     return w->depth;
413 }
414
415 xcb_visualid_t
416 window_get_visual(d_window_t *pubw)
417 {
418     d_window_priv_t *w = (d_window_priv_t*)pubw;
419     if (w->waiting_attr)
420         window_get_attributes_reply(w);
421     return w->visual;
422 }
423
424 xcb_xfixes_region_t
425 window_get_region(d_window_t *pubw)
426 {
427     d_window_priv_t *w = (d_window_priv_t*)pubw;
428
429     if (w->waiting_region) {
430         xcb_generic_error_t *err = xcb_request_check(w->sc->dpy->conn,
431                                                      w->ck_region);
432         if (err) {
433             display_error(w->sc->dpy, err);
434             w->region = XCB_NONE;
435             free(err);
436         }
437         w->waiting_region = FALSE;
438     }
439
440     return w->region;
441 }
442
443 void
444 window_configure(d_window_t *pubw, int x, int y, int width, int height,
445                  int border_width)
446 {
447     d_window_priv_t *w = (d_window_priv_t*)pubw;
448
449     /* this overrides any reply from our get_geometry call */
450     if (w->waiting_geom)
451         w->waiting_geom = FALSE;
452     w->x = x;
453     w->y = y;
454     w->w = width;
455     w->h = height;
456     w->bw = border_width;
457 }
458
459 void
460 window_move(d_window_t *pubw)
461 {
462     //d_window_priv_t *w = (d_window_priv_t*)pubw;
463     window_update_region((d_window_priv_t*)pubw);
464 }
465
466 void
467 window_resize(d_window_t *w)
468 {
469     window_update_pixmap((d_window_priv_t*)w);
470     window_update_region((d_window_priv_t*)w);
471 }
472
473 void
474 window_reshape(d_window_t *w)
475 {
476     window_update_region((d_window_priv_t*)w);
477 }
478
479 void window_opacity_change(d_window_t *w)
480 {
481     screen_refresh(w->sc);
482 }
483
484 void
485 window_add_plugin_data(d_window_t *pubw, int id, void *data)
486 {
487     d_window_priv_t *w = (d_window_priv_t*)pubw;
488     plugin_data_add(w->plugin_data, id, data);
489 }
490
491 void*
492 window_find_plugin_data(d_window_t *pubw, int id)
493 {
494     d_window_priv_t *w = (d_window_priv_t*)pubw;
495     return plugin_data_find(w->plugin_data, id);
496 }
497
498 void
499 window_remove_plugin_data(d_window_t *pubw, int id)
500 {
501     d_window_priv_t *w = (d_window_priv_t*)pubw;
502     plugin_data_remove(w->plugin_data, id);
503 }
504
505 void
506 window_create_damage(d_window_t *pubw)
507 {
508     d_window_priv_t *w = (d_window_priv_t*)pubw;
509
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);
516     }
517 }
518
519 void
520 window_destroy_damage(d_window_t *pubw)
521 {
522     d_window_priv_t *w = (d_window_priv_t*)pubw;
523
524     if (w->damage) {
525         //printf("destroying damage 0x%x\n", w->damage);
526         xcb_damage_destroy(w->sc->dpy->conn, w->damage);
527         w->damage = XCB_NONE;
528     }
529 }
530
531 static void
532 type_request(d_display_t *dpy, d_window_priv_t *w, xcb_window_t *id, int nid)
533 {
534     xcb_get_property_cookie_t *cks;
535     int i;
536
537     cks = malloc(sizeof(xcb_get_property_cookie_t) * nid * 2);
538     for (i = 0; i < nid; ++i) {
539         cks[i*2+0] =
540             xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
541                                        dpy->a.net_wm_window_type,
542                                        dpy->a.atom, 0, 20);
543         cks[i*2+1] =
544             xcb_get_property_unchecked(dpy->conn, FALSE, id[i],
545                                        dpy->a.wm_transient_for,
546                                        dpy->a.window, 0, 1);
547     }
548
549     for (i = 0; i < nid; ++i) {
550         xcb_get_property_reply_t *rep;
551         d_window_type_t type = DC_WINDOW_TYPE_INVALID;
552
553         rep = xcb_get_property_reply(dpy->conn, cks[i*2+0], NULL);
554         if (rep) {
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));
557                 uint32_t j;
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;
561                     }
562                     if (a[j] == dpy->a.net_wm_window_type_desktop) {
563                         type = DC_WINDOW_TYPE_DESKTOP; break;
564                     }
565                     if (a[j] == dpy->a.net_wm_window_type_dock) {
566                         type = DC_WINDOW_TYPE_DOCK; break;
567                     }
568                     if (a[j] == dpy->a.net_wm_window_type_dialog) {
569                         type = DC_WINDOW_TYPE_DIALOG; break;
570                     }
571                     if (a[j] == dpy->a.net_wm_window_type_toolbar) {
572                         type = DC_WINDOW_TYPE_TOOLBAR; break;
573                     }
574                     if (a[j] == dpy->a.net_wm_window_type_menu) {
575                         type = DC_WINDOW_TYPE_MENU; break;
576                     }
577                     if (a[j] == dpy->a.net_wm_window_type_utility) {
578                         type = DC_WINDOW_TYPE_UTILITY; break;
579                     }
580                     if (a[j] == dpy->a.net_wm_window_type_splash) {
581                         type = DC_WINDOW_TYPE_SPLASH; break;
582                     }
583                     if (a[j] == dpy->a.net_wm_window_type_dropdown_menu) {
584                         type = DC_WINDOW_TYPE_DROPDOWN_MENU; break;
585                     }
586                     if (a[j] == dpy->a.net_wm_window_type_popup_menu) {
587                         type = DC_WINDOW_TYPE_POPUP_MENU; break;
588                     }
589                     if (a[j] == dpy->a.net_wm_window_type_tooltip) {
590                         type = DC_WINDOW_TYPE_TOOLTIP; break;
591                     }
592                     if (a[j] == dpy->a.net_wm_window_type_notification) {
593                         type = DC_WINDOW_TYPE_NOTIFICATION; break;
594                     }
595                     if (a[j] == dpy->a.net_wm_window_type_combo) {
596                         type = DC_WINDOW_TYPE_COMBO; break;
597                     }
598                     if (a[j] == dpy->a.net_wm_window_type_dnd) {
599                         type = DC_WINDOW_TYPE_DND; break;
600                     }
601                 }
602             }
603             free(rep);
604         }
605         rep = xcb_get_property_reply(dpy->conn, cks[i*2+1], NULL);
606         if (rep) {
607             if (!w->type && rep->type == dpy->a.window && rep->length == 1)
608                 type = DC_WINDOW_TYPE_DIALOG;
609             free(rep);
610         }
611
612         /* also save the window id that we got the type/transient hint from */
613         if (!w->type && type) {
614             w->type = type;
615             w->client = id[i];
616         }
617     }
618
619
620     for (i = 0; i < nid && !w->type; ++i) {
621         xcb_query_tree_cookie_t ck;
622         xcb_query_tree_reply_t *rep;
623
624         ck = xcb_query_tree(dpy->conn, id[i]);
625         rep = xcb_query_tree_reply(dpy->conn, ck, NULL);
626         if (rep) {
627             int num = xcb_query_tree_children_length(rep);
628             xcb_window_t *ch = xcb_query_tree_children(rep);
629
630             type_request(dpy, w, ch, num);
631             free(rep);
632         }
633     }
634 }
635
636 static void
637 window_update_type(d_window_priv_t *w)
638 {
639     w->type = DC_WINDOW_TYPE_INVALID;
640     type_request(w->sc->dpy, w, &w->id, 1);
641     if (!w->type) {
642         w->type = DC_WINDOW_TYPE_NORMAL;
643         w->client = w->id;
644     }
645     //printf("window 0x%x type %d\n", w->id, w->type);
646 }
647
648 d_window_type_t
649 window_get_type(d_window_t *pubw)
650 {
651     d_window_priv_t *w = (d_window_priv_t*)pubw;
652
653     return w->type;
654 }
655
656 uint16_t
657 window_get_opacity(d_window_t *pubw)
658 {
659     d_window_priv_t *w = (d_window_priv_t*)pubw;
660     unsigned long long l;
661
662     if (w->waiting_opac) {
663         xcb_get_property_reply_t *rep;
664
665         w->user_opacity = 0xffff;
666         rep = xcb_get_property_reply(w->sc->dpy->conn, w->ck_get_opac, NULL);
667         if (rep) {
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;
671                 w->user_opacity = l;
672             }
673             free(rep);
674         }
675         w->waiting_opac = FALSE;
676     }
677
678     l = w->opacity;
679     l = l * w->user_opacity / 0xffff;
680     return l;
681 }
682
683 void
684 window_set_opacity(d_window_t *pubw, uint16_t o)
685 {
686     d_window_priv_t *w = (d_window_priv_t*)pubw;
687
688     w->opacity = o;
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);
692 }
693
694 void
695 window_zombie_ref(d_window_t *pubw)
696 {
697     d_window_priv_t *w = (d_window_priv_t*)pubw;
698
699     ++w->zombieref;
700 }
701
702 void
703 window_zombie_unref(d_window_t *pubw)
704 {
705     d_window_priv_t *w = (d_window_priv_t*)pubw;
706
707     if (--w->zombieref == 0) {
708         w->sc->window_zombie_dead(pubw);
709
710         w->zombie = FALSE;
711
712         if (w->region) {
713             xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
714             w->region = XCB_NONE;
715         }
716
717         if (w->pixmap) {
718             /* this may cause an error if the pixmap was never valid, but
719                that's fine */
720             xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
721             w->pixmap = XCB_NONE;
722         }
723
724         /* reset the opacity */
725         w->opacity = 0xffff;
726     }
727 }
728
729 void
730 window_damage(d_window_t *w)
731 {
732     (void)w;
733 }
734
735 void
736 window_restack(d_window_t *w, d_window_t *above)
737 {
738     screen_stacking_move_above(w->sc, w, above);
739 }