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