make sure that when a window is shown it stops being a zombie
[dana/dcompmgr.git] / dcompmgr.c
1 #include "efence.h"
2
3 #include "screen.h"
4 #include "window.h"
5 #include "list.h"
6 #include "display.h"
7 #include "gettext.h"
8 #include "time.h"
9
10 /* these can be plugins */
11 #include "render.h"
12 #include "fade.h"
13
14 #include <glib.h>
15 #include <stdio.h>
16 #include <sys/select.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <signal.h>
20 #include <xcb/xcb.h>
21 #include <xcb/damage.h>
22 #include <xcb/shape.h>
23
24 typedef struct {
25     int foo;
26 } d_options_t;
27
28 static gboolean quit = FALSE;
29
30 static void
31 read_options(int argc, char **argv, d_options_t *opts)
32 {
33     opts->foo = argc && argv;
34 }
35
36 static void
37 signal_quit_handler(int sig)
38 {
39     printf("caught signal %d, quitting\n", sig);
40     quit = TRUE;
41 }
42
43 static void
44 event(d_display_t *dpy)
45 {
46     xcb_generic_event_t *ev;
47
48     while ((ev = xcb_poll_for_event(dpy->conn)) && !quit) {
49         //printf("event %d\n", ev->response_type);
50
51         if (!ev->response_type) {
52             display_error(dpy, (xcb_generic_error_t*)ev);
53             free(ev);
54             continue;
55         }
56
57         switch (ev->response_type) {
58         case XCB_CREATE_NOTIFY:
59         {
60             xcb_create_notify_event_t *cev;
61             d_screen_t *sc;
62             d_window_t *w;
63
64             cev = (xcb_create_notify_event_t*)ev;
65             sc = display_screen_from_root(dpy, cev->parent);
66             if (!sc) break;
67             w = screen_add_window(sc, cev->window);
68             break;
69         }
70         case XCB_DESTROY_NOTIFY:
71         {
72             xcb_destroy_notify_event_t *dev;
73             d_screen_t *sc;
74             d_window_t *w;
75             gboolean vis;
76
77             dev = (xcb_destroy_notify_event_t*)ev;
78             sc = display_screen_from_root(dpy, dev->event);
79             if (!sc) break;
80             w = screen_find_window(sc, dev->window);
81             vis = window_is_mapped(w);
82             if (vis) {
83                 sc->window_become_zombie(w);
84                 sc->window_hide(w);
85             }
86             screen_remove_window(sc, w);
87             if (vis) screen_refresh(sc);
88             break;
89         }
90         case XCB_REPARENT_NOTIFY:
91         {
92             xcb_reparent_notify_event_t *rev;
93             d_screen_t *sc;
94             d_window_t *w;
95
96             rev = (xcb_reparent_notify_event_t*)ev;
97             sc = display_screen_from_root(dpy, rev->event);
98             if (!sc) break;
99             w = screen_find_window(sc, rev->window);
100             if (rev->parent == sc->super.root)
101                 screen_add_window(sc, rev->window);
102             else {
103                 if (window_is_mapped(w)) {
104                     sc->window_become_zombie(w);
105                     sc->window_hide(w);
106                 }
107                 screen_remove_window(sc, w);
108             }
109             screen_refresh(sc);
110             break;
111         }
112         case XCB_MAP_NOTIFY:
113         {
114             xcb_map_notify_event_t *mev;
115             d_screen_t *sc;
116             d_window_t *w;
117
118             mev = (xcb_map_notify_event_t*)ev;
119             sc = display_screen_from_root(dpy, mev->event);
120             if (!sc) break;
121             w = screen_find_window(sc, mev->window);
122             sc->window_show(w);
123             screen_refresh(w->sc);
124             break;
125         }
126         case XCB_UNMAP_NOTIFY:
127         {
128             xcb_unmap_notify_event_t *mev;
129             d_screen_t *sc;
130             d_window_t *w;
131
132             mev = (xcb_unmap_notify_event_t*)ev;
133             sc = display_screen_from_root(dpy, mev->event);
134             if (!sc) break;
135             w = screen_find_window(sc, mev->window);
136             sc->window_become_zombie(w);
137             sc->window_hide(w);
138             screen_refresh(w->sc);
139             break;
140         }
141         case XCB_CIRCULATE_NOTIFY:
142         {
143             xcb_circulate_notify_event_t *cev;
144             d_screen_t *sc;
145             d_window_t *w;
146
147             cev = (xcb_circulate_notify_event_t*)ev;
148             sc = display_screen_from_root(dpy, cev->event);
149             if (!sc) break;
150             w = screen_find_window(sc, cev->window);
151             if (cev->place == XCB_PLACE_ON_TOP)
152                 screen_stacking_move_to_top(sc, w);
153             else
154                 screen_stacking_move_to_bottom(sc, w);
155             screen_refresh(w->sc);
156         }
157         case XCB_CONFIGURE_NOTIFY:
158         {
159             xcb_configure_notify_event_t *cev;
160             d_screen_t *sc;
161             d_window_t *w, *above;
162             int x, y, width, height, bwidth;
163
164             cev = (xcb_configure_notify_event_t*)ev;
165             sc = display_screen_from_root(dpy, cev->event);
166             if (!sc) break;
167             //printf("configure 0x%x", cev->window);
168             w = screen_find_window(sc, cev->window);
169             window_get_area(w, &x, &y, &width, &height, &bwidth);
170             if (x != cev->x || y != cev->y || width != cev->width ||
171                 height != cev->height || bwidth != cev->border_width)
172             {
173                 window_configure(w, cev->x, cev->y,
174                                  cev->width, cev->height,
175                                  cev->border_width);
176                 if (window_is_mapped(w)) {
177                     if (x != cev->x || y != cev->y)
178                         sc->window_move(w);
179                     if (width != cev->width ||
180                         height != cev->height || bwidth != cev->border_width)
181                         sc->window_resize(w);
182                 }
183             }
184             above = screen_find_window(sc, cev->above_sibling);
185             screen_stacking_move_above(sc, w, above);
186             screen_refresh(w->sc);
187             break;
188         }
189         case XCB_PROPERTY_NOTIFY:
190         {
191             xcb_property_notify_event_t *pev;
192
193             pev = (xcb_property_notify_event_t*)ev;
194             if (pev->atom == dpy->a.xrootpmap_id ||
195                 pev->atom == dpy->a.esetroot_pmap_id ||
196                 pev->atom == dpy->a.xsetroot_id)
197             {
198                 d_screen_t *sc;
199
200                 sc = display_screen_from_root(dpy, pev->window);
201                 if (sc) sc->screen_root_pixmap_change(sc);
202             }
203             else if (pev->atom == dpy->a.net_wm_window_opacity) {
204                 d_list_it_t *it;
205
206                 for (it = list_top(dpy->screens); it; it = it->next) {
207                     d_screen_t *sc = it->data;
208                     d_window_t *w;
209
210                     w = screen_find_window(sc, pev->window);
211                     if (w) {
212                         window_update_user_opacity(w);
213                         if (window_is_mapped(w)) {
214                             sc->window_opacity_change(w);
215                             screen_refresh(w->sc);
216                         }
217                     }
218                 }
219                     
220             }
221             break;
222         }
223         default:
224             if (ev->response_type - dpy->damage.event == XCB_DAMAGE_NOTIFY) {
225                 xcb_damage_notify_event_t *dev;
226                 d_list_it_t *it;
227
228                 dev = (xcb_damage_notify_event_t*)ev;
229                 for (it = list_top(dpy->screens); it; it = it->next) {
230                     d_screen_t *sc = it->data;
231                     d_window_t *w;
232
233                     w = screen_find_window(sc, dev->drawable);
234                     if (w) {
235                         screen_refresh(w->sc);
236                         break;
237                     }
238                 }
239                 xcb_damage_subtract(dpy->conn, dev->damage,
240                                     XCB_NONE, XCB_NONE);
241             }
242             else if (dpy->shape.present &&
243                      ev->response_type - dpy->shape.event == XCB_SHAPE_NOTIFY)
244             {
245                 xcb_shape_notify_event_t *sev;
246                 d_list_it_t *it;
247
248                 sev = (xcb_shape_notify_event_t*)ev;
249                 for (it = list_top(dpy->screens); it; it = it->next) {
250                     d_screen_t *sc = it->data;
251                     d_window_t *w;
252
253                     w = screen_find_window(sc, sev->affected_window);
254                     if (w) {
255                         sc->window_reshape(w);
256                         screen_refresh(w->sc);
257                         break;
258                     }
259                 }
260             }
261             break;
262         }
263         free(ev);
264         xcb_flush(dpy->conn);
265     }
266 }
267
268 static void
269 timeouts(d_display_t *dpy, const struct timeval *now)
270 {
271     d_list_it_t *it;
272
273     for (it = list_top(dpy->screens); it; it = it->next) {
274         d_screen_t *sc = it->data;
275         struct timeval tv;
276
277         if (render_next_timeout(sc, &tv) && time_compare(&tv, now) <= 0)
278             render_timeout(sc, now);
279         if (fade_next_timeout(sc, &tv) && time_compare(&tv, now) <= 0)
280             fade_timeout(sc, now);
281     }
282 }
283
284 static void
285 paint(d_display_t *dpy)
286 {
287     d_list_it_t *it;
288
289     for (it = list_top(dpy->screens); it; it = it->next) {
290         d_screen_t *sc = it->data;
291         if (sc->need_repaint)
292             sc->screen_paint(sc);
293     }
294 }
295
296 static void
297 run(d_display_t *dpy)
298 {
299     struct timeval now, next_repaint;
300
301     gettimeofday(&now, NULL);
302     next_repaint = now;
303
304     while (!quit) {
305         struct timeval next, *wait;
306         int            r, npaint, ntime;
307         d_list_it_t   *it;
308         fd_set         fds;
309
310         event(dpy);
311
312         npaint = 0;
313         ntime = 0;
314         for (it = list_top(dpy->screens); it; it = it->next) {
315             d_screen_t *sc = it->data;
316             struct timeval next_timeout;
317
318             if (render_next_timeout(sc, &next_timeout)) {
319                 if (!ntime || time_compare(&next_timeout, &next) < 0) {
320                     next = next_timeout;
321                     ++ntime;
322                 }
323             }
324             if (fade_next_timeout(sc, &next_timeout)) {
325                 if (!ntime || time_compare(&next_timeout, &next) < 0) {
326                     next = next_timeout;
327                     ++ntime;
328                 }
329             }
330
331             if (sc->need_repaint)
332                 ++npaint;
333         }
334
335         if (npaint) {
336             if (!ntime || time_compare(&next_repaint, &next) < 0) {
337                 next = next_repaint;
338             }
339         }
340
341         if (!npaint && !ntime)
342             /* wait forever, there is nothing that needs drawing */
343             wait = NULL;
344         else if (time_compare(&next, &now) > 0) {
345             /* wait until the next allowed redraw time */
346             time_difference(&next, &now, &next);
347             wait = &next;
348         }
349         else {
350             /* don't wait cuz a timer is due now already */
351             next.tv_sec = 0;
352             next.tv_usec = 1;
353             wait = &next;
354         }
355
356         FD_ZERO(&fds);
357         FD_SET(dpy->fd, &fds);
358
359         //printf("select %d\n", npaint);
360
361         r = select(dpy->fd+1, &fds, NULL, NULL, wait);
362
363         gettimeofday(&now, NULL);
364         if (r == 0) {
365             //printf("select timeout\n");
366             timeouts(dpy, &now);
367             if (time_compare(&next_repaint, &now) <= 0) {
368                 paint(dpy);
369                 next_repaint = now;
370                 time_add(&next_repaint, 1000000/90); /* 60hz */
371             }
372             xcb_flush(dpy->conn);
373         }
374
375         if (xcb_connection_has_error(dpy->conn))
376             quit = TRUE;
377     }
378 }
379
380 static void
381 setup_functions(d_display_t *dpy)
382 {
383     d_list_it_t *it;
384
385     for (it = list_top(dpy->screens); it; it = it->next) {
386         d_screen_t *sc = it->data;
387         int id;
388         screen_setup_default_functions(sc);
389
390         /* these can be plugins.. */
391         id = 1;
392         render_init(sc, id++);
393         fade_init(sc, id++);
394     }
395 }
396
397 static void
398 cleanup_functions(d_display_t *dpy)
399 {
400     d_list_it_t *it;
401
402     for (it = list_top(dpy->screens); it; it = it->next) {
403         d_screen_t *sc = it->data;
404
405         /* these can be plugins.. */
406         fade_free(sc);
407         render_free(sc);
408     }
409 }
410
411 int
412 main(int argc, char **argv)
413 {
414     d_display_t         *dpy;
415     d_options_t          opts;
416
417     read_options(argc, argv, &opts);
418
419     dpy = display_open(NULL);
420     if (!dpy) {
421         printf(_("Unable to connect to display\n"));
422         return 1;
423     }
424
425     if (!dpy->composite.present) {
426         printf(_("no composite extension present on the display\n"));
427         display_unref(dpy);
428         return 1;
429     }
430     if (!dpy->xfixes.present) {
431         printf(_("no xfixes extension present on the display\n"));
432         display_unref(dpy);
433         return 1;
434     }
435     if (!dpy->damage.present) {
436         printf(_("no damage extension present on the display\n"));
437         display_unref(dpy);
438         return 1;
439     }
440     if (!dpy->render.present) {
441         printf(_("no render extension present on the display\n"));
442         display_unref(dpy);
443         return 1;
444     }
445     if (dpy->composite.major_version <= 0 && dpy->composite.minor_version < 3)
446     {
447         printf(_("composite extension does not support the overlay window"));
448         display_unref(dpy);
449         return 1;
450     }
451
452     if (!display_claim_screens(dpy)) {
453         printf(_("found no screens to run on\n"));
454         display_unref(dpy);
455         return 0;
456     }
457
458     signal(SIGINT, signal_quit_handler);
459     signal(SIGHUP, signal_quit_handler);
460     signal(SIGTERM, signal_quit_handler);
461     signal(SIGQUIT, signal_quit_handler);
462
463     setup_functions(dpy);
464
465     {
466         /* some of the windows may already be visible */
467         d_list_it_t *sc_it;
468
469         for (sc_it = list_top(dpy->screens); sc_it; sc_it = sc_it->next) {
470             d_screen_t *sc = sc_it->data;
471             d_list_it_t *it;
472             for (it = list_bottom(sc->stacking); it; it = it->prev)
473                 if (window_is_attr_mapped(it->data))
474                     sc->window_show(it->data);
475         }
476     }
477
478     run(dpy);
479
480     {
481         /* make everything hidden */
482         d_list_it_t *sc_it;
483
484         for (sc_it = list_top(dpy->screens); sc_it; sc_it = sc_it->next) {
485             d_screen_t *sc = sc_it->data;
486             d_list_it_t *it;
487             for (it = list_top(sc->stacking); it; it = it->next) {
488                 if (window_is_mapped(it->data))
489                     sc->window_hide(it->data);
490             }
491         }
492     }
493
494     cleanup_functions(dpy);
495
496     display_unref(dpy);
497     return 0;
498 }