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