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