super fading
[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)
270 {
271     d_list_it_t *it;
272     struct timeval now;
273
274     gettimeofday(&now, NULL);
275     
276     for (it = list_top(dpy->screens); it; it = it->next) {
277         d_screen_t *sc = it->data;
278
279         render_timeout(sc, &now);
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     struct timeval now;
289
290     gettimeofday(&now, NULL);
291     
292     for (it = list_top(dpy->screens); it; it = it->next) {
293         d_screen_t *sc = it->data;
294
295         if (time_compare(&sc->next_repaint, &now) <= 0)
296             sc->screen_paint(sc);
297     }
298 }
299
300 static void
301 run(d_display_t *dpy)
302 {
303     while (!quit) {
304         struct timeval next, now, *wait;
305         int            r, npaint, ntime;
306         d_list_it_t   *it;
307         fd_set         fds;
308
309         event(dpy);
310
311         npaint = 0;
312         ntime = 0;
313         for (it = list_top(dpy->screens); it; it = it->next) {
314             d_screen_t *sc = it->data;
315             struct timeval next_timeout;
316
317             if (render_next_timeout(sc, &next_timeout)) {
318                 if ((!npaint && !ntime) ||
319                     time_compare(&next_timeout, &next) < 0)
320                 {
321                     next = next_timeout;
322                     ++ntime;
323                 }
324             }
325             if (fade_next_timeout(sc, &next_timeout)) {
326                 if ((!npaint && !ntime) ||
327                     time_compare(&next_timeout, &next) < 0)
328                 {
329                     next = next_timeout;
330                     ++ntime;
331                 }
332             }
333
334             if (sc->need_repaint &&
335                 ((!npaint && !ntime) ||
336                  time_compare(&sc->next_repaint, &next) < 0))
337             {
338                 next = sc->next_repaint;
339                 ++npaint;
340             }
341         }
342
343         gettimeofday(&now, 0);
344
345         if (!npaint && !ntime)
346             /* wait forever, there is nothing that needs drawing */
347             wait = NULL;
348         else if (time_compare(&next, &now) > 0) {
349             /* wait until the next allowed redraw time */
350             time_difference(&next, &now, &next);
351             wait = &next;
352         }
353         else {
354             /* don't wait cuz a timer is due now already */
355             next.tv_sec = 0;
356             next.tv_usec = 0;
357             wait = &next;
358         }
359
360         FD_ZERO(&fds);
361         FD_SET(dpy->fd, &fds);
362
363         //printf("select %d\n", npaint);
364
365         r = select(dpy->fd+1, &fds, NULL, NULL, wait);
366         if (r == 0) {
367             //printf("select timeout\n");
368             timeouts(dpy);
369             paint(dpy);
370             xcb_flush(dpy->conn);
371         }
372
373         if (xcb_connection_has_error(dpy->conn))
374             quit = TRUE;
375     }
376 }
377
378 static void
379 setup_functions(d_display_t *dpy)
380 {
381     d_list_it_t *it;
382
383     for (it = list_top(dpy->screens); it; it = it->next) {
384         d_screen_t *sc = it->data;
385         int id;
386         screen_setup_default_functions(sc);
387
388         /* these can be plugins.. */
389         id = 1;
390         render_init(sc, id++);
391         fade_init(sc, id++);
392     }
393 }
394
395 static void
396 cleanup_functions(d_display_t *dpy)
397 {
398     d_list_it_t *it;
399
400     for (it = list_top(dpy->screens); it; it = it->next) {
401         d_screen_t *sc = it->data;
402
403         /* these can be plugins.. */
404         fade_free(sc);
405         render_free(sc);
406     }
407 }
408
409 int
410 main(int argc, char **argv)
411 {
412     d_display_t         *dpy;
413     d_options_t          opts;
414
415     read_options(argc, argv, &opts);
416
417     dpy = display_open(NULL);
418     if (!dpy) {
419         printf(_("Unable to connect to display\n"));
420         return 1;
421     }
422
423     if (!dpy->composite.present) {
424         printf(_("no composite extension present on the display\n"));
425         display_unref(dpy);
426         return 1;
427     }
428     if (!dpy->xfixes.present) {
429         printf(_("no xfixes extension present on the display\n"));
430         display_unref(dpy);
431         return 1;
432     }
433     if (!dpy->damage.present) {
434         printf(_("no damage extension present on the display\n"));
435         display_unref(dpy);
436         return 1;
437     }
438     if (!dpy->render.present) {
439         printf(_("no render extension present on the display\n"));
440         display_unref(dpy);
441         return 1;
442     }
443     if (dpy->composite.major_version <= 0 && dpy->composite.minor_version < 3)
444     {
445         printf(_("composite extension does not support the overlay window"));
446         display_unref(dpy);
447         return 1;
448     }
449
450     if (!display_claim_screens(dpy)) {
451         printf(_("found no screens to run on\n"));
452         display_unref(dpy);
453         return 0;
454     }
455
456     signal(SIGINT, signal_quit_handler);
457     signal(SIGHUP, signal_quit_handler);
458     signal(SIGTERM, signal_quit_handler);
459     signal(SIGQUIT, signal_quit_handler);
460
461     setup_functions(dpy);
462
463     {
464         /* some of the windows may already be visible */
465         d_list_it_t *sc_it;
466
467         for (sc_it = list_top(dpy->screens); sc_it; sc_it = sc_it->next) {
468             d_screen_t *sc = sc_it->data;
469             d_list_it_t *it;
470             for (it = list_bottom(sc->stacking); it; it = it->prev)
471                 if (window_is_attr_mapped(it->data))
472                     sc->window_show(it->data);
473         }
474     }
475
476     run(dpy);
477
478     {
479         /* make everything hidden */
480         d_list_it_t *sc_it;
481
482         for (sc_it = list_top(dpy->screens); sc_it; sc_it = sc_it->next) {
483             d_screen_t *sc = sc_it->data;
484             d_list_it_t *it;
485             for (it = list_top(sc->stacking); it; it = it->next) {
486                 if (window_is_mapped(it->data))
487                     sc->window_hide(it->data);
488             }
489         }
490     }
491
492     cleanup_functions(dpy);
493
494     display_unref(dpy);
495     return 0;
496 }