5a588d35d6826ea76f28ae39d90abfb8c9d4fab2
[dana/dcompmgr.git] / screen.c
1 #include "screen.h"
2 #include "display.h"
3 #include "list.h"
4 #include "time.h"
5 #include "window.h"
6 #include "gettext.h"
7 #include <string.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include <xcb/composite.h>
11
12 #define ROOT_MASK      (XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
13                         XCB_EVENT_MASK_STRUCTURE_NOTIFY)
14
15 #define SELECTION_MASK (XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
16                         XCB_EVENT_MASK_PROPERTY_CHANGE)
17
18 static gboolean screen_init(d_screen_t *sc);
19 static xcb_timestamp_t screen_timestamp(d_screen_t *sc);
20 static void screen_add_existing_windows(d_screen_t *sc);
21 static void screen_set_next_repaint(d_screen_t *sc);
22
23 static guint
24 xcb_window_hash(xcb_window_t *w) { return *w; }
25
26 static gboolean
27 xcb_window_equal(xcb_window_t *w1, xcb_window_t *w2) { return *w1 == *w2; }
28
29
30 d_screen_t*
31 screen_new(struct d_display *dpy, int num, xcb_screen_t *xcb)
32 {
33     d_screen_t *sc;
34
35     sc = malloc(sizeof(d_screen_t));
36     sc->super = *xcb;
37     sc->ref = 1;
38     sc->dpy = dpy;
39     sc->num = num;
40     sc->root = NULL;
41
42     gettimeofday(&sc->next_repaint, NULL);
43     sc->need_repaint = TRUE;
44
45     sc->winhash = g_hash_table_new((GHashFunc)xcb_window_hash,
46                                    (GEqualFunc)xcb_window_equal);
47     sc->stacking = list_new();
48     sc->plugin_data = g_hash_table_new_full((GHashFunc)g_str_hash,
49                                             (GEqualFunc)g_str_equal,
50                                             g_free, NULL);
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         if (sc->root)
66             screen_remove_window(sc, sc->root);
67
68         g_hash_table_unref(sc->winhash);
69         list_unref(sc->stacking);
70         g_hash_table_unref(sc->plugin_data);
71         free(sc);
72     }
73 }
74
75 gboolean
76 screen_register(d_screen_t *sc)
77 {
78     char *name;
79     xcb_window_t w;
80     xcb_intern_atom_cookie_t ack;
81     xcb_intern_atom_reply_t *arep;
82     xcb_get_selection_owner_cookie_t sck;
83     xcb_get_selection_owner_reply_t *srep;
84     uint32_t event_mask;
85     gboolean taken, ret;
86
87     w = xcb_generate_id(sc->dpy->conn);
88     event_mask = SELECTION_MASK;
89     xcb_create_window(sc->dpy->conn, XCB_COPY_FROM_PARENT, w, sc->super.root,
90                       0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
91                       sc->super.root_visual, XCB_CW_EVENT_MASK, &event_mask);
92
93     name = g_strdup_printf("_NET_WM_CM_S%d", sc->num);
94     ack = xcb_intern_atom(sc->dpy->conn, FALSE, strlen(name), name);
95     arep = xcb_intern_atom_reply(sc->dpy->conn, ack, NULL);
96     g_free(name);
97
98     xcb_grab_server(sc->dpy->conn);
99
100     sck = xcb_get_selection_owner(sc->dpy->conn, arep->atom);
101     srep = xcb_get_selection_owner_reply(sc->dpy->conn, sck, NULL);
102     taken = !!srep->owner;
103     free(srep);
104     if (taken) {
105         printf(_("screen %d already has a composite manager, skipping\n"),
106                sc->num);
107         ret = FALSE;
108     }
109     else {
110         xcb_timestamp_t time;
111
112         sc->selwin = w;
113         sc->selatom = arep->atom;
114
115         time = screen_timestamp(sc);
116
117         xcb_set_selection_owner(sc->dpy->conn, w, arep->atom, time);
118         sck = xcb_get_selection_owner(sc->dpy->conn, arep->atom);
119         srep = xcb_get_selection_owner_reply(sc->dpy->conn, sck, NULL);
120         taken = srep->owner == w;
121         if (taken && screen_init(sc)) {
122             screen_add_existing_windows(sc);
123             ret = TRUE;
124         }
125         else {
126             xcb_destroy_window(sc->dpy->conn, w);
127             ret = FALSE;
128         }
129     }
130
131     xcb_ungrab_server(sc->dpy->conn);
132     xcb_flush(sc->dpy->conn);
133
134     return ret;
135 }
136
137 static gboolean
138 screen_init(d_screen_t *sc)
139 {
140     uint32_t mask;
141 #if DO_COMP
142     xcb_generic_error_t *err;
143     xcb_void_cookie_t redir_ck;
144     xcb_composite_get_overlay_window_cookie_t overlay_ck;
145     xcb_composite_get_overlay_window_reply_t *overlay_rep;
146
147     redir_ck =
148         xcb_composite_redirect_subwindows(sc->dpy->conn, sc->super.root,
149                                           XCB_COMPOSITE_REDIRECT_MANUAL);
150
151     overlay_ck = xcb_composite_get_overlay_window(sc->dpy->conn,
152                                                   sc->super.root);
153
154     /* check that the redirect worked */
155     err = xcb_request_check(sc->dpy->conn, redir_ck);
156     if (err) {
157         printf(_("unable to redirect rendering, another composite manager must be running"));
158         free(err);
159         return FALSE;
160     }
161
162     /* get the overlay window reply */
163     overlay_rep = xcb_composite_get_overlay_window_reply(sc->dpy->conn,
164                                                          overlay_ck,
165                                                          NULL);
166     if (!overlay_rep) {
167         printf(_("unable to get the composite overlay window\n"));
168         return FALSE;
169     }
170     sc->overlay = overlay_rep->overlay_win;
171     free(overlay_rep);
172 #endif
173
174     mask = SELECTION_MASK;
175     xcb_change_window_attributes(sc->dpy->conn, sc->selwin,
176                                  XCB_CW_EVENT_MASK, &mask);
177     mask = ROOT_MASK;
178     xcb_change_window_attributes(sc->dpy->conn, sc->super.root,
179                                  XCB_CW_EVENT_MASK, &mask);
180
181     return TRUE;
182 }
183
184 d_window_t*
185 screen_add_window(d_screen_t *sc, xcb_window_t wid)
186 {
187     d_window_t *w;
188
189     w = window_new(wid, sc);
190     g_hash_table_insert(sc->winhash, &w->id, w);
191
192     printf("screen added window 0x%x\n", w->id);
193     return w;
194 }
195
196 static void
197 screen_add_existing_windows(d_screen_t *sc)
198 {
199     xcb_query_tree_cookie_t ck;
200     xcb_query_tree_reply_t *rep;
201
202     sc->root = screen_add_window(sc, sc->super.root);
203
204     ck = xcb_query_tree(sc->dpy->conn, sc->super.root);
205     rep = xcb_query_tree_reply(sc->dpy->conn, ck, NULL);
206     if (rep) {
207         xcb_window_iterator_t it;
208
209         it = xcb_query_tree_children_iterator(rep);
210         for (; it.rem; xcb_window_next(&it))
211             screen_add_window(sc, *it.data);
212
213         free(rep);
214     }
215 }
216
217 void
218 screen_remove_window(d_screen_t *sc, struct d_window *w)
219 {
220     printf("screen removed window 0x%x\n", w->id);
221
222     g_hash_table_remove(sc->winhash, &w->id);
223     sc->window_become_zombie(w);
224     window_unref(w);
225 }
226
227 d_window_t*
228 screen_find_window(d_screen_t *sc, xcb_window_t id)
229 {
230     return g_hash_table_lookup(sc->winhash, &id);
231 }
232
233 static xcb_timestamp_t
234 screen_timestamp(d_screen_t *sc)
235 {
236     xcb_void_cookie_t ck;
237     xcb_timestamp_t   time;
238
239     ck = xcb_change_property(sc->dpy->conn, XCB_PROP_MODE_REPLACE, sc->selwin,
240                              sc->selatom, sc->selatom, 32, 0, NULL);
241     xcb_flush(sc->dpy->conn);
242     while (1) {
243         xcb_generic_event_t *ev;
244
245         ev = xcb_wait_for_event(sc->dpy->conn);
246         if (!ev) {
247             printf(_("IO error\n"));
248             exit(0);
249         }
250
251         /* expect errors.. */
252         if (!ev->response_type) {
253             display_error(sc->dpy, (xcb_generic_error_t*)ev);
254             free(ev);
255             continue;
256         }
257
258         if (ev->response_type == XCB_PROPERTY_NOTIFY &&
259             ev->full_sequence == ck.sequence)
260         {
261             time = ((xcb_property_notify_event_t*)ev)->time;
262             free(ev);
263             break;
264         }
265     }
266     printf("created timestamp %lu\n", (unsigned long) time);
267     return time;
268 }
269
270 void
271 screen_stacking_add(d_screen_t *sc, struct d_window *w)
272 {
273     list_prepend(sc->stacking, w);
274 }
275
276 void
277 screen_stacking_remove(d_screen_t *sc, struct d_window *w)
278 {
279     list_remove(sc->stacking, w);
280 }
281
282 static void
283 screen_set_next_repaint(d_screen_t *sc)
284 {
285     gettimeofday(&sc->next_repaint, NULL);
286     /* add time for the refresh rate (60 hz) */
287     time_add(&sc->next_repaint, 1000000/60);
288     sc->need_repaint = FALSE;
289 }
290
291 void
292 screen_setup_default_functions(d_screen_t *sc)
293 {
294     sc->screen_paint = screen_set_next_repaint;
295     sc->window_show = window_show;
296     sc->window_hide = window_hide;
297     sc->window_become_zombie = window_become_zombie;
298
299 }
300
301 void
302 screen_add_plugin_data(d_screen_t *sc, const char *key, void *data)
303 {
304     char *skey = g_strdup(key);
305     g_hash_table_insert(sc->plugin_data, skey, data);
306 }
307
308 void*
309 screen_find_plugin_data(d_screen_t *sc, const char *key)
310 {
311     return g_hash_table_lookup(sc->plugin_data, key);
312 }
313
314 void
315 screen_remove_plugin_data(d_screen_t *sc, const char *key)
316 {
317     g_hash_table_remove(sc->plugin_data, key);
318 }
319
320 void screen_refresh(d_screen_t *sc)
321 {
322     sc->need_repaint = TRUE;
323 }