add shadows. they even take the same shape as the window. hee!
[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 typedef struct {
15     /* public stuff */
16     xcb_window_t     id;
17     struct d_screen *sc;
18
19     /* private stuff */
20     int              ref;
21
22     /* queried things, don't read them directly from the struct */
23     int                 x, y, w, h, bw;
24     gboolean            attr_mapped;
25     gboolean            input_only;
26     gboolean            argb;
27     xcb_visualid_t      visual;
28     xcb_pixmap_t        pixmap;
29     xcb_xfixes_region_t region;
30
31     double           opacity;
32
33     gboolean         mapped;
34     gboolean         zombie;
35
36     d_list_t        *plugin_data;
37
38     xcb_damage_damage_t damage;
39
40     gboolean waiting_attr;
41     xcb_get_window_attributes_cookie_t ck_get_attr;
42     gboolean waiting_geom;
43     xcb_get_geometry_cookie_t          ck_get_geom;
44 } d_window_priv_t;
45
46 static void window_get_attributes_reply(d_window_priv_t *w);
47 static void window_get_geometry_reply(d_window_priv_t *w);
48 static void window_update_pixmap(d_window_priv_t *w);
49 static void window_update_region(d_window_priv_t *w);
50
51 d_window_t*
52 window_new(xcb_window_t id, struct d_screen *sc)
53 {
54     d_window_priv_t *w;
55
56     w = malloc(sizeof(d_window_priv_t));
57     w->id = id;
58     w->ref = 1;
59     w->sc = sc;
60     w->zombie = FALSE;
61     w->mapped = FALSE;
62     w->pixmap = XCB_NONE;
63     w->damage = XCB_NONE;
64     w->region = XCB_NONE;
65
66     screen_stacking_add(sc, (d_window_t*)w);
67
68     w->ck_get_attr = xcb_get_window_attributes(sc->dpy->conn, id);
69     w->waiting_attr = TRUE;
70
71     w->ck_get_geom = xcb_get_geometry(sc->dpy->conn, id);
72     w->waiting_geom = TRUE;
73
74     w->plugin_data = list_new();
75
76     //printf("new window 0x%x\n", w->id);
77
78     return (d_window_t*)w;
79 }
80
81 void
82 window_ref(d_window_t *pubw)
83 {
84     d_window_priv_t *w = (d_window_priv_t*)pubw;
85
86     ++w->ref;
87 }
88
89 void
90 window_unref(d_window_t *pubw)
91 {
92     d_window_priv_t *w = (d_window_priv_t*)pubw;
93
94     if (w && --w->ref == 0) {
95         screen_stacking_remove(w->sc, (d_window_t*)w);
96
97         if (w->region) {
98             xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
99             w->region = XCB_NONE;
100         }
101
102         if (w->pixmap) {
103             /* this may cause an error if the pixmap was never valid, but
104                that's fine */
105             xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
106             w->pixmap = XCB_NONE;
107         }
108
109         list_unref(w->plugin_data);
110         free(w);
111     }
112 }
113
114 xcb_pixmap_t
115 window_get_pixmap(d_window_t *pubw)
116 {
117     d_window_priv_t *w = (d_window_priv_t*)pubw;
118
119     return w->pixmap;
120 }
121
122 static void
123 window_update_region(d_window_priv_t *w)
124 {
125     int x, y, wi, hei, bw;
126
127     if (window_is_zombie((d_window_t*)w)) return;
128
129     if (w->region) {
130         xcb_xfixes_destroy_region(w->sc->dpy->conn, w->region);
131         w->region = XCB_NONE;
132     }
133
134     w->region = xcb_generate_id(w->sc->dpy->conn);
135     xcb_xfixes_create_region_from_window(w->sc->dpy->conn, w->region,
136                                          w->id, XCB_SHAPE_SK_BOUNDING);
137     window_get_area((d_window_t*)w, &x, &y, &wi, &hei, &bw);
138     xcb_xfixes_translate_region(w->sc->dpy->conn, w->region, x+bw, y+bw);
139 }
140
141 static void
142 window_update_pixmap(d_window_priv_t *w)
143 {
144     if (window_is_zombie((d_window_t*)w)) return;
145
146     /* the pixmap may not be valid even though it is non-zero, but
147        we can free it anyways and let it fail.  we don't need to wait
148        for a response from the server */
149     if (w->pixmap) {
150         xcb_free_pixmap(w->sc->dpy->conn, w->pixmap);
151         w->pixmap = XCB_NONE;
152     }
153
154     //printf("updating pixmap for 0x%x\n", w->id);
155
156     /* we don't check the result of this call, because it seems that sometimes
157        the X server just doesn't reply.  if we check it, we end up hanging
158        sometimes waiting for the reply */
159     w->pixmap = xcb_generate_id(w->sc->dpy->conn);
160     xcb_composite_name_window_pixmap(w->sc->dpy->conn, w->id, w->pixmap);
161     //printf("requested pixmap sequence %u\n", w->ck_get_pixmap.sequence);
162     //fflush(stdout);
163     xcb_flush(w->sc->dpy->conn);
164 }
165
166 void
167 window_show(d_window_t *pubw)
168 {
169     d_window_priv_t *w = (d_window_priv_t*)pubw;
170
171     assert(!w->mapped);
172
173     //printf("show window 0x%x\n", w->id);
174
175     /* make sure this is before we update the window's region */
176     if (w->sc->dpy->shape.present)
177         xcb_shape_select_input(w->sc->dpy->conn, w->id, TRUE);
178
179     window_update_pixmap(w);
180     window_update_region(w);
181     w->mapped = TRUE;
182 }
183
184 void
185 window_hide(d_window_t *pubw)
186 {
187     d_window_priv_t *w = (d_window_priv_t*)pubw;
188
189     assert(w->mapped);
190
191     //printf("hide window 0x%x\n", w->id);
192     if (w->sc->dpy->shape.present)
193         xcb_shape_select_input(w->sc->dpy->conn, w->id, FALSE);
194
195     w->mapped = FALSE;
196 }
197
198 void
199 window_fake_unmapped(d_window_t *pubw)
200 {
201     d_window_priv_t *w = (d_window_priv_t*)pubw;
202
203     w->mapped = FALSE;
204 }
205
206 void
207 window_become_zombie(d_window_t *pubw)
208 {
209     d_window_priv_t *w = (d_window_priv_t*)pubw;
210
211     if (w->zombie) return;
212
213     w->zombie = TRUE;
214 }
215
216 gboolean
217 window_is_zombie(d_window_t *pubw)
218 {
219     d_window_priv_t *w = (d_window_priv_t*)pubw;
220     return w->zombie;
221 }
222
223 gboolean
224 window_is_input_only(d_window_t *pubw)
225 {
226     d_window_priv_t *w = (d_window_priv_t*)pubw;
227     if (w->waiting_attr)
228         window_get_attributes_reply(w);
229     return w->input_only;
230 }
231
232 void
233 window_get_area(d_window_t *pubw, int *x, int *y, int *width, int *height,
234                 int *border_width)
235 {
236     d_window_priv_t *w = (d_window_priv_t*)pubw;
237     if (w->waiting_geom)
238         window_get_geometry_reply(w);
239     *x = w->x;
240     *y = w->y;
241     *width = w->w;
242     *height = w->h;
243     *border_width = w->bw;
244 }
245
246 static void
247 window_get_attributes_reply(d_window_priv_t *w)
248 {
249     xcb_get_window_attributes_reply_t *rep;
250     xcb_generic_error_t *err = NULL;
251
252     rep = xcb_get_window_attributes_reply(w->sc->dpy->conn,
253                                           w->ck_get_attr,
254                                           &err);
255
256     if (rep) {
257         w->input_only = rep->_class == XCB_WINDOW_CLASS_INPUT_ONLY;
258         w->attr_mapped = rep->map_state != XCB_MAP_STATE_UNMAPPED;
259         w->visual = rep->visual;
260         //printf("0x%x attributes mapped %d\n", w->id, w->mapped);
261         free(rep);
262     }
263     else {
264         w->input_only = TRUE;
265         w->attr_mapped = FALSE;
266         w->visual = XCB_NONE;
267     }
268     if (err) {
269         printf("error getting attributes for window 0x%x\n", w->id);
270         free(err);
271     }
272     w->waiting_attr = FALSE;
273 }
274
275 static void
276 window_get_geometry_reply(d_window_priv_t *w)
277 {
278     xcb_get_geometry_reply_t *rep;
279     xcb_generic_error_t *err = NULL;
280
281     rep = xcb_get_geometry_reply(w->sc->dpy->conn,
282                                  w->ck_get_geom,
283                                  &err);
284
285     if (rep) {
286         w->x = rep->x;
287         w->y = rep->y;
288         w->w = rep->width;
289         w->h = rep->height;
290         w->bw = rep->border_width;
291         w->argb = rep->depth == 32;
292         free(rep);
293     }
294     else {
295         w->x = w->y = -1;
296         w->w = w->h = 1;
297         w->bw = 0;
298         w->argb = FALSE;
299     }
300     if (err) {
301         printf("error getting geometry for window 0x%x\n", w->id);
302         free(err);
303     }
304     w->waiting_geom = FALSE;
305 }
306
307 gboolean
308 window_is_mapped(d_window_t *pubw)
309 {
310     d_window_priv_t *w = (d_window_priv_t*)pubw;
311     return w->mapped;
312 }
313
314 gboolean
315 window_is_attr_mapped(d_window_t *pubw)
316 {
317     d_window_priv_t *w = (d_window_priv_t*)pubw;
318     if (w->waiting_attr)
319         window_get_attributes_reply(w);
320     return w->attr_mapped;
321 }
322
323 gboolean
324 window_is_argb(d_window_t *pubw)
325 {
326     d_window_priv_t *w = (d_window_priv_t*)pubw;
327     if (w->waiting_geom)
328         window_get_geometry_reply(w);
329     return w->argb;
330 }
331
332 xcb_visualid_t
333 window_get_visual(d_window_t *pubw)
334 {
335     d_window_priv_t *w = (d_window_priv_t*)pubw;
336     if (w->waiting_attr)
337         window_get_attributes_reply(w);
338     return w->visual;
339 }
340
341 xcb_xfixes_region_t
342 window_get_region(d_window_t *pubw)
343 {
344     d_window_priv_t *w = (d_window_priv_t*)pubw;
345
346     return w->region;
347 }
348
349 void
350 window_configure(d_window_t *pubw, int x, int y, int width, int height,
351                  int border_width)
352 {
353     d_window_priv_t *w = (d_window_priv_t*)pubw;
354
355     /* this overrides any reply from our get_geometry call */
356     if (w->waiting_geom)
357         w->waiting_geom = FALSE;
358     w->x = x;
359     w->y = y;
360     w->w = width;
361     w->h = height;
362     w->bw = border_width;
363 }
364
365 void
366 window_move(d_window_t *pubw)
367 {
368     //d_window_priv_t *w = (d_window_priv_t*)pubw;
369     window_update_region((d_window_priv_t*)pubw);
370 }
371
372 void
373 window_resize(d_window_t *w)
374 {
375     window_update_pixmap((d_window_priv_t*)w);
376     window_update_region((d_window_priv_t*)w);
377 }
378
379 void
380 window_reshape(d_window_t *w)
381 {
382     window_update_region((d_window_priv_t*)w);
383 }
384
385 void
386 window_add_plugin_data(d_window_t *pubw, int id, void *data)
387 {
388     d_window_priv_t *w = (d_window_priv_t*)pubw;
389     plugin_data_add(w->plugin_data, id, data);
390 }
391
392 void*
393 window_find_plugin_data(d_window_t *pubw, int id)
394 {
395     d_window_priv_t *w = (d_window_priv_t*)pubw;
396     return plugin_data_find(w->plugin_data, id);
397 }
398
399 void
400 window_remove_plugin_data(d_window_t *pubw, int id)
401 {
402     d_window_priv_t *w = (d_window_priv_t*)pubw;
403     plugin_data_remove(w->plugin_data, id);
404 }
405
406 void
407 window_create_damage(d_window_t *pubw)
408 {
409     d_window_priv_t *w = (d_window_priv_t*)pubw;
410
411     if (!window_is_input_only(pubw)) {
412         assert(w->damage == XCB_NONE);
413         w->damage = xcb_generate_id(w->sc->dpy->conn);
414         //printf("creating damage 0x%x\n", w->damage);
415         xcb_damage_create(w->sc->dpy->conn, w->damage, w->id,
416                           XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
417     }
418 }
419
420 void
421 window_destroy_damage(d_window_t *pubw)
422 {
423     d_window_priv_t *w = (d_window_priv_t*)pubw;
424
425     if (w->damage) {
426         //printf("destroying damage 0x%x\n", w->damage);
427         xcb_damage_destroy(w->sc->dpy->conn, w->damage);
428         w->damage = XCB_NONE;
429     }
430 }