]> icculus.org git repositories - dana/openbox.git/blob - openbox/composite.c
make the root background color a config option, and add name_window_pixmap() helper...
[dana/openbox.git] / openbox / composite.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    composite.c for the Openbox window manager
4    Copyright (c) 2010        Dana Jansens
5    Copyright (c) 2010        Derek Foreman
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 "composite.h"
21 #include "config.h"
22 #include "obt/display.h"
23 #include "openbox.h"
24 #include "screen.h"
25 #include "event.h"
26 #include "geom.h"
27 #include "client.h"
28 #include "window.h"
29 #include "frame.h"
30 #include "geom.h"
31 #include "debug.h"
32 #include "gettext.h"
33 #include "obt/prop.h"
34
35 #include <X11/Xlib.h>
36 #include <glib.h>
37
38 #ifdef USE_COMPOSITING
39 #  include <GL/glew.h>
40 #  include <GL/glxew.h>
41 #  include <GL/gl.h>
42 #endif
43 #ifdef DEBUG
44 #  include <sys/time.h>
45 #endif
46
47 Window composite_overlay = None;
48 Atom   composite_cm_atom = None;
49
50 #ifdef USE_COMPOSITING
51 #define MAX_DEPTH 32
52
53 typedef struct _ObCompositeFBConfig {
54     GLXFBConfig fbc; /* the fbconfig */
55     gint tf;         /* texture format */
56 } ObCompositeFBConfig;
57
58 /*! Turn composite redirection on for a window */
59 static void composite_window_redir(struct _ObWindow *w);
60 /*! Turn composite redirection off for a window */
61 static void composite_window_unredir(struct _ObWindow *w);
62
63 static GLXContext          composite_ctx = NULL;
64 static ObCompositeFBConfig pixmap_config[MAX_DEPTH + 1]; /* depth is index */
65 static gboolean            composite_enabled = FALSE;
66 static guint               composite_idle_source = 0;
67 static gboolean            need_redraw = FALSE;
68 static gboolean            animating = FALSE;
69 static Window              composite_support_win = None;
70 static Pixmap              root_pixmap = None;
71 static GLXPixmap           root_gpixmap = None;
72 static GLuint              root_texture = 0;
73 static gboolean            root_bound = FALSE;
74 #ifdef DEBUG
75 static gboolean composite_started = FALSE;
76 #endif
77
78 static gboolean composite(gpointer data);
79
80 static inline void time_fix(struct timeval *tv)
81 {
82     while (tv->tv_usec >= 1000000) {
83         tv->tv_usec -= 1000000;
84         ++tv->tv_sec;
85     }
86     while (tv->tv_usec < 0) {
87         tv->tv_usec += 1000000;
88         --tv->tv_sec;
89     }
90 }
91
92 static void get_best_fbcon(GLXFBConfig *in, int count, int depth,
93                            ObCompositeFBConfig *out)
94 {
95     GLXFBConfig best = 0;
96     XVisualInfo *vi;
97     int i, value, alpha, stencil, depthb;
98     gboolean rgba, db;
99
100     rgba = FALSE;
101     db = TRUE;
102     stencil = G_MAXSHORT;
103     depthb = G_MAXSHORT;
104
105     for (i = 0; i < count; i++) {
106         vi = glXGetVisualFromFBConfig(obt_display, in[i]);
107         if (vi == NULL)
108             continue;
109
110         value = vi->depth;
111         XFree(vi);
112
113         if (value != depth)
114             continue;
115
116         glXGetFBConfigAttrib(obt_display, in[i], GLX_ALPHA_SIZE, &alpha);
117         glXGetFBConfigAttrib(obt_display, in[i], GLX_BUFFER_SIZE, &value);
118
119         /* the buffer size should equal the depth or else the buffer size minus
120            the alpha size should */
121         if (value != depth && value - alpha != depth) continue;
122
123         value = 0;
124         if (depth == 32) {
125             glXGetFBConfigAttrib(obt_display, in[i],
126                                   GLX_BIND_TO_TEXTURE_RGBA_EXT, &value);
127             rgba = TRUE;
128         }
129         if (!value) {
130             if (rgba) continue; /* a different one has rgba, prefer that */
131
132             glXGetFBConfigAttrib(obt_display, in[i],
133                                   GLX_BIND_TO_TEXTURE_RGB_EXT, &value);
134         }
135         if (!value) // neither bind to texture?  no dice
136             continue;
137
138         /* get no doublebuffer if possible */
139         glXGetFBConfigAttrib(obt_display, in[i], GLX_DOUBLEBUFFER, &value);
140         if (value && !db) continue;
141         db = value;
142
143         /* get the smallest stencil buffer */
144         glXGetFBConfigAttrib(obt_display, in[i], GLX_STENCIL_SIZE, &value);
145         if (value > stencil) continue;
146         stencil = value;
147
148         /* get the smallest depth buffer */
149         glXGetFBConfigAttrib(obt_display, in[i], GLX_DEPTH_SIZE, &value);
150         if (value > depthb) continue;
151         depthb = value;
152
153         best = in[i];
154     }
155     out->fbc = best;
156     out->tf = rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
157 }
158
159 static Pixmap name_window_pixmap(Window w)
160 {
161     Pixmap p;
162     obt_display_ignore_errors(TRUE);
163     p = XCompositeNameWindowPixmap(obt_display, w);
164     obt_display_ignore_errors(FALSE);
165     if (obt_display_error_occured) {
166         ob_debug_type(OB_DEBUG_CM, "Error in XCompositeNameWindowPixmap for "
167                       "window 0x%x", w);
168         /* it can error but still return an ID, which will cause an
169            error to occur if you try to free it etc */
170         p = None;
171     }
172     return p;
173 }
174
175 static GLXPixmap create_glx_pixmap(Pixmap px, gint depth)
176 {
177     GLXPixmap gpx;
178     int attribs[] = {
179         GLX_TEXTURE_FORMAT_EXT,
180         pixmap_config[depth].tf,
181         GLX_TEXTURE_TARGET_EXT,
182         GLX_TEXTURE_2D_EXT,
183         None
184     };
185     obt_display_ignore_errors(TRUE);
186     gpx = glXCreatePixmap(obt_display, pixmap_config[depth].fbc, px, attribs);
187     obt_display_ignore_errors(FALSE);
188     if (obt_display_error_occured)
189         gpx = None; /* stupid drivers can and do exist */
190     return gpx;
191 }
192
193 static gboolean bind_glx_pixmap(GLXPixmap gpx, GLuint tex)
194 {
195     glBindTexture(GL_TEXTURE_2D, tex);
196
197     obt_display_ignore_errors(TRUE);
198     glXBindTexImageEXT(obt_display, gpx, GLX_FRONT_LEFT_EXT, NULL);
199     obt_display_ignore_errors(FALSE);
200
201     if (!obt_display_error_occured) {
202         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
203         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
204         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
205         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
206         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
207     }
208     else
209         ob_debug_type(OB_DEBUG_CM, "Error binding GLXPixmap to Texture");
210
211     return !obt_display_error_occured;
212 }
213
214 /* pass 0 for tex if it is not bound to one */
215 static void destroy_glx_pixmap(GLXPixmap gpx, GLuint tex)
216 {
217     if (gpx) {
218         if (tex) {
219             glBindTexture(GL_TEXTURE_2D, tex);
220             glXReleaseTexImageEXT(obt_display, gpx, GLX_FRONT_LEFT_EXT);
221         }
222         glXDestroyPixmap(obt_display, gpx);
223     }
224 }
225
226 void composite_dirty(void)
227 {
228     need_redraw = 1;
229     if (!composite_idle_source)
230         composite_idle_source = g_idle_add(composite, NULL);
231 }
232
233 static gboolean composite_annex(void)
234 {
235     gchar *astr;
236     Window cm_owner;
237     Time timestamp;
238     XSetWindowAttributes attrib;
239
240     g_assert(composite_support_win == None);
241
242     attrib.override_redirect = TRUE;
243     composite_support_win = XCreateWindow(obt_display, screen_support_win,
244                                           -100, -100, 1, 1, 0,
245                                           CopyFromParent, InputOnly,
246                                           CopyFromParent,
247                                           CWOverrideRedirect,
248                                           &attrib);
249
250     astr = g_strdup_printf("_NET_WM_CM_S%d", ob_screen);
251     composite_cm_atom = XInternAtom(obt_display, astr, FALSE);
252     g_free(astr);
253
254     cm_owner = XGetSelectionOwner(obt_display, composite_cm_atom);
255     if (cm_owner != None) {
256         XDestroyWindow(obt_display, composite_support_win);
257         composite_support_win = None;
258         return FALSE;
259     }
260
261     timestamp = event_time();
262     XSetSelectionOwner(obt_display, composite_cm_atom, composite_support_win,
263                        timestamp);
264
265     cm_owner = XGetSelectionOwner(obt_display, composite_cm_atom);
266     if (cm_owner != composite_support_win) {
267         XDestroyWindow(obt_display, composite_support_win);
268         composite_support_win = None;
269         return FALSE;
270     }
271
272     /* Send client message indicating that we are now the CM */
273     obt_prop_message(ob_screen, obt_root(ob_screen), OBT_PROP_ATOM(MANAGER),
274                      timestamp, composite_cm_atom, composite_support_win, 0, 0,
275                      SubstructureNotifyMask);
276
277     return TRUE;
278 }
279
280 gboolean composite_enable(void)
281 {
282     int count, val, i;
283     XWindowAttributes xa;
284     XserverRegion xr;
285     XVisualInfo tmp, *vi;
286     GLXFBConfig *fbcs;
287
288     if (composite_enabled) return TRUE;
289
290     g_assert(config_comp);
291
292     /* Check for the required extensions in the server */
293     if (!obt_display_extension_composite) {
294         g_message(
295             _("Failed to enable composite. The %s extension is missing."),
296             "XComposite");
297         composite_disable();
298         return FALSE;
299     }
300     if (!obt_display_extension_damage) {
301         g_message(
302             _("Failed to enable composite. The %s extension is missing."),
303             "XDamage");
304         composite_disable();
305         return FALSE;
306     }
307     if (!obt_display_extension_fixes) {
308         g_message(
309             _("Failed to enable composite. The %s extension is missing."),
310             "XFixes");
311         composite_disable();
312         return FALSE;
313     }
314
315     /* Make sure the root window's visual is acceptable for our GLX needs
316        and create a GLX context with it */
317
318     if (!XGetWindowAttributes(obt_display, obt_root(ob_screen), &xa)) {
319         g_message(_("Failed to enable composite. %s failed."),
320                   "XGetWindowAttributes");
321         composite_disable();
322         return FALSE;
323     }
324     tmp.visualid = XVisualIDFromVisual(xa.visual);
325     vi = XGetVisualInfo(obt_display, VisualIDMask, &tmp, &count);
326     if (!count) {
327         g_message(
328             _("Failed to enable composite. Failed to get visual info."));
329         composite_disable();
330         return FALSE;
331     }
332     glXGetConfig(obt_display, vi, GLX_USE_GL, &val);
333     if (!val) {
334         g_message(_("Failed to enable composite. Visual is not GL capable"));
335         XFree(vi);
336         composite_disable();
337         return FALSE;
338     }
339     glXGetConfig(obt_display, vi, GLX_DOUBLEBUFFER, &val);
340     if (!val) {
341         g_message(
342             _("Failed to enable composite. Visual is not double buffered"));
343         XFree(vi);
344         composite_disable();
345         return FALSE;
346     }
347     composite_ctx = glXCreateContext(obt_display, vi, NULL, True);
348     XFree(vi);
349     if (!composite_ctx) {
350         g_message(
351             _("Failed to enable composite. Unable to create GLX context"));
352         composite_disable();
353         return FALSE;
354     }
355
356     /* Attempt to take over as composite manager.  There can only be one. */
357
358     if (!composite_annex()) {
359         g_message(_("Failed to enable composite. Another composite manager is running."));
360         composite_disable();
361         return FALSE;
362     }
363
364     /* Set up the overlay window */
365
366     composite_overlay = XCompositeGetOverlayWindow(obt_display,
367                                                    obt_root(ob_screen));
368     if (!composite_overlay) {
369         g_message(_("Failed to enable composite. Unable to get overlay window from X server"));
370         composite_disable();
371         return FALSE;
372     }
373     xr = XFixesCreateRegion(obt_display, NULL, 0);
374     XFixesSetWindowShapeRegion(obt_display, composite_overlay, ShapeBounding,
375                                0, 0, 0);
376     XFixesSetWindowShapeRegion(obt_display, composite_overlay, ShapeInput,
377                                0, 0, xr);
378     XFixesDestroyRegion(obt_display, xr);
379
380     /* Need a current GL context before GLEW works. */
381
382     glXMakeCurrent(obt_display, composite_overlay, composite_ctx);
383
384     /* init GLEW */
385     GLenum err = glewInit();
386     if (GLEW_OK != err) {
387         g_message(_("Failed to enable composite.  GLEW init failed."));
388         composite_disable();
389         return FALSE;
390     }
391
392     /* Check for required GLX extensions */
393
394     if (!GLXEW_EXT_texture_from_pixmap) {
395         g_message(_("Failed to enable composite. %s is not present."),
396                   "GLX_EXT_texture_from_pixmap");
397         composite_disable();
398         return FALSE;
399     }
400
401     /* Check for FBconfigs */
402 //XXX: Technically we should test for GL 1.3 before using this, but that
403 //disqualifies some drivers that support parts of GL 1.3 yet report GL 1.2
404     fbcs = glXGetFBConfigs(obt_display, ob_screen, &count);
405     if (!count) {
406         g_message(_("Failed to enable composite. No valid FBConfigs."));
407         composite_disable();
408         return FALSE;
409     }
410     memset(&pixmap_config, 0, sizeof(pixmap_config));
411     for (i = 1; i < MAX_DEPTH + 1; i++)
412         get_best_fbcon(fbcs, count, i, &pixmap_config[i]);
413     if (count) XFree(fbcs);
414
415     printf("Best visual for 24bpp was 0x%lx\n",
416            (gulong)pixmap_config[24].fbc);
417     printf("Best visual for 32bpp was 0x%lx\n",
418            (gulong)pixmap_config[32].fbc);
419
420     /* We're good to go for composite ! */
421
422     /* register our screen redraw callback */
423     if (animating)
424         composite_idle_source = g_idle_add(composite, NULL);
425
426     //Attempt to enable vsync
427     if (GLXEW_EXT_swap_control) {
428         GLXDrawable drawable = glXGetCurrentDrawable();
429         glXSwapIntervalEXT(obt_display, drawable, 1);
430     } else {
431         ob_debug_type(OB_DEBUG_CM, "Vsync control not available.");
432     }
433
434     glClearColor(config_comp_root_color_r, config_comp_root_color_g,
435                  config_comp_root_color_b, 0.0f);
436     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
437     glXSwapBuffers(obt_display, composite_overlay);
438     glMatrixMode(GL_PROJECTION);
439     glLoadIdentity();
440     glMatrixMode(GL_MODELVIEW);
441     glLoadIdentity();
442     glDisable(GL_DEPTH_TEST);
443     glEnable(GL_TEXTURE_2D);
444     glEnable(GL_BLEND);
445     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
446
447     glGenTextures(1, &root_texture);
448
449     composite_enabled = TRUE;
450
451     composite_resize();
452     window_foreach(composite_window_redir);
453
454     return TRUE;
455 }
456
457 void composite_disable(void)
458 {
459     if (root_texture) {
460         glDeleteTextures(1, &root_texture);
461         root_texture = 0;
462     }
463
464     if (root_gpixmap) {
465         destroy_glx_pixmap(root_gpixmap, root_bound ? root_texture : 0);
466         root_bound = FALSE;
467         root_gpixmap = None;
468     }
469
470     window_foreach(composite_window_unredir);
471
472     if (composite_ctx) {
473         obt_display_ignore_errors(TRUE);
474         glXMakeCurrent(obt_display, None, NULL);
475         obt_display_ignore_errors(FALSE);
476
477         glXDestroyContext(obt_display, composite_ctx);
478         composite_ctx = NULL;
479     }
480
481     if (composite_overlay) {
482         XCompositeReleaseOverlayWindow(obt_display, composite_overlay);
483         composite_overlay = None;
484     }
485
486     if (composite_support_win)
487         XDestroyWindow(obt_display, composite_support_win);
488
489     if (composite_idle_source) {
490         g_source_remove(composite_idle_source);
491         composite_idle_source = 0;
492     }
493
494     composite_enabled = FALSE;
495     need_redraw = 0;
496 }
497
498 /*! This function will try enable composite if config_comp is TRUE.  At the
499   end of this process, config_comp will be set to TRUE only if composite
500   is enabled, and FALSE otherwise. */
501 void composite_startup(gboolean reconfig)
502 {
503 #ifdef DEBUG
504     composite_started = TRUE;
505 #endif
506
507     if (!reconfig) {
508         if (ob_comp_indirect)
509             setenv("LIBGL_ALWAYS_INDIRECT", "1", TRUE);
510         return;
511     }
512
513     if (composite_enabled)
514         /* this is a config option so it can be changed on reconfigure */
515         glClearColor(config_comp_root_color_r, config_comp_root_color_g,
516                      config_comp_root_color_b, 0.0f);
517 }
518
519 void composite_shutdown(gboolean reconfig)
520 {
521 #ifdef DEBUG
522     composite_started = FALSE;
523 #endif
524
525     if (reconfig) return;
526
527     if (composite_enabled)
528         composite_disable();
529 }
530
531 void composite_resize(void)
532 {
533     const Rect *a;
534
535     if (!composite_enabled) return;
536
537     a = screen_physical_area_all_monitors();
538     glOrtho(a->x, a->x + a->width, a->y + a->height, a->y, -100, 100);
539
540     composite_root_invalid();
541     composite_dirty();
542 }
543
544 static gboolean composite(gpointer data)
545 {
546     ObWindow *win;
547     ObClient *client;
548     ObStackingIter *it;
549
550     if (!composite_enabled) {
551         composite_idle_source = 0;
552         return FALSE;
553     }
554
555     if (!animating && !need_redraw) {
556         if (animating) return TRUE;
557         composite_idle_source = 0;
558         return FALSE;
559     }
560
561     glClear(GL_DEPTH_BUFFER_BIT);
562
563     /* draw the screen background */
564     if (root_pixmap && !root_gpixmap) {
565         const int depth = DefaultDepth(obt_display, ob_screen);
566         root_gpixmap = create_glx_pixmap(root_pixmap, depth);
567         root_bound = bind_glx_pixmap(root_gpixmap, root_texture);
568     }
569     if (root_bound) {
570         const Rect *rw = screen_physical_area_all_monitors();
571         const gint l = rw->x, r = rw->x + rw->width, t = rw->y,
572             b = rw->y + rw->height;
573         obt_display_ignore_errors(TRUE);
574         glBindTexture(GL_TEXTURE_2D, root_texture);
575         glBegin(GL_QUADS);
576         glTexCoord2f(0.0f, 0.0f);
577         glVertex3f(l, t, 0);
578         glTexCoord2f(0.0f, 1.0f);
579         glVertex3f(l, b, 0);
580         glTexCoord2f(1.0f, 1.0f);
581         glVertex3f(r, b, 0.0f);
582         glTexCoord2f(1.0f, 0.0f);
583         glVertex3f(r, t, 0);
584         glEnd();
585         obt_display_ignore_errors(FALSE);
586     }
587     else {
588         /* solid color as the fallback */
589         glClear(GL_COLOR_BUFFER_BIT);
590     }
591
592     it = stacking_iter_tail();
593     for (; (win = stacking_iter_win(it)); stacking_iter_prev(it)) {
594         gint x, y, w, h;
595
596         if (!win->mapped || !win->is_redir)
597             continue;
598
599         if (win->type == OB_WINDOW_CLASS_CLIENT) {
600             client = WINDOW_AS_CLIENT(win);
601             if (!client->frame->visible)
602                 continue;
603         }
604         else client = NULL;
605
606         if (win->pixmap == None) {
607             if (!(win->pixmap = name_window_pixmap(window_redir(win))))
608                 continue;
609         }
610
611         if (win->gpixmap == None) {
612             const int depth = window_depth(win);
613             if (!(win->gpixmap = create_glx_pixmap(win->pixmap, depth)))
614                 continue;
615             win->bound = bind_glx_pixmap(win->gpixmap, win->texture);
616         }
617
618         if (!win->bound)
619             continue;
620
621         glBindTexture(GL_TEXTURE_2D, win->texture);
622
623         x = win->toparea.x + win->topborder + win->area.x;
624         y = win->toparea.y + win->topborder + win->area.y;
625         w = win->area.width;
626         h = win->area.height;
627
628         if (win->alpha && *win->alpha < 0xffffffff)
629             glColor4ui(0xffffffff, 0xffffffff, 0xffffffff, *win->alpha);
630
631         glBegin(GL_QUADS);
632         if (!win->n_rects) {
633             glTexCoord2f(0, 0);
634             glVertex3f(x, y, 0.0);
635             glTexCoord2f(0, 1);
636             glVertex3f(x, y + h, 0.0);
637             glTexCoord2f(1, 1);
638             glVertex3f(x + w, y + h, 0.0);
639             glTexCoord2f(1, 0);
640             glVertex3f(x + w, y, 0.0);
641         }
642         else {
643             gint i;
644             /* the border is not included in the shape rect coords */
645             const gint sb = window_top(win) == window_redir(win) ?
646                 win->topborder : 0;
647
648
649             for (i = 0; i < win->n_rects; ++i) {
650                 const gint xb = win->rects[i].x + sb;
651                 const gint yb = win->rects[i].y + sb;
652                 const gint wb = win->rects[i].width;
653                 const gint hb = win->rects[i].height;
654
655                 glTexCoord2d((GLdouble)xb/w,
656                              (GLdouble)yb/h);
657                 glVertex3f(x + xb, y + yb, 0.0f);
658
659                 glTexCoord2d((GLdouble)xb/w,
660                              (GLdouble)(yb+hb)/h);
661                 glVertex3f(x + xb, y + yb + hb, 0.0f);
662
663                 glTexCoord2d((GLdouble)(xb+wb)/w,
664                              (GLdouble)(yb+hb)/h);
665                 glVertex3f(x + xb + wb, y + yb + hb, 0.0f);
666
667                 glTexCoord2d((GLdouble)(xb+wb)/w,
668                              (GLdouble)yb/h);
669                 glVertex3f(x + xb + wb, y + yb, 0.0f);
670             }
671         }
672         glEnd();
673
674         if (win->alpha && *win->alpha < 0xffffffff)
675             glColor4f(1.0, 1.0, 1.0, 1.0);
676
677         if (client && (client->frame->decorations & OB_FRAME_DECOR_BORDER)) {
678             int a, b, c, d;
679             a = client->frame->size.left;
680             b = client->frame->size.right;
681             c = client->frame->size.top;
682             d = client->frame->size.bottom;
683             if (client->frame->focused)
684                 glColor4f(ob_rr_theme->frame_focused_border_color->r/255.0,
685                           ob_rr_theme->frame_focused_border_color->g/255.0,
686                           ob_rr_theme->frame_focused_border_color->b/255.0,
687                           0.5);
688             else
689                 glColor4f(ob_rr_theme->frame_unfocused_border_color->r/255.0,
690                           ob_rr_theme->frame_unfocused_border_color->g/255.0,
691                           ob_rr_theme->frame_unfocused_border_color->b/255.0,
692                           0.5);
693             glDisable(GL_TEXTURE_2D);
694             glBegin(GL_QUADS);
695             glVertex3f(x - a, y - c, 0.0);
696             glVertex3f(x + w, y - c, 0.0);
697             glVertex3f(x + w, y, 0.0);
698             glVertex3f(x - a, y, 0.0);
699
700             glVertex3f(x + w, y - c, 0.0);
701             glVertex3f(x + w + b, y - c, 0.0);
702             glVertex3f(x + w + b, y + h + d, 0.0);
703             glVertex3f(x + w, y + h + d, 0.0);
704
705             glVertex3f(x - a, y + h + d, 0.0);
706             glVertex3f(x + w, y + h + d, 0.0);
707             glVertex3f(x + w, y + h, 0.0);
708             glVertex3f(x - a, y + h, 0.0);
709
710             glVertex3f(x - a, y, 0.0);
711             glVertex3f(x, y, 0.0);
712             glVertex3f(x, y + h, 0.0);
713             glVertex3f(x - a, y + h, 0.0);
714             glEnd();
715             glEnable(GL_TEXTURE_2D);
716             glColor4f(1.0, 1.0, 1.0, 1.0);
717         }
718     }
719     stacking_iter_free(it);
720
721     glXSwapBuffers(obt_display, composite_overlay);
722     glFinish();
723
724     if (ob_comp_indirect)
725         g_usleep(1000);
726
727 #ifdef DEBUG
728     {
729         GLenum gler;
730         while ((gler = glGetError()) != GL_NO_ERROR) {
731             printf("gl error %d\n", gler);
732         }
733     }
734 #endif
735
736     need_redraw = FALSE;
737     if (animating) return TRUE;
738     composite_idle_source = 0;
739     return FALSE;
740 }
741
742 static void composite_window_redir(ObWindow *w)
743 {
744     if (!composite_enabled) return;
745
746     if (w->is_redir) return;
747
748     g_assert(w->gpixmap == None);
749     g_assert(w->pixmap == None);
750     g_assert(w->bound == FALSE);
751     g_assert(w->texture == 0);
752
753     XCompositeRedirectWindow(obt_display, window_redir(w),
754                              CompositeRedirectManual);
755
756     glGenTextures(1, &w->texture);
757
758     w->is_redir = TRUE;
759 }
760
761 static void composite_window_unredir(ObWindow *w)
762 {
763     if (!w->redir) return;
764
765     /* this call can cause a BadValue error */
766     obt_display_ignore_errors(TRUE);
767     XCompositeUnredirectWindow(obt_display, window_redir(w),
768                                CompositeRedirectManual);
769     obt_display_ignore_errors(FALSE);
770
771     glDeleteTextures(1, &w->texture);
772     w->texture = 0;
773
774     w->is_redir = FALSE;
775
776     composite_window_invalid(w);
777 }
778
779 void composite_window_setup(ObWindow *w)
780 {
781     if (w->type == OB_WINDOW_CLASS_PROMPT) return;
782
783 #ifdef DEBUG
784     g_assert(composite_started);
785 #endif
786
787     obt_display_ignore_errors(TRUE);
788     w->damage = XDamageCreate(obt_display, window_redir(w),
789                               XDamageReportRawRectangles);
790     obt_display_ignore_errors(FALSE);
791
792     composite_window_redir(w);
793 }
794
795 void composite_window_cleanup(ObWindow *w)
796 {
797     if (w->type == OB_WINDOW_CLASS_PROMPT) return;
798
799 #ifdef DEBUG
800     g_assert(composite_started);
801 #endif
802
803     composite_window_unredir(w);
804
805     if (w->damage) {
806         XDamageDestroy(obt_display, w->damage);
807         w->damage = None;
808     }
809 }
810
811 void composite_window_invalid(ObWindow *w)
812 {
813     if (w->gpixmap) {
814         destroy_glx_pixmap(w->gpixmap, w->bound ? w->texture : 0);
815         w->bound = FALSE;
816         w->gpixmap = None;
817     }
818     if (w->pixmap) {
819         XFreePixmap(obt_display, w->pixmap);
820         w->pixmap = None;
821     }
822 }
823
824 void composite_root_invalid(void)
825 {
826     guint32 px;
827
828     px = None;
829     if (!OBT_PROP_GET32(obt_root(ob_screen), XROOTPMAP_ID, PIXMAP, &px))
830         OBT_PROP_GET32(obt_root(ob_screen), ESETROOT_PMAP_ID, PIXMAP, &px);
831
832     if (px != root_pixmap) {
833         if (root_gpixmap) {
834             destroy_glx_pixmap(root_gpixmap, root_bound ? root_texture : 0);
835             root_bound = FALSE;
836             root_gpixmap = None;
837         }
838         root_pixmap = px;
839     }
840 }
841
842 #else
843 void composite_startup        (gboolean boiv) {;(void)(biov);}
844 void composite_shutdown       (gboolean boiv) {;(void)(biov);}
845 void composite                (void)          {}
846 void composite_resize         (void)          {}
847 void composite_disable        (void)          {}
848 void composite_window_setup   (ObWindow *w)   {}
849 void composite_window_cleanup (ObWindow *w)   {}
850 void composite_window_invalid (ObWindow *w)   {}
851 void composite_root_invalid   (void)          {}
852 void composite_dirty          (void)          {}
853
854 void composite_enable(void)
855 {
856     g_message(
857         _("Unable to use compositing. Openbox was compiled without it."));
858 }
859 #endif