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