]> icculus.org git repositories - dana/openbox.git/blob - loco/loco.c
do all the fancy stuff to find a visual only once at startup
[dana/openbox.git] / loco / loco.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    loco.c for the Openbox window manager
4    Copyright (c) 2008        Derek Foreman
5    Copyright (c) 2008        Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include <stdio.h>
21 #include "obt/mainloop.h"
22 #include "obt/display.h"
23 #include "obt/mainloop.h"
24 #include "obt/prop.h"
25 #include <glib.h>
26 #include <GL/glx.h>
27 #include <GL/glext.h>
28 #include <GL/glxext.h>
29 #include <GL/glxtokens.h>
30 /*
31 <dana> you want CreateNotify, DestroyNotify, MapNotify, UnmapNotify, and
32           ConfigureNotify
33           <dana> and then xdamage or whatever
34
35 */
36
37 #define glError()                                      \
38 {                                                      \
39     /*const GLchar *err_file = strrchr(err_path, '/');*/ \
40     GLenum        gl_error = glGetError();             \
41                                                        \
42     /*++err_file;*/                                        \
43                                                        \
44     for (; (gl_error); gl_error = glGetError())        \
45       fprintf(stderr, "%s: %d caught at line %u\n",    \
46               __FUNCTION__, gl_error, __LINE__);       \
47               /*(const GLchar*)gluErrorString(gl_error)*/ \
48 }
49
50
51 #define MAX_DEPTH 32
52 #define REFRESH_RATE (G_USEC_PER_SEC/60)
53
54 typedef struct {
55     Window id;
56     gint x, y, w, h;
57     gint depth;
58     gboolean input_only;
59     gboolean visible;
60     gint type; /* XXX make this an enum */
61     GLuint texname;
62     Damage damage;
63     GLXPixmap glpixmap;
64     gboolean damaged;
65 } LocoWindow;
66
67 typedef struct _LocoList {
68     struct _LocoList *next;
69     struct _LocoList *prev;
70     LocoWindow *window;
71 } LocoList;
72
73 static Window      loco_root;
74 static Window      loco_overlay;
75 /* Maps X Window ID -> LocoWindow* */
76 static GHashTable *window_map;
77 /* Maps X Window ID -> LocoList* which is in the stacking_top/bottom list */
78 static GHashTable *stacking_map;
79 /* From top to bottom */
80 static LocoList   *stacking_top;
81 static LocoList   *stacking_bottom;
82 static GLXFBConfig glxFBConfig[MAX_DEPTH + 1];
83 static int loco_screen_num;
84 static gboolean full_redraw_required;
85 static gboolean redraw_required;
86
87 typedef void (*BindEXTFunc)(Display *, GLXDrawable, int, const int *);
88 typedef void (*ReleaseEXTFunc)(Display *, GLXDrawable, int);
89
90 BindEXTFunc    BindTexImageEXT;
91 ReleaseEXTFunc ReleaseTexImageEXT;
92
93 static void show_window(LocoWindow *lw);
94 static void remove_window(LocoWindow *lw);
95
96 /*
97 static void print_stacking()
98 {
99     LocoList *it;
100
101     for (it = stacking_top; it; it = it->next) {
102         printf("0x%lx\n", it->window->id);
103     }
104 }
105 */
106
107 static glong timecompare(GTimeVal *a, GTimeVal *b)
108 {
109     glong r;
110     if ((r = a->tv_sec - b->tv_sec)) return r;
111     return a->tv_usec - b->tv_usec;
112 }
113
114 static void timeadd(GTimeVal *t, glong microseconds)
115 {
116     t->tv_usec += microseconds;
117     while (t->tv_usec >= G_USEC_PER_SEC) {
118         t->tv_usec -= G_USEC_PER_SEC;
119         ++t->tv_sec;
120     }
121     while (t->tv_usec < 0) {
122         t->tv_usec += G_USEC_PER_SEC;
123         --t->tv_sec;
124     }
125 }
126
127 gboolean create_glxpixmap(LocoWindow *lw)
128 {
129     Pixmap pixmap;
130     static const int attrs[] =
131         { GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, None };
132
133     if (!glxFBConfig[lw->depth]) {
134         printf("no glxFBConfig for depth %d for window 0x%lx\n",
135                lw->depth, lw->id);
136         return FALSE;
137     }
138
139     pixmap = XCompositeNameWindowPixmap(obt_display, lw->id);
140     lw->glpixmap = glXCreatePixmap(obt_display, glxFBConfig[lw->depth],
141                                    pixmap, attrs);
142     return !!lw->glpixmap;
143 }
144
145 int bindPixmapToTexture(LocoWindow *lw)
146 {
147     if (lw->glpixmap != None)
148         return 1; /* already bound */
149
150     if (!create_glxpixmap(lw))
151         return 0; /* horrible failure */
152
153 /*
154     if (screen->queryDrawable (screen->display->display,
155                                texture->pixmap,
156                                GLX_TEXTURE_TARGET_EXT,
157                                &target))
158     {
159         fprintf (stderr, "%s: glXQueryDrawable failed\n", programName);
160
161         glXDestroyGLXPixmap (screen->display->display, texture->pixmap);
162         texture->pixmap = None;
163
164         return FALSE;
165     }
166 */
167
168     glBindTexture(GL_TEXTURE_2D, lw->texname);
169 glError();
170     BindTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
171
172     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
173     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
174
175     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
176     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
177     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
178     return 1;
179 }
180
181 void releasePixmapFromTexture(LocoWindow *lw)
182 {
183     if (lw->glpixmap) {
184         glBindTexture(GL_TEXTURE_2D, lw->texname);
185
186         ReleaseTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT);
187
188         glBindTexture(GL_TEXTURE_2D, 0);
189     }
190 }
191
192 void destroy_glxpixmap(LocoWindow *lw)
193 {
194     if (lw->glpixmap) {
195                 obt_display_ignore_errors(TRUE);
196         releasePixmapFromTexture(lw);
197         glXDestroyGLXPixmap(obt_display, lw->glpixmap);
198         obt_display_ignore_errors(FALSE);
199
200         lw->glpixmap = None;
201     }
202 }
203
204 static void full_composite(void)
205 {
206     int ret;
207     LocoList *it;
208
209     /* XXX if (full_redraw_required) */
210         glClear(GL_COLOR_BUFFER_BIT);
211
212     for (it = stacking_bottom; it != stacking_top; it = it->prev) {
213         if (it->window->input_only) continue;
214         if (!it->window->visible)   continue;
215         /* XXX if (!full_redraw_required && !it->window->damaged) continue; */
216
217         if (!full_redraw_required) {
218             /* XXX if the window is transparent, then clear the background
219                behind it */
220         }
221
222         glBindTexture(GL_TEXTURE_2D, it->window->texname);
223         ret = bindPixmapToTexture(it->window);
224         if (ret) {
225             glBegin(GL_QUADS);
226             glColor3f(1.0, 1.0, 1.0);
227             glVertex2i(it->window->x, it->window->y);
228             glTexCoord2f(1, 0);
229             glVertex2i(it->window->x + it->window->w, it->window->y);
230             glTexCoord2f(1, 1);
231             glVertex2i(it->window->x + it->window->w,
232                        it->window->y + it->window->h);
233             glTexCoord2f(0, 1);
234             glVertex2i(it->window->x, it->window->y + it->window->h);
235             glTexCoord2f(0, 0);
236             glEnd();
237         }
238
239         it->window->damaged = FALSE;
240     }
241     glXSwapBuffers(obt_display, loco_overlay);
242
243     full_redraw_required = redraw_required = FALSE;
244 }
245
246 static LocoList* loco_list_prepend(LocoList **top, LocoList **bottom,
247                                    LocoWindow *window)
248 {
249     LocoList *n = g_new(LocoList, 1);
250     n->window = window;
251
252     n->prev = NULL;
253     n->next = *top;
254     if (n->next) n->next->prev = n;
255
256     *top = n;
257     if (!*bottom) *bottom = n;
258     return n;
259 }
260
261 static void loco_list_delete_link(LocoList **top, LocoList **bottom,
262                                   LocoList *pos)
263 {
264     LocoList *prev = pos->prev;
265     LocoList *next = pos->next;
266
267     if (next)
268         next->prev = prev;
269     if (prev)
270         prev->next = next;
271     if (!next)
272         *bottom = prev;
273     if (!prev)
274         *top = next;
275
276     g_free(pos);
277 }
278
279 static void loco_list_move_before(LocoList **top, LocoList **bottom,
280                                   LocoList *move, LocoList *before)
281 {
282     LocoList *prev, *next;
283
284     /* these won't move it anywhere */
285     if (move == before || move->next == before) return;
286
287     prev = move->prev;
288     next = move->next;
289
290     /* remove it from the list */
291     if (next) next->prev = prev;
292     else      *bottom = prev;
293     if (prev) prev->next = next;
294     else      *top = next;
295
296     /* reinsert it */
297     if (before) {
298         move->next = before;
299         move->prev = before->prev;
300         move->next->prev = move;
301         if (move->prev) move->prev->next = move;
302     }
303     else {
304         /* after the bottom */
305         move->prev = *bottom;
306         move->next = NULL;
307         if (move->prev) move->prev->next = move;
308         *bottom = move;
309     }
310
311     if (!move->prev) *top = move;
312 }
313
314 /* Returns a LocoWindow structure */
315 static LocoWindow* find_window(Window window)
316 {
317     return g_hash_table_lookup(window_map, &window);
318 }
319
320 /* Returns a node from the stacking_top/bottom list */
321 static LocoList* find_stacking(Window window)
322 {
323     return g_hash_table_lookup(stacking_map, &window);
324 }
325
326 static void add_window(Window window)
327 {
328     LocoWindow *lw;
329     LocoList *it;
330     XWindowAttributes attrib;
331
332     printf("add window 0x%lx\n", window);
333
334     if (!XGetWindowAttributes(obt_display, window, &attrib))
335         return;
336
337     lw = g_new0(LocoWindow, 1);
338     lw->id = window;
339     lw->input_only = attrib.class == InputOnly;
340     lw->x = attrib.x;
341     lw->y = attrib.y;
342     lw->w = attrib.width;
343     lw->h = attrib.height;
344     lw->depth = attrib.depth;
345
346     g_hash_table_insert(window_map, &lw->id, lw);
347     /* new windows are at the top */
348     it = loco_list_prepend(&stacking_top, &stacking_bottom, lw);
349     g_hash_table_insert(stacking_map, &lw->id, it);
350
351     if (!lw->input_only) {
352         glGenTextures(1, &lw->texname);
353         //  glTexImage2D(TARGET, 0, GL_RGB, lw->w, lw->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
354         lw->damage = XDamageCreate(obt_display, window, XDamageReportNonEmpty);
355     }
356
357     //print_stacking();
358
359     if (attrib.map_state != IsUnmapped)
360         show_window(lw);
361 }
362
363 static void remove_window(LocoWindow *lw)
364 {
365     printf("remove window 0x%lx\n", lw->id);
366
367     LocoList *pos = find_stacking(lw->id);
368     g_assert(pos);
369
370     if (!lw->input_only) {
371         glDeleteTextures(1, &lw->texname);
372                 obt_display_ignore_errors(TRUE);
373                 XDamageDestroy(obt_display, lw->damage);
374         obt_display_ignore_errors(FALSE);
375     }
376
377     loco_list_delete_link(&stacking_top, &stacking_bottom, pos);
378     g_hash_table_remove(stacking_map, &lw->id);
379     g_hash_table_remove(window_map, &lw->id);
380
381     g_free(lw);
382
383     //print_stacking();
384 }
385
386 static void show_window(LocoWindow *lw)
387 {
388     guint32 *type;
389     guint i, ntype;
390
391     lw->visible = TRUE;
392
393     /* get the window's semantic type (for different effects!) */
394     lw->type = 0; /* XXX set this to the default type */
395     if (OBT_PROP_GETA32(lw->id, NET_WM_WINDOW_TYPE, ATOM, &type, &ntype)) {
396         /* use the first value that we know about in the array */
397         for (i = 0; i < ntype; ++i) {
398             /* XXX SET THESE TO AN ENUM'S VALUES */
399             if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
400                 lw->type = 1;
401             if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
402                 lw->type = 2;
403             /* XXX there are more TYPES that need to be added to prop.h */
404         }
405         g_free(type);
406     }
407
408     full_redraw_required = redraw_required = TRUE;
409 }
410
411 static void hide_window(LocoWindow *lw, gboolean destroyed)
412 {
413     /* if destroyed, then the window is no longer available */
414     lw->visible = FALSE;
415     destroy_glxpixmap(lw);
416
417     full_redraw_required = redraw_required = TRUE;
418 }
419
420 static void configure_window(LocoWindow *lw, const XConfigureEvent *e)
421 {
422     LocoList *above, *pos;
423
424     pos = find_stacking(e->window);
425     above = find_stacking(e->above);
426
427     g_assert(pos != NULL && pos->window != NULL);
428     if (e->above && !above)
429         printf("missing windows from the stacking list!!\n");
430
431     if ((lw->x != e->x) || (lw->y != e->y)) {
432         lw->x = e->x;
433         lw->y = e->y;
434
435         full_redraw_required = redraw_required = TRUE;
436     }
437         
438     if ((lw->w != e->width) || (lw->h != e->height)) {
439         lw->w = e->width;
440         lw->h = e->height;
441
442         destroy_glxpixmap(lw);
443
444         full_redraw_required = redraw_required = TRUE;
445     }
446
447     if (pos->next != above) {
448         //printf("Window 0x%lx above 0x%lx\n", pos->window->id,
449         //       above ? above->window->id : 0);
450         loco_list_move_before(&stacking_top, &stacking_bottom, pos, above);
451
452         full_redraw_required = redraw_required = TRUE;
453     }
454 }
455
456 static void damage_window(LocoWindow *lw)
457 {
458     LocoList *it, *pos;
459
460     pos = find_stacking(lw->id);
461
462     /* XXX if it is transparent, then damage any windows below it that
463        intersect */
464
465     /* damage any windows above it that intersect */
466     for (it = pos; it; it = it->prev) {
467         const LocoWindow *lwa = it->window;
468         if (lwa->x < lw->x + lw->w && lwa->x + lwa->w > lw->x &&
469             lwa->y < lw->y + lw->h && lwa->y + lwa->h > lw->y)
470         {
471             it->window->damaged = TRUE;
472         }
473     }
474
475     /* mark the damage as fixed - we know about it now */
476     obt_display_ignore_errors(TRUE);
477     XDamageSubtract(obt_display, lw->damage, None, None);
478     obt_display_ignore_errors(FALSE);
479
480     lw->damaged = TRUE;
481     redraw_required = TRUE;
482 }
483
484 void COMPOSTER_RAWR(const XEvent *e, gpointer data)
485 {
486     //g_print("COMPOSTER_RAWR() %d\n", e->type);
487
488     if (e->type == ConfigureNotify) {
489         LocoWindow *lw;
490
491         lw = find_window(e->xconfigure.window);
492         if (lw)
493             configure_window(lw, &e->xconfigure);
494         //print_stacking();
495     }
496     else if (e->type == CreateNotify) {
497         add_window(e->xmap.window);
498     }
499     else if (e->type == DestroyNotify) {
500         LocoWindow *lw = find_window(e->xdestroywindow.window);
501         if (lw) {
502             hide_window(lw, TRUE);
503             remove_window(lw);
504         }
505         else
506             printf("destroy notify for unknown window 0x%lx\n",
507                    e->xdestroywindow.window);
508     }
509     else if (e->type == ReparentNotify) {
510         if (e->xreparent.parent == loco_root)
511             add_window(e->xreparent.window);
512         else {
513             LocoWindow *lw = find_window(e->xreparent.window);
514             if (lw) {
515                 printf("window 0x%lx reparented from root\n", lw->id);
516                 hide_window(lw, FALSE);
517                 remove_window(lw);
518             }
519             else
520                 printf("reparent notify away from root for unknown window "
521                        "0x%lx\n", e->xreparent.window);
522         }
523     }
524
525     else if (e->type == MapNotify) {
526         LocoWindow *lw = find_window(e->xmap.window);
527         if (lw)
528             show_window(lw);
529         else
530             printf("map notify for unknown window 0x%lx\n",
531                    e->xmap.window);
532     }
533     else if (e->type == UnmapNotify) {
534         LocoWindow *lw = find_window(e->xunmap.window);
535         if (lw)
536             hide_window(lw, FALSE);
537         else
538             printf("unmap notify for unknown window 0x%lx\n",
539                    e->xunmap.window);
540     }
541     else if (e->type == obt_display_extension_damage_basep + XDamageNotify) {
542         const XDamageNotifyEvent *de;
543         LocoWindow *lw;
544
545         de = (const XDamageNotifyEvent*)e;
546         lw = find_window(de->drawable);
547         if (lw) {
548             damage_window(lw);
549         }
550         else if (de->drawable == loco_root) {
551             XDamageSubtract(obt_display, de->damage, None, None);
552             full_redraw_required = redraw_required = TRUE;
553         }
554     }
555 }
556
557 static void find_all_windows(gint screen)
558 {
559     guint i, nchild;
560     Window w, *children;
561
562     if (!XQueryTree(obt_display, loco_root, &w, &w, &children, &nchild))
563         nchild = 0;
564
565     for (i = 0; i < nchild; ++i)
566         if (children[i] != None) add_window(children[i]);
567
568     if (children) XFree(children);
569 }
570
571 static gboolean compositor_timeout(gpointer data)
572 {
573     if (redraw_required)
574         full_composite();
575     return TRUE; /* repeat */
576 }
577
578 static guint window_hash(Window *w) { return *w; }
579 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
580
581 void loco_set_mainloop(gint screen_num, ObtMainLoop *loop)
582 {
583     int db, stencil, depth;
584     int i, j, value, count;
585     int w, h;
586     XVisualInfo *vi, tvis, *visinfo;
587     GLXFBConfig *allfbconfigs;
588     int numfbconfigs;
589     GLXContext cont;
590     static int config[] =
591         { GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, GLX_RGBA, None };
592     static const int drawable_tfp_attrs[] =
593         {
594             GLX_CONFIG_CAVEAT, GLX_NONE,
595             GLX_DOUBLEBUFFER, False,
596             GLX_DEPTH_SIZE, 0,
597             GLX_RED_SIZE, 1,
598             GLX_GREEN_SIZE, 1,
599             GLX_BLUE_SIZE, 1,
600             GLX_ALPHA_SIZE, 1,
601             GLX_RENDER_TYPE, GLX_RGBA_BIT,
602             GLX_BIND_TO_TEXTURE_RGBA_EXT, True, // additional for tfp
603             None
604         };
605
606     loco_screen_num = screen_num;
607     loco_root = obt_root(screen_num);
608     loco_overlay = XCompositeGetOverlayWindow(obt_display, loco_root);
609     XserverRegion region = XFixesCreateRegion(obt_display, NULL, 0);
610
611     XFixesSetWindowShapeRegion (obt_display,
612                                 loco_overlay,
613                                 ShapeBounding,
614                                 0, 0,
615                                 0);
616     XFixesSetWindowShapeRegion (obt_display,
617                                 loco_overlay,
618                                 ShapeInput,
619                                 0, 0,
620                                 region);
621
622     XFixesDestroyRegion (obt_display, region);
623
624     vi = glXChooseVisual(obt_display, screen_num, config);
625     cont = glXCreateContext(obt_display, vi, NULL, GL_TRUE);
626     if (cont == NULL)
627         printf("context creation failed\n");
628     glXMakeCurrent(obt_display, loco_overlay, cont);
629
630     BindTexImageEXT = (BindEXTFunc)
631         glXGetProcAddress((const guchar*)"glXBindTexImageEXT");
632     ReleaseTexImageEXT = (ReleaseEXTFunc)
633         glXGetProcAddress((const guchar*)"glXReleaseTexImageEXT");
634
635     w = WidthOfScreen(ScreenOfDisplay(obt_display, screen_num));
636     h = HeightOfScreen(ScreenOfDisplay(obt_display, screen_num));
637     glViewport(0, 0, w, h);
638     glMatrixMode(GL_PROJECTION);
639     glLoadIdentity();
640 printf("Setting up an orthographic projection of %dx%d\n", w, h);
641     glOrtho(0, w, h, 0.0, -1.0, 100.0);
642     glMatrixMode(GL_MODELVIEW);
643     glLoadIdentity();
644     glClear(GL_COLOR_BUFFER_BIT);
645     glEnable(GL_TEXTURE_2D);
646 glError();
647     glXSwapBuffers(obt_display, loco_overlay);
648     glClearColor(1.0, 0.0, 0.0, 1.0);
649 //    glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
650 //    glEnable(GL_BLEND);
651
652     allfbconfigs = glXChooseFBConfig(obt_display,
653                                      loco_screen_num,
654                                      drawable_tfp_attrs,
655                                      &numfbconfigs);
656
657     db = 32767;
658     stencil = 32767;
659     depth = 32767;
660     for (i = 0; i <= MAX_DEPTH; i++) {
661         VisualID vid;
662
663         vid = 0;
664         glxFBConfig[i] = 0;
665
666         tvis.depth = i;
667         visinfo = XGetVisualInfo(obt_display, VisualDepthMask, &tvis, &count);
668         /* pick the nicest visual for the depth */
669         for (j = 0; j < count; j++) {
670             glXGetConfig(obt_display, &visinfo[j], GLX_USE_GL, &value);
671             if (!value)
672                 continue;
673
674             glXGetConfig(obt_display, &visinfo[j], GLX_DOUBLEBUFFER, &value);
675             if (value > db)
676                 continue;
677             db = value;
678
679             glXGetConfig(obt_display, &visinfo[j], GLX_STENCIL_SIZE, &value);
680             if (value > stencil)
681                 continue;
682             stencil = value;
683
684             glXGetConfig(obt_display, &visinfo[j], GLX_DEPTH_SIZE, &value);
685             if (value > depth)
686                 continue;
687             depth = value;
688
689             /* use this visual */
690             vid = visinfo[j].visualid;
691         }
692
693         if (!vid)
694             continue;
695
696         printf("found visual %d\n", i);
697
698         /* find the fbconfig for this depth/visual */
699         for(j = 0; j < numfbconfigs; ++j) {
700             glXGetFBConfigAttrib(obt_display, allfbconfigs[j],
701                                  GLX_VISUAL_ID, &value);
702             if (value == (int)vid) {
703                 glxFBConfig[i] = allfbconfigs[j]; /* save it */
704                 printf("supporting depth %d\n", i);
705                 break; /* next depth */
706             }
707         }
708     }
709
710     XFree(allfbconfigs);
711
712     obt_main_loop_x_add(loop, COMPOSTER_RAWR, NULL, NULL);
713     window_map = g_hash_table_new((GHashFunc)window_hash,
714                                   (GEqualFunc)window_comp);
715     stacking_map = g_hash_table_new((GHashFunc)window_hash,
716                                     (GEqualFunc)window_comp);
717     stacking_top = stacking_bottom = NULL;
718
719     XGrabServer(obt_display);
720
721     XCompositeRedirectSubwindows(obt_display, loco_root,
722                                  CompositeRedirectManual);
723     find_all_windows(screen_num);
724
725     XUngrabServer(obt_display);
726
727     full_redraw_required = redraw_required = TRUE;
728
729     obt_main_loop_timeout_add(loop, REFRESH_RATE,
730                               (GSourceFunc)compositor_timeout,
731                               NULL, NULL, NULL);
732 }
733
734 void loco_shutdown(void)
735 {
736 }