initialize gl on the overlay window
[dana/dcompmgr.git] / screen.c
1 #include "efence.h"
2
3 #include "screen.h"
4 #include "display.h"
5 #include "plugin.h"
6 #include "list.h"
7 #include "time.h"
8 #include "window.h"
9 #include "gettext.h"
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <xcb/composite.h>
14 #include <xcb/xfixes.h>
15
16 #define ROOT_MASK      (XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
17                         XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
18                         XCB_EVENT_MASK_PROPERTY_CHANGE)
19
20 #define SELECTION_MASK (XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
21                         XCB_EVENT_MASK_PROPERTY_CHANGE)
22
23 static gboolean screen_init(d_screen_t *sc);
24 static xcb_timestamp_t screen_timestamp(d_screen_t *sc);
25 static void screen_add_existing_windows(d_screen_t *sc);
26
27 static guint
28 xcb_window_hash(xcb_window_t *w) { return *w; }
29
30 static gboolean
31 xcb_window_equal(xcb_window_t *w1, xcb_window_t *w2) { return *w1 == *w2; }
32
33
34 d_screen_t*
35 screen_new(struct d_display *dpy, int num, xcb_screen_t *xcb)
36 {
37     d_screen_t *sc;
38
39     sc = malloc(sizeof(d_screen_t));
40     sc->super = *xcb;
41     sc->ref = 1;
42     sc->dpy = dpy;
43     sc->num = num;
44
45     sc->need_repaint = TRUE;
46
47     sc->winhash = g_hash_table_new((GHashFunc)xcb_window_hash,
48                                    (GEqualFunc)xcb_window_equal);
49     sc->stacking = list_new();
50     sc->plugin_data = list_new();
51
52     return sc;
53 }
54
55 void
56 screen_ref(d_screen_t *sc)
57 {
58     ++sc->ref;
59 }
60
61 void
62 screen_unref(d_screen_t *sc)
63 {
64     if (sc && --sc->ref == 0) {
65         d_list_it_t *it, *next;
66
67         /* clean up pending requests */
68         screen_get_root_pixmap(sc);
69
70         g_hash_table_unref(sc->winhash);
71         for (it = list_top(sc->stacking); it; it = next) {
72             next = it->next;
73             window_unref(it->data);
74         }
75         list_unref(sc->stacking);
76         list_unref(sc->plugin_data);
77         free(sc);
78     }
79 }
80
81 gboolean
82 screen_register(d_screen_t *sc)
83 {
84     char *name;
85     xcb_window_t w;
86     xcb_intern_atom_cookie_t ack;
87     xcb_intern_atom_reply_t *arep;
88     xcb_get_selection_owner_cookie_t sck;
89     xcb_get_selection_owner_reply_t *srep;
90     uint32_t event_mask;
91     gboolean taken, ret;
92
93     w = xcb_generate_id(sc->dpy->conn);
94     event_mask = SELECTION_MASK;
95     xcb_create_window(sc->dpy->conn, XCB_COPY_FROM_PARENT, w, sc->super.root,
96                       0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
97                       sc->super.root_visual, XCB_CW_EVENT_MASK, &event_mask);
98
99     name = g_strdup_printf("_NET_WM_CM_S%d", sc->num);
100     ack = xcb_intern_atom(sc->dpy->conn, FALSE, strlen(name), name);
101     arep = xcb_intern_atom_reply(sc->dpy->conn, ack, NULL);
102     g_free(name);
103
104     xcb_grab_server(sc->dpy->conn);
105
106     sck = xcb_get_selection_owner(sc->dpy->conn, arep->atom);
107     srep = xcb_get_selection_owner_reply(sc->dpy->conn, sck, NULL);
108     taken = !!srep->owner;
109     free(srep);
110     if (taken) {
111         printf(_("screen %d already has a composite manager, skipping\n"),
112                sc->num);
113         ret = FALSE;
114     }
115     else {
116         xcb_timestamp_t time;
117
118         sc->selwin = w;
119         sc->selatom = arep->atom;
120
121         time = screen_timestamp(sc);
122
123         xcb_set_selection_owner(sc->dpy->conn, w, arep->atom, time);
124         sck = xcb_get_selection_owner(sc->dpy->conn, arep->atom);
125         srep = xcb_get_selection_owner_reply(sc->dpy->conn, sck, NULL);
126         taken = srep->owner == w;
127         free(srep);
128         if (taken && screen_init(sc)) {
129             screen_add_existing_windows(sc);
130             ret = TRUE;
131         }
132         else {
133             xcb_destroy_window(sc->dpy->conn, w);
134             ret = FALSE;
135         }
136     }
137     g_free(arep);
138
139     xcb_ungrab_server(sc->dpy->conn);
140     xcb_flush(sc->dpy->conn);
141
142     return ret;
143 }
144
145 static gboolean
146 screen_init(d_screen_t *sc)
147 {
148     uint32_t mask;
149     xcb_generic_error_t *err;
150     xcb_void_cookie_t redir_ck;
151     xcb_composite_get_overlay_window_cookie_t overlay_ck;
152     xcb_composite_get_overlay_window_reply_t *overlay_rep;
153
154     redir_ck =
155         xcb_composite_redirect_subwindows(sc->dpy->conn, sc->super.root,
156                                           XCB_COMPOSITE_REDIRECT_AUTOMATIC);
157
158 #if 1
159     redir_ck =
160         xcb_composite_redirect_subwindows(sc->dpy->conn, sc->super.root,
161                                           XCB_COMPOSITE_REDIRECT_MANUAL);
162
163     overlay_ck = xcb_composite_get_overlay_window(sc->dpy->conn,
164                                                   sc->super.root);
165
166     /* check that the redirect worked */
167     err = xcb_request_check(sc->dpy->conn, redir_ck);
168     if (err) {
169         printf(_("unable to redirect rendering, another composite manager must be running"));
170         free(err);
171         return FALSE;
172     }
173
174     /* get the overlay window reply */
175     overlay_rep = xcb_composite_get_overlay_window_reply(sc->dpy->conn,
176                                                          overlay_ck,
177                                                          NULL);
178     if (!overlay_rep) {
179         printf(_("unable to get the composite overlay window\n"));
180         return FALSE;
181     }
182     sc->overlay = overlay_rep->overlay_win;
183     free(overlay_rep);
184
185     /* make the overlay window click-through */
186     if (sc->overlay) {
187         xcb_xfixes_region_t region;
188         xcb_get_window_attributes_cookie_t attr_ck;
189         xcb_get_window_attributes_reply_t *attr_rep;
190
191         region = xcb_generate_id(sc->dpy->conn);
192         xcb_xfixes_create_region(sc->dpy->conn, region, 0, NULL);
193         xcb_xfixes_set_window_shape_region(sc->dpy->conn,
194                                            sc->overlay,
195                                            XCB_SHAPE_SK_BOUNDING,
196                                            0, 0, XCB_NONE);
197         xcb_xfixes_set_window_shape_region(sc->dpy->conn,
198                                            sc->overlay,
199                                            XCB_SHAPE_SK_INPUT,
200                                            0, 0, region);
201         xcb_xfixes_destroy_region(sc->dpy->conn, region);
202
203         attr_ck = xcb_get_window_attributes(sc->dpy->conn, sc->overlay);
204         attr_rep = xcb_get_window_attributes_reply(sc->dpy->conn, attr_ck,
205                                                    NULL);
206         if (!attr_rep)
207             return FALSE;
208         sc->overlay_visual = attr_rep->visual;
209         free(attr_rep);
210     }
211     else
212         return FALSE;
213 #endif
214
215     mask = SELECTION_MASK;
216     xcb_change_window_attributes(sc->dpy->conn, sc->selwin,
217                                  XCB_CW_EVENT_MASK, &mask);
218     mask = ROOT_MASK;
219     xcb_change_window_attributes(sc->dpy->conn, sc->super.root,
220                                  XCB_CW_EVENT_MASK, &mask);
221
222     screen_update_root_pixmap(sc);
223
224     return TRUE;
225 }
226
227 void
228 screen_update_root_pixmap(d_screen_t *sc)
229 {
230     if (sc->root_pixmap_waiting) {
231         xcb_get_property_reply_t *rep;
232         rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[0],
233                                      NULL);
234         if (rep) free(rep);
235         rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[1],
236                                      NULL);
237         if (rep) free(rep);
238         rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[2],
239                                      NULL);
240         if (rep) free(rep);
241     }
242     sc->root_pixmap_ck[0] =
243         xcb_get_property_unchecked(sc->dpy->conn, FALSE,
244                                    sc->super.root, sc->dpy->a.xrootpmap_id,
245                                    sc->dpy->a.pixmap, 0, 1);
246     sc->root_pixmap_ck[1] =
247         xcb_get_property_unchecked(sc->dpy->conn, FALSE,
248                                    sc->super.root, sc->dpy->a.esetroot_pmap_id,
249                                    sc->dpy->a.pixmap, 0, 1);
250     sc->root_pixmap_ck[2] =
251         xcb_get_property_unchecked(sc->dpy->conn, FALSE,
252                                    sc->super.root, sc->dpy->a.xsetroot_id,
253                                    sc->dpy->a.pixmap, 0, 1);
254     sc->root_pixmap_waiting = TRUE;
255 }
256
257 d_window_t*
258 screen_add_window(d_screen_t *sc, xcb_window_t wid)
259 {
260     d_window_t *w;
261
262     w = window_new(wid, sc);
263     g_hash_table_insert(sc->winhash, &w->id, w);
264
265     window_create_damage(w);
266
267     //printf("screen added window 0x%x\n", w->id);
268     return w;
269 }
270
271 static void
272 screen_add_existing_windows(d_screen_t *sc)
273 {
274     xcb_query_tree_cookie_t ck;
275     xcb_query_tree_reply_t *rep;
276
277     ck = xcb_query_tree(sc->dpy->conn, sc->super.root);
278     rep = xcb_query_tree_reply(sc->dpy->conn, ck, NULL);
279     if (rep) {
280         xcb_window_iterator_t it;
281
282         it = xcb_query_tree_children_iterator(rep);
283         for (; it.rem; xcb_window_next(&it))
284             screen_add_window(sc, *it.data);
285
286         free(rep);
287     }
288 }
289
290 void
291 screen_remove_window(d_screen_t *sc, struct d_window *w)
292 {
293     //printf("screen removed window 0x%x\n", w->id);
294
295     window_destroy_damage(w);
296     g_hash_table_remove(sc->winhash, &w->id);
297     window_unref(w);
298 }
299
300 d_window_t*
301 screen_find_window(d_screen_t *sc, xcb_window_t id)
302 {
303     return g_hash_table_lookup(sc->winhash, &id);
304 }
305
306 static xcb_timestamp_t
307 screen_timestamp(d_screen_t *sc)
308 {
309     xcb_void_cookie_t ck;
310     xcb_timestamp_t   time;
311
312     ck = xcb_change_property(sc->dpy->conn, XCB_PROP_MODE_REPLACE, sc->selwin,
313                              sc->selatom, sc->selatom, 32, 0, NULL);
314     xcb_flush(sc->dpy->conn);
315     while (1) {
316         xcb_generic_event_t *ev;
317
318         ev = xcb_wait_for_event(sc->dpy->conn);
319         if (!ev) {
320             printf(_("IO error\n"));
321             exit(0);
322         }
323
324         /* expect errors.. */
325         if (!ev->response_type) {
326             display_error(sc->dpy, (xcb_generic_error_t*)ev);
327             free(ev);
328             continue;
329         }
330
331         if (ev->response_type == XCB_PROPERTY_NOTIFY &&
332             ev->full_sequence == ck.sequence)
333         {
334             time = ((xcb_property_notify_event_t*)ev)->time;
335             free(ev);
336             break;
337         }
338     }
339     //printf("created timestamp %lu\n", (unsigned long) time);
340     return time;
341 }
342
343 void
344 screen_stacking_add(d_screen_t *sc, struct d_window *w)
345 {
346     list_prepend(sc->stacking, w);
347 }
348
349 void
350 screen_stacking_remove(d_screen_t *sc, struct d_window *w)
351 {
352     list_remove(sc->stacking, w);
353 }
354
355 void
356 screen_stacking_move_above(d_screen_t *sc, struct d_window *w,
357                            struct d_window *above)
358 {
359     d_list_it_t *wit = list_find(sc->stacking, w);
360     d_list_it_t *ait = list_find(sc->stacking, above);
361     list_move_before(sc->stacking, wit, ait);
362 }
363
364 void screen_stacking_move_to_top(d_screen_t *sc, struct d_window *w)
365 {
366     d_list_it_t *wit = list_find(sc->stacking, w);
367     d_list_it_t *ait = list_top(sc->stacking);
368     list_move_before(sc->stacking, wit, ait);
369 }
370
371 void screen_stacking_move_to_bottom(d_screen_t *sc, struct d_window *w)
372 {
373     d_list_it_t *wit = list_find(sc->stacking, w);
374     list_move_before(sc->stacking, wit, NULL);
375 }
376
377 static void
378 screen_set_need_repaint(d_screen_t *sc)
379 {
380     sc->need_repaint = FALSE;
381 }
382
383 void
384 screen_setup_default_functions(d_screen_t *sc)
385 {
386     sc->screen_paint = screen_set_need_repaint;
387     sc->window_show = window_show;
388     sc->window_hide = window_hide;
389     sc->window_become_zombie = window_become_zombie;
390     sc->window_zombie_dead = window_zombie_dead;
391     sc->window_move = window_move;
392     sc->window_resize = window_resize;
393     sc->window_reshape = window_reshape;
394     sc->window_opacity_change = window_opacity_change;
395     sc->window_damage = window_damage;
396     sc->window_restack = window_restack;
397     sc->screen_root_pixmap_change = screen_update_root_pixmap;
398 }
399
400 void
401 screen_add_plugin_data(d_screen_t *sc, int id, void *data)
402 {
403     plugin_data_add(sc->plugin_data, id, data);
404 }
405
406 void*
407 screen_find_plugin_data(d_screen_t *sc, int id)
408 {
409     return plugin_data_find(sc->plugin_data, id);
410 }
411
412 void
413 screen_remove_plugin_data(d_screen_t *sc, int id)
414 {
415     plugin_data_remove(sc->plugin_data, id);
416 }
417
418 void
419 screen_refresh(d_screen_t *sc)
420 {
421     sc->need_repaint = TRUE;
422     //printf("*** need repaint! ***\n");
423 }
424
425 xcb_pixmap_t
426 screen_get_root_pixmap(d_screen_t *sc)
427 {
428     if (sc->root_pixmap_waiting) {
429         xcb_get_property_reply_t *rep;
430         int i;
431
432         sc->root_pixmap = XCB_NONE;
433         for (i = 2; i >= 0; --i) {
434             rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[i],
435                                          NULL);
436             if (rep) {
437                 if (rep->type == sc->dpy->a.pixmap && rep->length >= 1) {
438                     sc->root_pixmap =
439                         ((xcb_pixmap_t*)xcb_get_property_value(rep))[0];
440                     //printf("got root pixmap 0x%x\n", sc->root_pixmap);
441                 }
442                 free(rep);
443             }
444         }
445     }
446     return sc->root_pixmap;
447 }