Enable VSync (Fix bug 5296)
[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_t *it;
285         int count, i;
286
287         it = xcb_query_tree_children(rep);
288         count = xcb_query_tree_children_length(rep);
289         for (i = 0; i < count; i++)
290             screen_add_window(sc, it[i]);
291
292         free(rep);
293     }
294 }
295
296 void
297 screen_remove_window(d_screen_t *sc, struct d_window *w)
298 {
299     //printf("screen removed window 0x%x\n", w->id);
300
301     window_destroy_damage(w);
302     g_hash_table_remove(sc->winhash, &w->id);
303     window_unref(w);
304 }
305
306 d_window_t*
307 screen_find_window(d_screen_t *sc, xcb_window_t id)
308 {
309     return g_hash_table_lookup(sc->winhash, &id);
310 }
311
312 static xcb_timestamp_t
313 screen_timestamp(d_screen_t *sc)
314 {
315     xcb_void_cookie_t ck;
316     xcb_timestamp_t   time;
317
318     ck = xcb_change_property(sc->dpy->conn, XCB_PROP_MODE_REPLACE, sc->selwin,
319                              sc->selatom, sc->selatom, 32, 0, NULL);
320     xcb_flush(sc->dpy->conn);
321     while (1) {
322         xcb_generic_event_t *ev;
323
324         ev = xcb_wait_for_event(sc->dpy->conn);
325         if (!ev) {
326             printf(_("IO error\n"));
327             exit(0);
328         }
329
330         /* expect errors.. */
331         if (!ev->response_type) {
332             display_error(sc->dpy, (xcb_generic_error_t*)ev);
333             free(ev);
334             continue;
335         }
336
337         if (ev->response_type == XCB_PROPERTY_NOTIFY &&
338             ev->full_sequence == ck.sequence)
339         {
340             time = ((xcb_property_notify_event_t*)ev)->time;
341             free(ev);
342             break;
343         }
344     }
345     //printf("created timestamp %lu\n", (unsigned long) time);
346     return time;
347 }
348
349 void
350 screen_stacking_add(d_screen_t *sc, struct d_window *w)
351 {
352     list_prepend(sc->stacking, w);
353 }
354
355 void
356 screen_stacking_remove(d_screen_t *sc, struct d_window *w)
357 {
358     list_remove(sc->stacking, w);
359 }
360
361 void
362 screen_stacking_move_above(d_screen_t *sc, struct d_window *w,
363                            struct d_window *above)
364 {
365     d_list_it_t *wit = list_find(sc->stacking, w);
366     d_list_it_t *ait = list_find(sc->stacking, above);
367     list_move_before(sc->stacking, wit, ait);
368 }
369
370 void screen_stacking_move_to_top(d_screen_t *sc, struct d_window *w)
371 {
372     d_list_it_t *wit = list_find(sc->stacking, w);
373     d_list_it_t *ait = list_top(sc->stacking);
374     list_move_before(sc->stacking, wit, ait);
375 }
376
377 void screen_stacking_move_to_bottom(d_screen_t *sc, struct d_window *w)
378 {
379     d_list_it_t *wit = list_find(sc->stacking, w);
380     list_move_before(sc->stacking, wit, NULL);
381 }
382
383 static void
384 screen_set_need_repaint(d_screen_t *sc)
385 {
386     sc->need_repaint = FALSE;
387 }
388
389 void
390 screen_setup_default_functions(d_screen_t *sc)
391 {
392     sc->screen_paint = screen_set_need_repaint;
393     sc->window_show = window_show;
394     sc->window_hide = window_hide;
395     sc->window_become_zombie = window_become_zombie;
396     sc->window_zombie_dead = window_zombie_dead;
397     sc->window_move = window_move;
398     sc->window_resize = window_resize;
399     sc->window_reshape = window_reshape;
400     sc->window_opacity_change = window_opacity_change;
401     sc->window_damage = window_damage;
402     sc->window_restack = window_restack;
403     sc->screen_root_pixmap_change = screen_update_root_pixmap;
404 }
405
406 void
407 screen_add_plugin_data(d_screen_t *sc, int id, void *data)
408 {
409     plugin_data_add(sc->plugin_data, id, data);
410 }
411
412 void*
413 screen_find_plugin_data(d_screen_t *sc, int id)
414 {
415     return plugin_data_find(sc->plugin_data, id);
416 }
417
418 void
419 screen_remove_plugin_data(d_screen_t *sc, int id)
420 {
421     plugin_data_remove(sc->plugin_data, id);
422 }
423
424 void
425 screen_refresh(d_screen_t *sc)
426 {
427     sc->need_repaint = TRUE;
428     //printf("*** need repaint! ***\n");
429 }
430
431 xcb_pixmap_t
432 screen_get_root_pixmap(d_screen_t *sc)
433 {
434     if (sc->root_pixmap_waiting) {
435         xcb_get_property_reply_t *rep;
436         int i;
437
438         sc->root_pixmap = XCB_NONE;
439         for (i = 2; i >= 0; --i) {
440             rep = xcb_get_property_reply(sc->dpy->conn, sc->root_pixmap_ck[i],
441                                          NULL);
442             if (rep) {
443                 if (rep->type == sc->dpy->a.pixmap && rep->length >= 1) {
444                     sc->root_pixmap =
445                         ((xcb_pixmap_t*)xcb_get_property_value(rep))[0];
446                     //printf("got root pixmap 0x%x\n", sc->root_pixmap);
447                 }
448                 free(rep);
449             }
450         }
451     }
452     return sc->root_pixmap;
453 }