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