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