super fading
[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 static void screen_set_next_repaint(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
46     gettimeofday(&sc->next_repaint, NULL);
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
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 #endif
204
205     mask = SELECTION_MASK;
206     xcb_change_window_attributes(sc->dpy->conn, sc->selwin,
207                                  XCB_CW_EVENT_MASK, &mask);
208     mask = ROOT_MASK;
209     xcb_change_window_attributes(sc->dpy->conn, sc->super.root,
210                                  XCB_CW_EVENT_MASK, &mask);
211
212     screen_update_root_pixmap(sc);
213
214     return TRUE;
215 }
216
217 void
218 screen_update_root_pixmap(d_screen_t *sc)
219 {
220     if (sc->root_pixmap_waiting) {
221         xcb_get_property_reply_t *rep;
222         rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[0],
223                                      NULL);
224         if (rep) free(rep);
225         rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[1],
226                                      NULL);
227         if (rep) free(rep);
228         rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[2],
229                                      NULL);
230         if (rep) free(rep);
231     }
232     sc->root_pixmap_ck[0] =
233         xcb_get_property_unchecked(sc->dpy->conn, FALSE,
234                                    sc->super.root, sc->dpy->a.xrootpmap_id,
235                                    sc->dpy->a.pixmap, 0, 1);
236     sc->root_pixmap_ck[1] =
237         xcb_get_property_unchecked(sc->dpy->conn, FALSE,
238                                    sc->super.root, sc->dpy->a.esetroot_pmap_id,
239                                    sc->dpy->a.pixmap, 0, 1);
240     sc->root_pixmap_ck[2] =
241         xcb_get_property_unchecked(sc->dpy->conn, FALSE,
242                                    sc->super.root, sc->dpy->a.xsetroot_id,
243                                    sc->dpy->a.pixmap, 0, 1);
244     sc->root_pixmap_waiting = TRUE;
245 }
246
247 d_window_t*
248 screen_add_window(d_screen_t *sc, xcb_window_t wid)
249 {
250     d_window_t *w;
251
252     w = window_new(wid, sc);
253     g_hash_table_insert(sc->winhash, &w->id, w);
254
255     window_create_damage(w);
256
257     //printf("screen added window 0x%x\n", w->id);
258     return w;
259 }
260
261 static void
262 screen_add_existing_windows(d_screen_t *sc)
263 {
264     xcb_query_tree_cookie_t ck;
265     xcb_query_tree_reply_t *rep;
266
267     ck = xcb_query_tree(sc->dpy->conn, sc->super.root);
268     rep = xcb_query_tree_reply(sc->dpy->conn, ck, NULL);
269     if (rep) {
270         xcb_window_iterator_t it;
271
272         it = xcb_query_tree_children_iterator(rep);
273         for (; it.rem; xcb_window_next(&it))
274             screen_add_window(sc, *it.data);
275
276         free(rep);
277     }
278 }
279
280 void
281 screen_remove_window(d_screen_t *sc, struct d_window *w)
282 {
283     //printf("screen removed window 0x%x\n", w->id);
284
285     window_destroy_damage(w);
286     g_hash_table_remove(sc->winhash, &w->id);
287     window_unref(w);
288 }
289
290 d_window_t*
291 screen_find_window(d_screen_t *sc, xcb_window_t id)
292 {
293     return g_hash_table_lookup(sc->winhash, &id);
294 }
295
296 static xcb_timestamp_t
297 screen_timestamp(d_screen_t *sc)
298 {
299     xcb_void_cookie_t ck;
300     xcb_timestamp_t   time;
301
302     ck = xcb_change_property(sc->dpy->conn, XCB_PROP_MODE_REPLACE, sc->selwin,
303                              sc->selatom, sc->selatom, 32, 0, NULL);
304     xcb_flush(sc->dpy->conn);
305     while (1) {
306         xcb_generic_event_t *ev;
307
308         ev = xcb_wait_for_event(sc->dpy->conn);
309         if (!ev) {
310             printf(_("IO error\n"));
311             exit(0);
312         }
313
314         /* expect errors.. */
315         if (!ev->response_type) {
316             display_error(sc->dpy, (xcb_generic_error_t*)ev);
317             free(ev);
318             continue;
319         }
320
321         if (ev->response_type == XCB_PROPERTY_NOTIFY &&
322             ev->full_sequence == ck.sequence)
323         {
324             time = ((xcb_property_notify_event_t*)ev)->time;
325             free(ev);
326             break;
327         }
328     }
329     //printf("created timestamp %lu\n", (unsigned long) time);
330     return time;
331 }
332
333 void
334 screen_stacking_add(d_screen_t *sc, struct d_window *w)
335 {
336     list_prepend(sc->stacking, w);
337 }
338
339 void
340 screen_stacking_remove(d_screen_t *sc, struct d_window *w)
341 {
342     list_remove(sc->stacking, w);
343 }
344
345 void
346 screen_stacking_move_above(d_screen_t *sc, struct d_window *w,
347                            struct d_window *above)
348 {
349     d_list_it_t *wit = list_find(sc->stacking, w);
350     d_list_it_t *ait = list_find(sc->stacking, above);
351     list_move_before(sc->stacking, wit, ait);
352 }
353
354 void screen_stacking_move_to_top(d_screen_t *sc, struct d_window *w)
355 {
356     d_list_it_t *wit = list_find(sc->stacking, w);
357     d_list_it_t *ait = list_top(sc->stacking);
358     list_move_before(sc->stacking, wit, ait);
359 }
360
361 void screen_stacking_move_to_bottom(d_screen_t *sc, struct d_window *w)
362 {
363     d_list_it_t *wit = list_find(sc->stacking, w);
364     list_move_before(sc->stacking, wit, NULL);
365 }
366
367 static void
368 screen_set_next_repaint(d_screen_t *sc)
369 {
370     gettimeofday(&sc->next_repaint, NULL);
371     /* add time for the refresh rate (60 hz) */
372     time_add(&sc->next_repaint, 1000000/60);
373     sc->need_repaint = FALSE;
374 }
375
376 void
377 screen_setup_default_functions(d_screen_t *sc)
378 {
379     sc->screen_paint = screen_set_next_repaint;
380     sc->window_show = window_show;
381     sc->window_hide = window_hide;
382     sc->window_become_zombie = window_become_zombie;
383     sc->window_zombie_dead = window_zombie_dead;
384     sc->window_move = window_move;
385     sc->window_resize = window_resize;
386     sc->window_reshape = window_reshape;
387     sc->window_opacity_change = window_opacity_change;
388     sc->screen_root_pixmap_change = screen_update_root_pixmap;
389 }
390
391 void
392 screen_add_plugin_data(d_screen_t *sc, int id, void *data)
393 {
394     plugin_data_add(sc->plugin_data, id, data);
395 }
396
397 void*
398 screen_find_plugin_data(d_screen_t *sc, int id)
399 {
400     return plugin_data_find(sc->plugin_data, id);
401 }
402
403 void
404 screen_remove_plugin_data(d_screen_t *sc, int id)
405 {
406     plugin_data_remove(sc->plugin_data, id);
407 }
408
409 void
410 screen_refresh(d_screen_t *sc)
411 {
412     sc->need_repaint = TRUE;
413     //printf("*** need repaint! ***\n");
414 }
415
416 xcb_pixmap_t
417 screen_get_root_pixmap(d_screen_t *sc)
418 {
419     if (sc->root_pixmap_waiting) {
420         xcb_get_property_reply_t *rep;
421         int i;
422
423         sc->root_pixmap = XCB_NONE;
424         for (i = 2; i >= 0; --i) {
425             rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[i],
426                                          NULL);
427             if (rep) {
428                 if (rep->type == sc->dpy->a.pixmap && rep->length >= 1) {
429                     sc->root_pixmap =
430                         ((xcb_pixmap_t*)xcb_get_property_value(rep))[0];
431                     //printf("got root pixmap 0x%x\n", sc->root_pixmap);
432                 }
433                 free(rep);
434             }
435         }
436     }
437     return sc->root_pixmap;
438 }