1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 composite.c for the Openbox window manager
4 Copyright (c) 2010 Dana Jansens
5 Copyright (c) 2010 Derek Foreman
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
20 #include "composite.h"
22 #include "obt/display.h"
38 #ifdef USE_COMPOSITING
40 # include <GL/glxew.h>
44 # include <sys/time.h>
47 Window composite_overlay = None;
48 Atom composite_cm_atom = None;
50 #ifdef USE_COMPOSITING
53 typedef struct _ObCompositeFBConfig {
54 GLXFBConfig fbc; /* the fbconfig */
55 gint tf; /* texture format */
56 } ObCompositeFBConfig;
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);
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;
75 static gboolean composite_started = FALSE;
78 static gboolean composite(gpointer data);
80 static inline void time_fix(struct timeval *tv)
82 while (tv->tv_usec >= 1000000) {
83 tv->tv_usec -= 1000000;
86 while (tv->tv_usec < 0) {
87 tv->tv_usec += 1000000;
92 static void get_best_fbcon(GLXFBConfig *in, int count, int depth,
93 ObCompositeFBConfig *out)
97 int i, value, alpha, stencil, depthb;
102 stencil = G_MAXSHORT;
105 for (i = 0; i < count; i++) {
106 vi = glXGetVisualFromFBConfig(obt_display, in[i]);
116 glXGetFBConfigAttrib(obt_display, in[i], GLX_ALPHA_SIZE, &alpha);
117 glXGetFBConfigAttrib(obt_display, in[i], GLX_BUFFER_SIZE, &value);
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;
125 glXGetFBConfigAttrib(obt_display, in[i],
126 GLX_BIND_TO_TEXTURE_RGBA_EXT, &value);
130 if (rgba) continue; /* a different one has rgba, prefer that */
132 glXGetFBConfigAttrib(obt_display, in[i],
133 GLX_BIND_TO_TEXTURE_RGB_EXT, &value);
135 if (!value) // neither bind to texture? no dice
138 /* get no doublebuffer if possible */
139 glXGetFBConfigAttrib(obt_display, in[i], GLX_DOUBLEBUFFER, &value);
140 if (value && !db) continue;
143 /* get the smallest stencil buffer */
144 glXGetFBConfigAttrib(obt_display, in[i], GLX_STENCIL_SIZE, &value);
145 if (value > stencil) continue;
148 /* get the smallest depth buffer */
149 glXGetFBConfigAttrib(obt_display, in[i], GLX_DEPTH_SIZE, &value);
150 if (value > depthb) continue;
156 out->tf = rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
159 static Pixmap name_window_pixmap(Window w)
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 "
168 /* it can error but still return an ID, which will cause an
169 error to occur if you try to free it etc */
175 static GLXPixmap create_glx_pixmap(Pixmap px, gint depth)
179 GLX_TEXTURE_FORMAT_EXT,
180 pixmap_config[depth].tf,
181 GLX_TEXTURE_TARGET_EXT,
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 */
193 static gboolean bind_glx_pixmap(GLXPixmap gpx, GLuint tex)
195 glBindTexture(GL_TEXTURE_2D, tex);
197 obt_display_ignore_errors(TRUE);
198 glXBindTexImageEXT(obt_display, gpx, GLX_FRONT_LEFT_EXT, NULL);
199 obt_display_ignore_errors(FALSE);
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);
209 ob_debug_type(OB_DEBUG_CM, "Error binding GLXPixmap to Texture");
211 return !obt_display_error_occured;
214 /* pass 0 for tex if it is not bound to one */
215 static void destroy_glx_pixmap(GLXPixmap gpx, GLuint tex)
219 glBindTexture(GL_TEXTURE_2D, tex);
220 glXReleaseTexImageEXT(obt_display, gpx, GLX_FRONT_LEFT_EXT);
222 glXDestroyPixmap(obt_display, gpx);
226 void composite_dirty(void)
229 if (!composite_idle_source)
230 composite_idle_source = g_idle_add(composite, NULL);
233 static gboolean composite_annex(void)
238 XSetWindowAttributes attrib;
240 g_assert(composite_support_win == None);
242 attrib.override_redirect = TRUE;
243 composite_support_win = XCreateWindow(obt_display, screen_support_win,
245 CopyFromParent, InputOnly,
250 astr = g_strdup_printf("_NET_WM_CM_S%d", ob_screen);
251 composite_cm_atom = XInternAtom(obt_display, astr, FALSE);
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;
261 timestamp = event_time();
262 XSetSelectionOwner(obt_display, composite_cm_atom, composite_support_win,
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;
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);
280 gboolean composite_enable(void)
283 XWindowAttributes xa;
285 XVisualInfo tmp, *vi;
288 if (composite_enabled) return TRUE;
290 g_assert(config_comp);
292 /* Check for the required extensions in the server */
293 if (!obt_display_extension_composite) {
295 _("Failed to enable composite. The %s extension is missing."),
300 if (!obt_display_extension_damage) {
302 _("Failed to enable composite. The %s extension is missing."),
307 if (!obt_display_extension_fixes) {
309 _("Failed to enable composite. The %s extension is missing."),
315 /* Make sure the root window's visual is acceptable for our GLX needs
316 and create a GLX context with it */
318 if (!XGetWindowAttributes(obt_display, obt_root(ob_screen), &xa)) {
319 g_message(_("Failed to enable composite. %s failed."),
320 "XGetWindowAttributes");
324 tmp.visualid = XVisualIDFromVisual(xa.visual);
325 vi = XGetVisualInfo(obt_display, VisualIDMask, &tmp, &count);
328 _("Failed to enable composite. Failed to get visual info."));
332 glXGetConfig(obt_display, vi, GLX_USE_GL, &val);
334 g_message(_("Failed to enable composite. Visual is not GL capable"));
339 glXGetConfig(obt_display, vi, GLX_DOUBLEBUFFER, &val);
342 _("Failed to enable composite. Visual is not double buffered"));
347 composite_ctx = glXCreateContext(obt_display, vi, NULL, True);
349 if (!composite_ctx) {
351 _("Failed to enable composite. Unable to create GLX context"));
356 /* Attempt to take over as composite manager. There can only be one. */
358 if (!composite_annex()) {
359 g_message(_("Failed to enable composite. Another composite manager is running."));
364 /* Set up the overlay window */
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"));
373 xr = XFixesCreateRegion(obt_display, NULL, 0);
374 XFixesSetWindowShapeRegion(obt_display, composite_overlay, ShapeBounding,
376 XFixesSetWindowShapeRegion(obt_display, composite_overlay, ShapeInput,
378 XFixesDestroyRegion(obt_display, xr);
380 /* Need a current GL context before GLEW works. */
382 glXMakeCurrent(obt_display, composite_overlay, composite_ctx);
385 GLenum err = glewInit();
386 if (GLEW_OK != err) {
387 g_message(_("Failed to enable composite. GLEW init failed."));
392 /* Check for required GLX extensions */
394 if (!GLXEW_EXT_texture_from_pixmap) {
395 g_message(_("Failed to enable composite. %s is not present."),
396 "GLX_EXT_texture_from_pixmap");
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);
406 g_message(_("Failed to enable composite. No valid FBConfigs."));
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);
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);
420 /* We're good to go for composite ! */
422 /* register our screen redraw callback */
424 composite_idle_source = g_idle_add(composite, NULL);
426 //Attempt to enable vsync
427 if (GLXEW_EXT_swap_control) {
428 GLXDrawable drawable = glXGetCurrentDrawable();
429 glXSwapIntervalEXT(obt_display, drawable, 1);
431 ob_debug_type(OB_DEBUG_CM, "Vsync control not available.");
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);
440 glMatrixMode(GL_MODELVIEW);
442 glDisable(GL_DEPTH_TEST);
443 glEnable(GL_TEXTURE_2D);
445 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
447 glGenTextures(1, &root_texture);
449 composite_enabled = TRUE;
452 window_foreach(composite_window_redir);
457 void composite_disable(void)
460 glDeleteTextures(1, &root_texture);
465 destroy_glx_pixmap(root_gpixmap, root_bound ? root_texture : 0);
470 window_foreach(composite_window_unredir);
473 obt_display_ignore_errors(TRUE);
474 glXMakeCurrent(obt_display, None, NULL);
475 obt_display_ignore_errors(FALSE);
477 glXDestroyContext(obt_display, composite_ctx);
478 composite_ctx = NULL;
481 if (composite_overlay) {
482 XCompositeReleaseOverlayWindow(obt_display, composite_overlay);
483 composite_overlay = None;
486 if (composite_support_win)
487 XDestroyWindow(obt_display, composite_support_win);
489 if (composite_idle_source) {
490 g_source_remove(composite_idle_source);
491 composite_idle_source = 0;
494 composite_enabled = FALSE;
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)
504 composite_started = TRUE;
508 if (ob_comp_indirect)
509 setenv("LIBGL_ALWAYS_INDIRECT", "1", TRUE);
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);
519 void composite_shutdown(gboolean reconfig)
522 composite_started = FALSE;
525 if (reconfig) return;
527 if (composite_enabled)
531 void composite_resize(void)
535 if (!composite_enabled) return;
537 a = screen_physical_area_all_monitors();
538 glOrtho(a->x, a->x + a->width, a->y + a->height, a->y, -100, 100);
540 composite_root_invalid();
544 static gboolean composite(gpointer data)
550 if (!composite_enabled) {
551 composite_idle_source = 0;
555 if (!animating && !need_redraw) {
556 if (animating) return TRUE;
557 composite_idle_source = 0;
561 glClear(GL_DEPTH_BUFFER_BIT);
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);
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);
576 glTexCoord2f(0.0f, 0.0f);
578 glTexCoord2f(0.0f, 1.0f);
580 glTexCoord2f(1.0f, 1.0f);
581 glVertex3f(r, b, 0.0f);
582 glTexCoord2f(1.0f, 0.0f);
585 obt_display_ignore_errors(FALSE);
588 /* solid color as the fallback */
589 glClear(GL_COLOR_BUFFER_BIT);
592 it = stacking_iter_tail();
593 for (; (win = stacking_iter_win(it)); stacking_iter_prev(it)) {
596 if (!win->mapped || !win->is_redir)
599 if (win->type == OB_WINDOW_CLASS_CLIENT) {
600 client = WINDOW_AS_CLIENT(win);
601 if (!client->frame->visible)
606 if (win->pixmap == None) {
607 if (!(win->pixmap = name_window_pixmap(window_redir(win))))
611 if (win->gpixmap == None) {
612 const int depth = window_depth(win);
613 if (!(win->gpixmap = create_glx_pixmap(win->pixmap, depth)))
615 win->bound = bind_glx_pixmap(win->gpixmap, win->texture);
621 glBindTexture(GL_TEXTURE_2D, win->texture);
623 x = win->toparea.x + win->topborder + win->area.x;
624 y = win->toparea.y + win->topborder + win->area.y;
626 h = win->area.height;
628 if (win->alpha && *win->alpha < 0xffffffff)
629 glColor4ui(0xffffffff, 0xffffffff, 0xffffffff, *win->alpha);
634 glVertex3f(x, y, 0.0);
636 glVertex3f(x, y + h, 0.0);
638 glVertex3f(x + w, y + h, 0.0);
640 glVertex3f(x + w, y, 0.0);
644 /* the border is not included in the shape rect coords */
645 const gint sb = window_top(win) == window_redir(win) ?
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;
655 glTexCoord2d((GLdouble)xb/w,
657 glVertex3f(x + xb, y + yb, 0.0f);
659 glTexCoord2d((GLdouble)xb/w,
660 (GLdouble)(yb+hb)/h);
661 glVertex3f(x + xb, y + yb + hb, 0.0f);
663 glTexCoord2d((GLdouble)(xb+wb)/w,
664 (GLdouble)(yb+hb)/h);
665 glVertex3f(x + xb + wb, y + yb + hb, 0.0f);
667 glTexCoord2d((GLdouble)(xb+wb)/w,
669 glVertex3f(x + xb + wb, y + yb, 0.0f);
674 if (win->alpha && *win->alpha < 0xffffffff)
675 glColor4f(1.0, 1.0, 1.0, 1.0);
677 if (client && (client->frame->decorations & OB_FRAME_DECOR_BORDER)) {
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,
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,
693 glDisable(GL_TEXTURE_2D);
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);
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);
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);
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);
715 glEnable(GL_TEXTURE_2D);
716 glColor4f(1.0, 1.0, 1.0, 1.0);
719 stacking_iter_free(it);
721 glXSwapBuffers(obt_display, composite_overlay);
724 if (ob_comp_indirect)
730 while ((gler = glGetError()) != GL_NO_ERROR) {
731 printf("gl error %d\n", gler);
737 if (animating) return TRUE;
738 composite_idle_source = 0;
742 static void composite_window_redir(ObWindow *w)
744 if (!composite_enabled) return;
746 if (w->is_redir) return;
748 g_assert(w->gpixmap == None);
749 g_assert(w->pixmap == None);
750 g_assert(w->bound == FALSE);
751 g_assert(w->texture == 0);
753 XCompositeRedirectWindow(obt_display, window_redir(w),
754 CompositeRedirectManual);
756 glGenTextures(1, &w->texture);
761 static void composite_window_unredir(ObWindow *w)
763 if (!w->redir) return;
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);
771 glDeleteTextures(1, &w->texture);
776 composite_window_invalid(w);
779 void composite_window_setup(ObWindow *w)
781 if (w->type == OB_WINDOW_CLASS_PROMPT) return;
784 g_assert(composite_started);
787 obt_display_ignore_errors(TRUE);
788 w->damage = XDamageCreate(obt_display, window_redir(w),
789 XDamageReportRawRectangles);
790 obt_display_ignore_errors(FALSE);
792 composite_window_redir(w);
795 void composite_window_cleanup(ObWindow *w)
797 if (w->type == OB_WINDOW_CLASS_PROMPT) return;
800 g_assert(composite_started);
803 composite_window_unredir(w);
806 XDamageDestroy(obt_display, w->damage);
811 void composite_window_invalid(ObWindow *w)
814 destroy_glx_pixmap(w->gpixmap, w->bound ? w->texture : 0);
819 XFreePixmap(obt_display, w->pixmap);
824 void composite_root_invalid(void)
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);
832 if (px != root_pixmap) {
834 destroy_glx_pixmap(root_gpixmap, root_bound ? root_texture : 0);
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) {}
854 void composite_enable(void)
857 _("Unable to use compositing. Openbox was compiled without it."));