2013-03-15 23:16:23 +08:00
|
|
|
/*
|
|
|
|
* Compton - a compositor for X11
|
|
|
|
*
|
|
|
|
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011-2013, Christopher Jeffrey
|
|
|
|
* See LICENSE for more information.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "opengl.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize OpenGL.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
glx_init(session_t *ps, bool need_render) {
|
|
|
|
bool success = false;
|
|
|
|
XVisualInfo *pvis = NULL;
|
|
|
|
|
|
|
|
// Check for GLX extension
|
|
|
|
if (!ps->glx_exists) {
|
|
|
|
if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
|
|
|
|
ps->glx_exists = true;
|
|
|
|
else {
|
|
|
|
printf_errf("(): No GLX extension.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get XVisualInfo
|
|
|
|
pvis = get_visualinfo_from_visual(ps, ps->vis);
|
|
|
|
if (!pvis) {
|
|
|
|
printf_errf("(): Failed to acquire XVisualInfo for current visual.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the visual is double-buffered
|
|
|
|
if (need_render) {
|
|
|
|
int value = 0;
|
|
|
|
if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
|
|
|
|
printf_errf("(): Root visual is not a GL visual.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value)
|
|
|
|
|| !value) {
|
|
|
|
printf_errf("(): Root visual is not a double buffered GL visual.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure GLX_EXT_texture_from_pixmap exists
|
2013-03-16 22:54:43 +08:00
|
|
|
if (need_render && !glx_hasglxext(ps, "GLX_EXT_texture_from_pixmap"))
|
2013-03-15 23:16:23 +08:00
|
|
|
goto glx_init_end;
|
|
|
|
|
|
|
|
// Get GLX context
|
|
|
|
ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
|
|
|
|
|
|
|
|
if (!ps->glx_context) {
|
|
|
|
printf_errf("(): Failed to get GLX context.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attach GLX context
|
|
|
|
if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
|
|
|
|
printf_errf("(): Failed to attach GLX context.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
|
2013-03-16 22:54:43 +08:00
|
|
|
// Ensure we have a stencil buffer. X Fixes does not guarantee rectangles
|
|
|
|
// in regions don't overlap, so we must use stencil buffer to make sure
|
|
|
|
// we don't paint a region for more than one time, I think?
|
|
|
|
if (need_render) {
|
|
|
|
GLint val = 0;
|
|
|
|
glGetIntegerv(GL_STENCIL_BITS, &val);
|
|
|
|
if (!val) {
|
|
|
|
printf_errf("(): Target window doesn't have stencil buffer.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check GL_ARB_texture_non_power_of_two, requires a GLX context and
|
|
|
|
// must precede FBConfig fetching
|
|
|
|
if (need_render)
|
|
|
|
ps->glx_has_texture_non_power_of_two = glx_hasglext(ps,
|
|
|
|
"GL_ARB_texture_non_power_of_two");
|
|
|
|
|
2013-03-15 23:16:23 +08:00
|
|
|
// Acquire function addresses
|
|
|
|
if (need_render) {
|
|
|
|
ps->glXBindTexImageProc = (f_BindTexImageEXT)
|
|
|
|
glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT");
|
|
|
|
ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT)
|
|
|
|
glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT");
|
|
|
|
if (!ps->glXBindTexImageProc || !ps->glXReleaseTexImageProc) {
|
|
|
|
printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Acquire FBConfigs
|
|
|
|
if (need_render && !glx_update_fbconfig(ps))
|
|
|
|
goto glx_init_end;
|
|
|
|
|
|
|
|
if (need_render) {
|
2013-03-16 22:54:43 +08:00
|
|
|
glx_on_root_change(ps);
|
|
|
|
|
2013-03-15 23:16:23 +08:00
|
|
|
// glEnable(GL_DEPTH_TEST);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
2013-03-16 22:54:43 +08:00
|
|
|
// Initialize stencil buffer
|
|
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
glStencilMask(0x1);
|
|
|
|
glStencilFunc(GL_EQUAL, 0x1, 0x1);
|
|
|
|
|
2013-03-15 23:16:23 +08:00
|
|
|
// Clear screen
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
2013-03-16 22:54:43 +08:00
|
|
|
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// glXSwapBuffers(ps->dpy, get_tgt_window(ps));
|
2013-03-15 23:16:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
success = true;
|
|
|
|
|
|
|
|
glx_init_end:
|
|
|
|
if (pvis)
|
|
|
|
XFree(pvis);
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
glx_destroy(ps);
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy GLX related resources.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
glx_destroy(session_t *ps) {
|
|
|
|
// Free FBConfigs
|
|
|
|
for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) {
|
|
|
|
free(ps->glx_fbconfigs[i]);
|
|
|
|
ps->glx_fbconfigs[i] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destroy GLX context
|
|
|
|
if (ps->glx_context) {
|
|
|
|
glXDestroyContext(ps->dpy, ps->glx_context);
|
|
|
|
ps->glx_context = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback to run on root window size change.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
glx_on_root_change(session_t *ps) {
|
|
|
|
glViewport(0, 0, ps->root_width, ps->root_height);
|
2013-03-16 22:54:43 +08:00
|
|
|
|
|
|
|
// Initialize matrix, copied from dcompmgr
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
glLoadIdentity();
|
|
|
|
glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
glLoadIdentity();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize GLX blur filter.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
glx_init_blur(session_t *ps) {
|
|
|
|
printf_errf("(): Blur on GLX backend isn't implemented yet, sorry.");
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// #ifdef CONFIG_VSYNC_OPENGL_GLSL
|
|
|
|
// static const char *FRAG_SHADER_BLUR = "";
|
|
|
|
// GLuint frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, FRAG_SHADER_BLUR);
|
|
|
|
// if (!frag_shader) {
|
|
|
|
// printf_errf("(): Failed to create fragment shader for blurring.");
|
|
|
|
// return false;
|
|
|
|
// }
|
|
|
|
// #else
|
|
|
|
// printf_errf("(): GLSL support not compiled in. Cannot do blur with GLX backend.");
|
|
|
|
// return false;
|
|
|
|
// #endif
|
2013-03-15 23:16:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Update the FBConfig of given depth.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) {
|
|
|
|
// Make sure the depth is sane
|
|
|
|
if (depth < 0 || depth > OPENGL_MAX_DEPTH)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Compare new FBConfig with current one
|
|
|
|
if (glx_cmp_fbconfig(ps, ps->glx_fbconfigs[depth], pfbcfg) < 0) {
|
|
|
|
#ifdef DEBUG_GLX
|
|
|
|
printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->glx_fbconfigs[depth] ? (unsigned) ps->glx_fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts);
|
|
|
|
#endif
|
|
|
|
if (!ps->glx_fbconfigs[depth]) {
|
|
|
|
ps->glx_fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t));
|
|
|
|
allocchk(ps->glx_fbconfigs[depth]);
|
|
|
|
}
|
|
|
|
(*ps->glx_fbconfigs[depth]) = *pfbcfg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get GLX FBConfigs for all depths.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
glx_update_fbconfig(session_t *ps) {
|
|
|
|
// Acquire all FBConfigs and loop through them
|
|
|
|
int nele = 0;
|
|
|
|
GLXFBConfig* pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele);
|
|
|
|
|
|
|
|
for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) {
|
|
|
|
glx_fbconfig_t fbinfo = {
|
|
|
|
.cfg = *pcur,
|
|
|
|
.texture_fmt = 0,
|
|
|
|
.texture_tgts = 0,
|
|
|
|
.y_inverted = false,
|
|
|
|
};
|
|
|
|
int depth = 0, depth_alpha = 0, val = 0;
|
|
|
|
|
|
|
|
if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth)
|
|
|
|
|| Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) {
|
|
|
|
printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", (int) (pcur - pfbcfgs));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) {
|
|
|
|
printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", (int) (pcur - pfbcfgs));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rgb = false;
|
|
|
|
bool rgba = false;
|
|
|
|
|
|
|
|
if (depth >= 32 && depth_alpha && Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val)
|
|
|
|
rgba = true;
|
|
|
|
|
|
|
|
if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val)
|
|
|
|
rgb = true;
|
|
|
|
|
|
|
|
if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val))
|
|
|
|
fbinfo.y_inverted = val;
|
|
|
|
|
|
|
|
if ((depth - depth_alpha) < 32 && rgb) {
|
|
|
|
fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT;
|
|
|
|
glx_update_fbconfig_bydepth(ps, depth - depth_alpha, &fbinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rgba) {
|
|
|
|
fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT;
|
|
|
|
glx_update_fbconfig_bydepth(ps, depth, &fbinfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pfbcfgs)
|
|
|
|
XFree(pfbcfgs);
|
|
|
|
|
|
|
|
// Sanity checks
|
|
|
|
if (!ps->glx_fbconfigs[ps->depth]) {
|
|
|
|
printf_errf("(): No FBConfig found for default depth %d.", ps->depth);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ps->glx_fbconfigs[32]) {
|
|
|
|
printf_errf("(): No FBConfig found for depth 32. Expect crazy things.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int
|
|
|
|
glx_cmp_fbconfig_cmpattr(session_t *ps,
|
|
|
|
const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b,
|
|
|
|
int attr) {
|
|
|
|
int attr_a = 0, attr_b = 0;
|
|
|
|
|
|
|
|
// TODO: Error checking
|
|
|
|
glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a);
|
|
|
|
glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b);
|
|
|
|
|
|
|
|
return attr_a - attr_b;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Compare two GLX FBConfig's to find the preferred one.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
glx_cmp_fbconfig(session_t *ps,
|
|
|
|
const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) {
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (!pfbc_a)
|
|
|
|
return -1;
|
|
|
|
if (!pfbc_b)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
#define P_CMPATTR_LT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return -result; }
|
|
|
|
#define P_CMPATTR_GT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return result; }
|
|
|
|
|
|
|
|
P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT);
|
|
|
|
P_CMPATTR_LT(GLX_DOUBLEBUFFER);
|
|
|
|
P_CMPATTR_LT(GLX_STENCIL_SIZE);
|
|
|
|
P_CMPATTR_LT(GLX_DEPTH_SIZE);
|
|
|
|
P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Bind a X pixmap to an OpenGL texture.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
|
|
|
|
int width, int height, int depth) {
|
|
|
|
if (depth > OPENGL_MAX_DEPTH) {
|
|
|
|
printf_errf("(%d): Requested depth higher than %d.", depth,
|
|
|
|
OPENGL_MAX_DEPTH);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth];
|
|
|
|
if (!pcfg) {
|
|
|
|
printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const GLenum target = GL_TEXTURE_2D;
|
|
|
|
glx_texture_t *ptex = *pptex;
|
|
|
|
bool need_release = true;
|
|
|
|
|
|
|
|
// Allocate structure
|
|
|
|
if (!ptex) {
|
|
|
|
static const glx_texture_t GLX_TEX_DEF = {
|
|
|
|
.texture = 0,
|
|
|
|
.glpixmap = 0,
|
|
|
|
.pixmap = 0,
|
|
|
|
.width = 0,
|
|
|
|
.height = 0,
|
|
|
|
.depth = 0,
|
|
|
|
.y_inverted = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
ptex = malloc(sizeof(glx_texture_t));
|
|
|
|
allocchk(ptex);
|
|
|
|
memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
|
|
|
|
*pptex = ptex;
|
|
|
|
}
|
|
|
|
|
|
|
|
glEnable(target);
|
|
|
|
|
|
|
|
// Release pixmap if parameters are inconsistent
|
|
|
|
if (ptex->texture && !(width == ptex->width && height == ptex->height
|
|
|
|
&& ptex->pixmap == pixmap && depth == ptex->depth)) {
|
|
|
|
glx_release_pixmap(ps, ptex);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create texture
|
|
|
|
if (!ptex->texture) {
|
|
|
|
need_release = false;
|
|
|
|
|
|
|
|
GLuint texture = 0;
|
|
|
|
glGenTextures(1, &texture);
|
|
|
|
glBindTexture(target, texture);
|
|
|
|
|
|
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
|
|
|
|
glBindTexture(target, 0);
|
|
|
|
|
|
|
|
ptex->texture = texture;
|
|
|
|
ptex->y_inverted = pcfg->y_inverted;
|
|
|
|
}
|
|
|
|
if (!ptex->texture) {
|
|
|
|
printf_errf("(): Failed to allocate texture.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
glBindTexture(target, ptex->texture);
|
|
|
|
|
|
|
|
// Create GLX pixmap
|
|
|
|
if (!ptex->glpixmap) {
|
|
|
|
need_release = false;
|
|
|
|
|
2013-03-16 22:54:43 +08:00
|
|
|
// Determine texture target, copied from compiz
|
|
|
|
GLint tex_tgt = 0;
|
|
|
|
if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts
|
|
|
|
&& ps->glx_has_texture_non_power_of_two)
|
|
|
|
tex_tgt = GLX_TEXTURE_2D_EXT;
|
|
|
|
else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts)
|
|
|
|
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
|
|
|
else if (!(GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts))
|
|
|
|
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
|
|
|
else
|
|
|
|
tex_tgt = GLX_TEXTURE_2D_EXT;
|
|
|
|
|
2013-03-15 23:16:23 +08:00
|
|
|
#ifdef DEBUG_GLX
|
2013-03-16 22:54:43 +08:00
|
|
|
printf_dbgf("(): depth %d, tgt %#x, rgba %d\n", depth, tex_tgt,
|
|
|
|
(GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt));
|
2013-03-15 23:16:23 +08:00
|
|
|
#endif
|
|
|
|
|
2013-03-16 22:54:43 +08:00
|
|
|
GLint attrs[] = {
|
2013-03-15 23:16:23 +08:00
|
|
|
GLX_TEXTURE_FORMAT_EXT,
|
|
|
|
pcfg->texture_fmt,
|
2013-03-16 22:54:43 +08:00
|
|
|
GLX_TEXTURE_TARGET_EXT,
|
|
|
|
tex_tgt,
|
2013-03-15 23:16:23 +08:00
|
|
|
0,
|
|
|
|
};
|
|
|
|
|
|
|
|
ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs);
|
|
|
|
}
|
|
|
|
if (!ptex->glpixmap) {
|
|
|
|
printf_errf("(): Failed to allocate GLX pixmap.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The specification requires rebinding whenever the content changes...
|
|
|
|
// We can't follow this, too slow.
|
|
|
|
if (need_release)
|
|
|
|
ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
|
|
|
|
|
|
|
ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
glBindTexture(target, 0);
|
|
|
|
glDisable(target);
|
|
|
|
|
|
|
|
ptex->width = width;
|
|
|
|
ptex->height = height;
|
|
|
|
ptex->depth = depth;
|
|
|
|
ptex->pixmap = pixmap;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Release binding of a texture.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
|
|
|
|
// Release binding
|
|
|
|
if (ptex->glpixmap && ptex->texture) {
|
|
|
|
glBindTexture(GL_TEXTURE_2D, ptex->texture);
|
|
|
|
ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free GLX Pixmap
|
|
|
|
if (ptex->glpixmap) {
|
|
|
|
glXDestroyPixmap(ps->dpy, ptex->glpixmap);
|
|
|
|
ptex->glpixmap = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-16 22:54:43 +08:00
|
|
|
/**
|
|
|
|
* Set clipping region on the target window.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
glx_set_clip(session_t *ps, XserverRegion reg) {
|
|
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
|
|
|
|
|
if (reg) {
|
|
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);
|
|
|
|
|
|
|
|
int nrects = 0;
|
|
|
|
XRectangle *rects = XFixesFetchRegion(ps->dpy, reg, &nrects);
|
|
|
|
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
|
|
|
|
for (int i = 0; i < nrects; ++i) {
|
|
|
|
GLint rx = rects[i].x;
|
|
|
|
GLint ry = rects[i].y;
|
|
|
|
GLint rxe = rx + rects[i].width;
|
|
|
|
GLint rye = ry + rects[i].height;
|
|
|
|
GLint z = 0;
|
|
|
|
|
|
|
|
#ifdef DEBUG_GLX
|
|
|
|
printf_dbgf("(): Rect %d: %f, %f, %f, %f\n", i, rx, ry, rxe, rye);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
glVertex3i(rx, ry, z);
|
|
|
|
glVertex3i(rxe, ry, z);
|
|
|
|
glVertex3i(rxe, rye, z);
|
|
|
|
glVertex3i(rx, rye, z);
|
|
|
|
}
|
|
|
|
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
if (rects)
|
|
|
|
XFree(rects);
|
|
|
|
|
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-15 23:16:23 +08:00
|
|
|
/**
|
|
|
|
* @brief Render a region with texture data.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
glx_render(session_t *ps, const glx_texture_t *ptex,
|
|
|
|
int x, int y, int dx, int dy, int width, int height, int z,
|
|
|
|
double opacity, bool neg, XserverRegion reg_tgt) {
|
|
|
|
if (!ptex || !ptex->texture) {
|
|
|
|
printf_errf("(): Missing texture.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable blending if needed
|
|
|
|
if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT ==
|
|
|
|
ps->glx_fbconfigs[ptex->depth]->texture_fmt) {
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
// Needed for handling opacity of ARGB texture
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
glColor4f(1.0f, 1.0f, 1.0f, opacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Color negation
|
|
|
|
if (neg) {
|
|
|
|
// Simple color negation
|
|
|
|
if (!glIsEnabled(GL_BLEND)) {
|
|
|
|
glEnable(GL_COLOR_LOGIC_OP);
|
|
|
|
glLogicOp(GL_COPY_INVERTED);
|
|
|
|
}
|
|
|
|
// Blending color negation
|
|
|
|
else {
|
|
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
|
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
|
|
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
XserverRegion reg_new = None;
|
|
|
|
|
|
|
|
XRectangle rec_all = {
|
|
|
|
.x = dx,
|
|
|
|
.y = dy,
|
|
|
|
.width = width,
|
|
|
|
.height = height
|
|
|
|
};
|
|
|
|
|
|
|
|
XRectangle *rects = &rec_all;
|
|
|
|
int nrects = 1;
|
|
|
|
|
|
|
|
#ifdef DEBUG_GLX
|
2013-03-16 22:54:43 +08:00
|
|
|
printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z);
|
2013-03-15 23:16:23 +08:00
|
|
|
#endif
|
|
|
|
|
2013-03-16 22:54:43 +08:00
|
|
|
/*
|
2013-03-15 23:16:23 +08:00
|
|
|
if (reg_tgt) {
|
|
|
|
reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1);
|
|
|
|
XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt);
|
|
|
|
|
|
|
|
nrects = 0;
|
|
|
|
rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects);
|
|
|
|
}
|
2013-03-16 22:54:43 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, ptex->texture);
|
|
|
|
|
|
|
|
glBegin(GL_QUADS);
|
2013-03-15 23:16:23 +08:00
|
|
|
|
|
|
|
for (int i = 0; i < nrects; ++i) {
|
|
|
|
GLfloat rx = (double) (rects[i].x - dx + x) / ptex->width;
|
|
|
|
GLfloat ry = (double) (rects[i].y - dy + y) / ptex->height;
|
|
|
|
GLfloat rxe = rx + (double) rects[i].width / ptex->width;
|
|
|
|
GLfloat rye = ry + (double) rects[i].height / ptex->height;
|
|
|
|
GLint rdx = rects[i].x;
|
|
|
|
GLint rdy = rects[i].y;
|
|
|
|
GLint rdxe = rdx + rects[i].width;
|
|
|
|
GLint rdye = rdy + rects[i].height;
|
|
|
|
|
|
|
|
// Invert Y if needed, this may not work as expected, though. I don't
|
|
|
|
// have such a FBConfig to test with.
|
|
|
|
if (!ptex->y_inverted) {
|
|
|
|
ry = 1.0 - ry;
|
|
|
|
rye = 1.0 - rye;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_GLX
|
|
|
|
printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
glTexCoord2f(rx, ry);
|
|
|
|
glVertex3i(rdx, rdy, z);
|
|
|
|
|
|
|
|
glTexCoord2f(rxe, ry);
|
|
|
|
glVertex3i(rdxe, rdy, z);
|
|
|
|
|
|
|
|
glTexCoord2f(rxe, rye);
|
|
|
|
glVertex3i(rdxe, rdye, z);
|
|
|
|
|
|
|
|
glTexCoord2f(rx, rye);
|
|
|
|
glVertex3i(rdx, rdye, z);
|
|
|
|
}
|
|
|
|
|
2013-03-16 22:54:43 +08:00
|
|
|
glEnd();
|
|
|
|
|
2013-03-15 23:16:23 +08:00
|
|
|
if (rects && rects != &rec_all)
|
|
|
|
XFree(rects);
|
|
|
|
free_region(ps, ®_new);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDisable(GL_COLOR_LOGIC_OP);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-03-16 22:54:43 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_VSYNC_OPENGL_GLSL
|
|
|
|
GLuint
|
|
|
|
glx_create_shader(GLenum shader_type, const char *shader_str) {
|
|
|
|
bool success = false;
|
|
|
|
GLuint shader = glCreateShader(shader_type);
|
|
|
|
if (!shader) {
|
|
|
|
printf_errf("(): Failed to create shader with type %d.", shader_type);
|
|
|
|
goto glx_create_shader_end;
|
|
|
|
}
|
|
|
|
glShaderSource(shader, 1, &shader_str, NULL);
|
|
|
|
glCompileShader(shader);
|
|
|
|
|
|
|
|
// Get shader status
|
|
|
|
{
|
|
|
|
GLint status = GL_FALSE;
|
|
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
|
|
|
if (GL_FALSE == status) {
|
|
|
|
GLint log_len = 0;
|
|
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
|
|
|
|
if (log_len) {
|
|
|
|
char log[log_len + 1];
|
|
|
|
glGetShaderInfoLog(shader, log_len, NULL, log);
|
|
|
|
printf_errf("(): Failed to compile shader with type %d: %s",
|
|
|
|
shader_type, log);
|
|
|
|
}
|
|
|
|
goto glx_create_shader_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
success = true;
|
|
|
|
|
|
|
|
glx_create_shader_end:
|
|
|
|
if (shader && !success) {
|
|
|
|
glDeleteShader(shader);
|
|
|
|
shader = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return shader;
|
|
|
|
}
|
|
|
|
|
|
|
|
GLuint
|
|
|
|
glx_create_program(GLenum shader_type, const GLuint * const shaders,
|
|
|
|
int nshaders) {
|
|
|
|
bool success = false;
|
|
|
|
GLuint program = glCreateProgram();
|
|
|
|
if (!program) {
|
|
|
|
printf_errf("(): Failed to create program.");
|
|
|
|
goto glx_create_program_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < nshaders; ++i)
|
|
|
|
glAttachShader(program, shaders[i]);
|
|
|
|
glLinkProgram(program);
|
|
|
|
|
|
|
|
// Get program status
|
|
|
|
{
|
|
|
|
GLint status = GL_FALSE;
|
|
|
|
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
|
|
|
if (GL_FALSE == status) {
|
|
|
|
GLint log_len = 0;
|
|
|
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
|
|
|
|
if (log_len) {
|
|
|
|
char log[log_len + 1];
|
|
|
|
glGetProgramInfoLog(program, log_len, NULL, log);
|
|
|
|
printf_errf("(): Failed to link program: %s", log);
|
|
|
|
}
|
|
|
|
goto glx_create_program_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
success = true;
|
|
|
|
|
|
|
|
glx_create_program_end:
|
|
|
|
if (program && !success) {
|
|
|
|
glDeleteProgram(program);
|
|
|
|
program = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return program;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|