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