diff options
| author | allusive-dev <[email protected]> | 2023-09-19 17:46:20 +1000 |
|---|---|---|
| committer | allusive-dev <[email protected]> | 2023-09-19 17:46:20 +1000 |
| commit | 5650d887357bf2a3fac8c5fd4f467bf8795b5fc4 (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /src/opengl.c | |
| parent | Update picom.sample.conf (diff) | |
| download | compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.tar.xz compfy-5650d887357bf2a3fac8c5fd4f467bf8795b5fc4.zip | |
reset
Diffstat (limited to 'src/opengl.c')
| -rw-r--r-- | src/opengl.c | 1514 |
1 files changed, 0 insertions, 1514 deletions
diff --git a/src/opengl.c b/src/opengl.c deleted file mode 100644 index 5d2d66c..0000000 --- a/src/opengl.c +++ /dev/null @@ -1,1514 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * Compton - a compositor for X11 - * - * Based on `xcompmgr` - Copyright (c) 2003, Keith Packard - * - * Copyright (c) 2011-2013, Christopher Jeffrey - * See LICENSE-mit for more information. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <xcb/render.h> -#include <xcb/xcb.h> - -#include "backend/gl/gl_common.h" -#include "backend/gl/glx.h" -#include "common.h" -#include "compiler.h" -#include "config.h" -#include "kernel.h" -#include "log.h" -#include "region.h" -#include "string_utils.h" -#include "uthash_extra.h" -#include "utils.h" -#include "win.h" - -#include "opengl.h" - -#ifndef GL_TEXTURE_RECTANGLE -#define GL_TEXTURE_RECTANGLE 0x84F5 -#endif - -static inline XVisualInfo *get_visualinfo_from_visual(session_t *ps, xcb_visualid_t visual) { - XVisualInfo vreq = {.visualid = visual}; - int nitems = 0; - - return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems); -} - -/** - * 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) { - log_error("No GLX extension."); - goto glx_init_end; - } - - // Get XVisualInfo - pvis = get_visualinfo_from_visual(ps, ps->vis); - if (!pvis) { - log_error("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) { - log_error("Root visual is not a GL visual."); - goto glx_init_end; - } - - if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) { - log_error("Root visual is not a double buffered GL visual."); - goto glx_init_end; - } - } - - // Ensure GLX_EXT_texture_from_pixmap exists - if (need_render && !glxext.has_GLX_EXT_texture_from_pixmap) - goto glx_init_end; - - // Initialize GLX data structure - if (!ps->psglx) { - static const glx_session_t CGLX_SESSION_DEF = CGLX_SESSION_INIT; - ps->psglx = cmalloc(glx_session_t); - memcpy(ps->psglx, &CGLX_SESSION_DEF, sizeof(glx_session_t)); - - // +1 for the zero terminator - ps->psglx->blur_passes = ccalloc(ps->o.blur_kernel_count, glx_blur_pass_t); - - for (int i = 0; i < ps->o.blur_kernel_count; ++i) { - glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; - ppass->unifm_factor_center = -1; - ppass->unifm_offset_x = -1; - ppass->unifm_offset_y = -1; - } - - ps->psglx->round_passes = ccalloc(1, glx_round_pass_t); - glx_round_pass_t *ppass = ps->psglx->round_passes; - ppass->unifm_radius = -1; - ppass->unifm_texcoord = -1; - ppass->unifm_texsize = -1; - ppass->unifm_borderw = -1; - ppass->unifm_borderc = -1; - ppass->unifm_resolution = -1; - ppass->unifm_tex_scr = -1; - } - - glx_session_t *psglx = ps->psglx; - - if (!psglx->context) { - // Get GLX context -#ifndef DEBUG_GLX_DEBUG_CONTEXT - psglx->context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE); -#else - { - GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis); - if (!fbconfig) { - log_error("Failed to get GLXFBConfig for root visual " - "%#lx.", - pvis->visualid); - goto glx_init_end; - } - - f_glXCreateContextAttribsARB p_glXCreateContextAttribsARB = - (f_glXCreateContextAttribsARB)glXGetProcAddress( - (const GLubyte *)"glXCreateContextAttribsARB"); - if (!p_glXCreateContextAttribsARB) { - log_error("Failed to get glXCreateContextAttribsARB()."); - goto glx_init_end; - } - - static const int attrib_list[] = { - GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None}; - psglx->context = p_glXCreateContextAttribsARB( - ps->dpy, fbconfig, NULL, GL_TRUE, attrib_list); - } -#endif - - if (!psglx->context) { - log_error("Failed to get GLX context."); - goto glx_init_end; - } - - // Attach GLX context - if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), psglx->context)) { - log_error("Failed to attach GLX context."); - goto glx_init_end; - } - -#ifdef DEBUG_GLX_DEBUG_CONTEXT - { - f_DebugMessageCallback p_DebugMessageCallback = - (f_DebugMessageCallback)glXGetProcAddress( - (const GLubyte *)"glDebugMessageCallback"); - if (!p_DebugMessageCallback) { - log_error("Failed to get glDebugMessageCallback(0."); - goto glx_init_end; - } - p_DebugMessageCallback(glx_debug_msg_callback, ps); - } -#endif - } - - // 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 && !ps->o.glx_no_stencil) { - GLint val = 0; - glGetIntegerv(GL_STENCIL_BITS, &val); - if (!val) { - log_error("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) - psglx->has_texture_non_power_of_two = - gl_has_extension("GL_ARB_texture_non_power_of_two"); - - // Render preparations - if (need_render) { - glx_on_root_change(ps); - - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glDisable(GL_BLEND); - - if (!ps->o.glx_no_stencil) { - // Initialize stencil buffer - glClear(GL_STENCIL_BUFFER_BIT); - glDisable(GL_STENCIL_TEST); - glStencilMask(0x1); - glStencilFunc(GL_EQUAL, 0x1, 0x1); - } - - // Clear screen - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // glXSwapBuffers(ps->dpy, get_tgt_window(ps)); - } - - success = true; - -glx_init_end: - XFree(pvis); - - if (!success) - glx_destroy(ps); - - return success; -} - -static void glx_free_prog_main(glx_prog_main_t *pprogram) { - if (!pprogram) - return; - if (pprogram->prog) { - glDeleteProgram(pprogram->prog); - pprogram->prog = 0; - } - pprogram->unifm_opacity = -1; - pprogram->unifm_invert_color = -1; - pprogram->unifm_tex = -1; -} - -/** - * Destroy GLX related resources. - */ -void glx_destroy(session_t *ps) { - if (!ps->psglx) - return; - - // Free all GLX resources of windows - win_stack_foreach_managed(w, &ps->window_stack) { - free_win_res_glx(ps, w); - } - - // Free GLSL shaders/programs - for (int i = 0; i < ps->o.blur_kernel_count; ++i) { - glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; - if (ppass->frag_shader) { - glDeleteShader(ppass->frag_shader); - } - if (ppass->prog) { - glDeleteProgram(ppass->prog); - } - } - free(ps->psglx->blur_passes); - - glx_round_pass_t *ppass = ps->psglx->round_passes; - if (ppass->frag_shader) { - glDeleteShader(ppass->frag_shader); - } - if (ppass->prog) { - glDeleteProgram(ppass->prog); - } - free(ps->psglx->round_passes); - - glx_free_prog_main(&ps->glx_prog_win); - - gl_check_err(); - - // Destroy GLX context - if (ps->psglx->context) { - glXMakeCurrent(ps->dpy, None, NULL); - glXDestroyContext(ps->dpy, ps->psglx->context); - ps->psglx->context = NULL; - } - - free(ps->psglx); - ps->psglx = NULL; - ps->argb_fbconfig = 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); - - // Initialize matrix, copied from dcompmgr - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, ps->root_width, 0, ps->root_height, -1000.0, 1000.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); -} - -/** - * Initialize GLX blur filter. - */ -bool glx_init_blur(session_t *ps) { - assert(ps->o.blur_kernel_count > 0); - assert(ps->o.blur_kerns); - assert(ps->o.blur_kerns[0]); - - // Allocate PBO if more than one blur kernel is present - if (ps->o.blur_kernel_count > 1) { - // Try to generate a framebuffer - GLuint fbo = 0; - glGenFramebuffers(1, &fbo); - if (!fbo) { - log_error("Failed to generate Framebuffer. Cannot do multi-pass " - "blur with GLX" - " backend."); - return false; - } - glDeleteFramebuffers(1, &fbo); - } - - { - char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL)); - // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane - // Thanks to hiciu for reporting. - setlocale(LC_NUMERIC, "C"); - - static const char *FRAG_SHADER_BLUR_PREFIX = - "#version 110\n" - "%s" - "uniform float offset_x;\n" - "uniform float offset_y;\n" - "uniform float factor_center;\n" - "uniform %s tex_scr;\n" - "\n" - "void main() {\n" - " vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);\n"; - static const char *FRAG_SHADER_BLUR_ADD = - " sum += float(%.7g) * %s(tex_scr, vec2(gl_TexCoord[0].x + offset_x " - "* float(%d), gl_TexCoord[0].y + offset_y * float(%d)));\n"; - static const char *FRAG_SHADER_BLUR_SUFFIX = - " sum += %s(tex_scr, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * " - "factor_center;\n" - " gl_FragColor = sum / (factor_center + float(%.7g));\n" - "}\n"; - - const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; - const char *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D"); - const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D"); - const char *shader_add = FRAG_SHADER_BLUR_ADD; - char *extension = NULL; - if (use_texture_rect) { - mstrextend(&extension, "#extension GL_ARB_texture_rectangle : " - "require\n"); - } - if (!extension) { - extension = strdup(""); - } - - for (int i = 0; i < ps->o.blur_kernel_count; ++i) { - auto kern = ps->o.blur_kerns[i]; - glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; - - // Build shader - int width = kern->w, height = kern->h; - int nele = width * height - 1; - assert(nele >= 0); - auto len = - strlen(FRAG_SHADER_BLUR_PREFIX) + strlen(sampler_type) + - strlen(extension) + - (strlen(shader_add) + strlen(texture_func) + 42) * (uint)nele + - strlen(FRAG_SHADER_BLUR_SUFFIX) + strlen(texture_func) + 12 + 1; - char *shader_str = ccalloc(len, char); - char *pc = shader_str; - sprintf(pc, FRAG_SHADER_BLUR_PREFIX, extension, sampler_type); - pc += strlen(pc); - assert(strlen(shader_str) < len); - - double sum = 0.0; - for (int j = 0; j < height; ++j) { - for (int k = 0; k < width; ++k) { - if (height / 2 == j && width / 2 == k) - continue; - double val = kern->data[j * width + k]; - if (val == 0) { - continue; - } - sum += val; - sprintf(pc, shader_add, val, texture_func, - k - width / 2, j - height / 2); - pc += strlen(pc); - assert(strlen(shader_str) < len); - } - } - - sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum); - assert(strlen(shader_str) < len); - ppass->frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, shader_str); - free(shader_str); - - if (!ppass->frag_shader) { - log_error("Failed to create fragment shader %d.", i); - free(extension); - free(lc_numeric_old); - return false; - } - - // Build program - ppass->prog = gl_create_program(&ppass->frag_shader, 1); - if (!ppass->prog) { - log_error("Failed to create GLSL program."); - free(extension); - free(lc_numeric_old); - return false; - } - - // Get uniform addresses -#define P_GET_UNIFM_LOC(name, target) \ - { \ - ppass->target = glGetUniformLocation(ppass->prog, name); \ - if (ppass->target < 0) { \ - log_error("Failed to get location of %d-th uniform '" name \ - "'. Might be troublesome.", \ - i); \ - } \ - } - - P_GET_UNIFM_LOC("factor_center", unifm_factor_center); - P_GET_UNIFM_LOC("offset_x", unifm_offset_x); - P_GET_UNIFM_LOC("offset_y", unifm_offset_y); - -#undef P_GET_UNIFM_LOC - } - free(extension); - - // Restore LC_NUMERIC - setlocale(LC_NUMERIC, lc_numeric_old); - free(lc_numeric_old); - } - - gl_check_err(); - - return true; -} - -/** - * Initialize GLX rounded corners filter. - */ -bool glx_init_rounded_corners(session_t *ps) { - char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL)); - // Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane - // Thanks to hiciu for reporting. - setlocale(LC_NUMERIC, "C"); - - static const char *FRAG_SHADER = - "#version 110\n" - "%s" // extensions - "uniform float u_radius;\n" - "uniform float u_borderw;\n" - "uniform vec4 u_borderc;\n" - "uniform vec2 u_texcoord;\n" - "uniform vec2 u_texsize;\n" - "uniform vec2 u_resolution;\n" - "uniform %s tex_scr;\n" // sampler2D | sampler2DRect - "\n" - "// https://www.shadertoy.com/view/ltS3zW\n" - "float RectSDF(vec2 p, vec2 b, float r) {\n" - " vec2 d = abs(p) - b + vec2(r);\n" - " return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;\n" - "}\n\n" - "void main()\n" - "{\n" - " vec2 coord = vec2(u_texcoord.x, " - "u_resolution.y-u_texsize.y-u_texcoord.y);\n" - " vec4 u_v4WndBgColor = %s(tex_scr, vec2(gl_TexCoord[0].st));\n" - " float u_fRadiusPx = u_radius;\n" - " float u_fHalfBorderThickness = u_borderw / 2.0;\n" - " vec4 u_v4BorderColor = u_borderc;\n" - " vec4 u_v4FillColor = vec4(0.0, 0.0, 0.0, 0.0);\n" - " vec4 v4FromColor = u_v4BorderColor; //Always the border " - "color. If no border, this still should be set\n" - " vec4 v4ToColor = u_v4WndBgColor; //Outside color is the " - "background texture\n" - "\n" - " vec2 u_v2HalfShapeSizePx = u_texsize/2.0 - " - "vec2(u_fHalfBorderThickness);\n" - " vec2 v_v2CenteredPos = (gl_FragCoord.xy - u_texsize.xy / 2.0 - " - "coord);\n" - "\n" - " float fDist = RectSDF(v_v2CenteredPos, u_v2HalfShapeSizePx, " - "u_fRadiusPx - u_fHalfBorderThickness);\n" - " if (u_fHalfBorderThickness > 0.0) {\n" - " if (fDist < 0.0) {\n" - " v4ToColor = u_v4FillColor;\n" - " }\n" - " fDist = abs(fDist) - u_fHalfBorderThickness;\n" - " } else {\n" - " v4FromColor = u_v4FillColor;\n" - " }\n" - " float fBlendAmount = smoothstep(-1.0, 1.0, fDist);\n" - " vec4 c = mix(v4FromColor, v4ToColor, fBlendAmount);\n" - "\n" - " // final color\n" - " gl_FragColor = c;\n" - "\n" - "}\n"; - - const bool use_texture_rect = !ps->psglx->has_texture_non_power_of_two; - const char *sampler_type = (use_texture_rect ? "sampler2DRect" : "sampler2D"); - const char *texture_func = (use_texture_rect ? "texture2DRect" : "texture2D"); - char *extension = NULL; - if (use_texture_rect) { - mstrextend(&extension, "#extension GL_ARB_texture_rectangle : " - "require\n"); - } - if (!extension) { - extension = strdup(""); - } - - bool success = false; - // Build rounded corners shader - auto ppass = ps->psglx->round_passes; - auto len = strlen(FRAG_SHADER) + strlen(extension) + strlen(sampler_type) + - strlen(texture_func) + 1; - char *shader_str = ccalloc(len, char); - - sprintf(shader_str, FRAG_SHADER, extension, sampler_type, texture_func); - assert(strlen(shader_str) < len); - - log_debug("Generated rounded corners shader:\n%s\n", shader_str); - - ppass->frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, shader_str); - free(shader_str); - - if (!ppass->frag_shader) { - log_error("Failed to create rounded corners fragment shader."); - goto out; - } - - // Build program - ppass->prog = gl_create_program(&ppass->frag_shader, 1); - if (!ppass->prog) { - log_error("Failed to create GLSL program."); - goto out; - } - - // Get uniform addresses -#define P_GET_UNIFM_LOC(name, target) \ - { \ - ppass->target = glGetUniformLocation(ppass->prog, name); \ - if (ppass->target < 0) { \ - log_debug("Failed to get location of rounded corners uniform " \ - "'" name "'. Might be troublesome."); \ - } \ - } - P_GET_UNIFM_LOC("u_radius", unifm_radius); - P_GET_UNIFM_LOC("u_texcoord", unifm_texcoord); - P_GET_UNIFM_LOC("u_texsize", unifm_texsize); - P_GET_UNIFM_LOC("u_borderw", unifm_borderw); - P_GET_UNIFM_LOC("u_borderc", unifm_borderc); - P_GET_UNIFM_LOC("u_resolution", unifm_resolution); - P_GET_UNIFM_LOC("tex_scr", unifm_tex_scr); -#undef P_GET_UNIFM_LOC - - success = true; - -out: - free(extension); - - // Restore LC_NUMERIC - setlocale(LC_NUMERIC, lc_numeric_old); - free(lc_numeric_old); - - gl_check_err(); - - return success; -} - -/** - * Load a GLSL main program from shader strings. - */ -bool glx_load_prog_main(const char *vshader_str, const char *fshader_str, - glx_prog_main_t *pprogram) { - assert(pprogram); - - // Build program - pprogram->prog = gl_create_program_from_str(vshader_str, fshader_str); - if (!pprogram->prog) { - log_error("Failed to create GLSL program."); - return false; - } - - // Get uniform addresses -#define P_GET_UNIFM_LOC(name, target) \ - { \ - pprogram->target = glGetUniformLocation(pprogram->prog, name); \ - if (pprogram->target < 0) { \ - log_error("Failed to get location of uniform '" name \ - "'. Might be troublesome."); \ - } \ - } - P_GET_UNIFM_LOC("opacity", unifm_opacity); - P_GET_UNIFM_LOC("invert_color", unifm_invert_color); - P_GET_UNIFM_LOC("tex", unifm_tex); - P_GET_UNIFM_LOC("time", unifm_time); -#undef P_GET_UNIFM_LOC - - gl_check_err(); - - return true; -} - -static inline void glx_copy_region_to_tex(session_t *ps, GLenum tex_tgt, int basex, - int basey, int dx, int dy, int width, int height) { - if (width > 0 && height > 0) { - glCopyTexSubImage2D(tex_tgt, 0, dx - basex, dy - basey, dx, - ps->root_height - dy - height, width, height); - } -} - -static inline GLuint glx_gen_texture(GLenum tex_tgt, int width, int height) { - GLuint tex = 0; - glGenTextures(1, &tex); - if (!tex) { - return 0; - } - glEnable(tex_tgt); - glBindTexture(tex_tgt, tex); - glTexParameteri(tex_tgt, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(tex_tgt, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(tex_tgt, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(tex_tgt, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); - glBindTexture(tex_tgt, 0); - - return tex; -} - -/** - * Bind an OpenGL texture and fill it with pixel data from back buffer - */ -bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, int y, - int width, int height) { - if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) { - return true; - } - - glx_texture_t *ptex = *pptex; - - // log_trace("Copying xy(%d %d) wh(%d %d) ptex(%p)", x, y, width, height, ptex); - - // Release texture if parameters are inconsistent - if (ptex && ptex->texture && (ptex->width != width || ptex->height != height)) { - free_texture(ps, &ptex); - } - - // Allocate structure - if (!ptex) { - ptex = ccalloc(1, glx_texture_t); - *pptex = ptex; - - ptex->width = width; - ptex->height = height; - ptex->target = GL_TEXTURE_RECTANGLE; - if (ps->psglx->has_texture_non_power_of_two) { - ptex->target = GL_TEXTURE_2D; - } - } - - // Create texture - if (!ptex->texture) { - ptex->texture = glx_gen_texture(ptex->target, width, height); - } - if (!ptex->texture) { - log_error("Failed to allocate texture."); - return false; - } - - // Read destination pixels into a texture - glEnable(ptex->target); - glBindTexture(ptex->target, ptex->texture); - if (width > 0 && height > 0) { - glx_copy_region_to_tex(ps, ptex->target, x, y, x, y, width, height); - } - - // Cleanup - glBindTexture(ptex->target, 0); - glDisable(ptex->target); - - gl_check_err(); - - return true; -} - -/** - * Bind a X pixmap to an OpenGL texture. - */ -bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, int width, - int height, bool repeat, const struct glx_fbconfig_info *fbcfg) { - if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) - return true; - - if (!pixmap) { - log_error("Binding to an empty pixmap %#010x. This can't work.", pixmap); - return false; - } - - assert(fbcfg); - glx_texture_t *ptex = *pptex; - bool need_release = true; - - // Release pixmap if parameters are inconsistent - if (ptex && ptex->texture && ptex->pixmap != pixmap) { - glx_release_pixmap(ps, ptex); - } - - // Allocate structure - if (!ptex) { - static const glx_texture_t GLX_TEX_DEF = { - .texture = 0, - .glpixmap = 0, - .pixmap = 0, - .target = 0, - .width = 0, - .height = 0, - .y_inverted = false, - }; - - ptex = cmalloc(glx_texture_t); - memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t)); - *pptex = ptex; - } - - // Create GLX pixmap - int depth = 0; - if (!ptex->glpixmap) { - need_release = false; - - // Retrieve pixmap parameters, if they aren't provided - if (!width || !height) { - auto r = xcb_get_geometry_reply( - ps->c, xcb_get_geometry(ps->c, pixmap), NULL); - if (!r) { - log_error("Failed to query info of pixmap %#010x.", pixmap); - return false; - } - if (r->depth > OPENGL_MAX_DEPTH) { - log_error("Requested depth %d higher than %d.", depth, - OPENGL_MAX_DEPTH); - return false; - } - depth = r->depth; - width = r->width; - height = r->height; - free(r); - } - - // Determine texture target, copied from compiz - // The assumption we made here is the target never changes based on any - // pixmap-specific parameters, and this may change in the future - GLenum tex_tgt = 0; - if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts && - ps->psglx->has_texture_non_power_of_two) - tex_tgt = GLX_TEXTURE_2D_EXT; - else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) - tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; - else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts)) - tex_tgt = GLX_TEXTURE_RECTANGLE_EXT; - else - tex_tgt = GLX_TEXTURE_2D_EXT; - - log_debug("depth %d, tgt %#x, rgba %d", depth, tex_tgt, - (GLX_TEXTURE_FORMAT_RGBA_EXT == fbcfg->texture_fmt)); - - GLint attrs[] = { - GLX_TEXTURE_FORMAT_EXT, - fbcfg->texture_fmt, - GLX_TEXTURE_TARGET_EXT, - (GLint)tex_tgt, - 0, - }; - - ptex->glpixmap = glXCreatePixmap(ps->dpy, fbcfg->cfg, pixmap, attrs); - ptex->pixmap = pixmap; - ptex->target = - (GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE); - ptex->width = width; - ptex->height = height; - ptex->y_inverted = fbcfg->y_inverted; - } - if (!ptex->glpixmap) { - log_error("Failed to allocate GLX pixmap."); - return false; - } - - glEnable(ptex->target); - - // Create texture - if (!ptex->texture) { - need_release = false; - - GLuint texture = 0; - glGenTextures(1, &texture); - glBindTexture(ptex->target, texture); - - glTexParameteri(ptex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(ptex->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - if (repeat) { - glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_REPEAT); - } else { - glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - - glBindTexture(ptex->target, 0); - - ptex->texture = texture; - } - if (!ptex->texture) { - log_error("Failed to allocate texture."); - return false; - } - - glBindTexture(ptex->target, ptex->texture); - - // The specification requires rebinding whenever the content changes... - // We can't follow this, too slow. - if (need_release) - glXReleaseTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); - - glXBindTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL); - - // Cleanup - glBindTexture(ptex->target, 0); - glDisable(ptex->target); - - gl_check_err(); - - 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(ptex->target, ptex->texture); - glXReleaseTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT); - glBindTexture(ptex->target, 0); - } - - // Free GLX Pixmap - if (ptex->glpixmap) { - glXDestroyPixmap(ps->dpy, ptex->glpixmap); - ptex->glpixmap = 0; - } - - gl_check_err(); -} - -/** - * Set clipping region on the target window. - */ -void glx_set_clip(session_t *ps, const region_t *reg) { - // Quit if we aren't using stencils - if (ps->o.glx_no_stencil) - return; - - glDisable(GL_STENCIL_TEST); - glDisable(GL_SCISSOR_TEST); - - if (!reg) - return; - - int nrects; - const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects); - - if (nrects == 1) { - glEnable(GL_SCISSOR_TEST); - glScissor(rects[0].x1, ps->root_height - rects[0].y2, - rects[0].x2 - rects[0].x1, rects[0].y2 - rects[0].y1); - } - - gl_check_err(); -} - -#define P_PAINTREG_START(var) \ - region_t reg_new; \ - int nrects; \ - const rect_t *rects; \ - assert(width >= 0 && height >= 0); \ - pixman_region32_init_rect(®_new, dx, dy, (uint)width, (uint)height); \ - pixman_region32_intersect(®_new, ®_new, (region_t *)reg_tgt); \ - rects = pixman_region32_rectangles(®_new, &nrects); \ - glBegin(GL_QUADS); \ - \ - for (int ri = 0; ri < nrects; ++ri) { \ - rect_t var = rects[ri]; - -#define P_PAINTREG_END() \ - } \ - glEnd(); \ - \ - pixman_region32_fini(®_new); - -/** - * Blur contents in a particular region. - * - * XXX seems to be way to complex for what it does - */ -bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, - GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc) { - assert(ps->psglx->blur_passes[0].prog); - const bool more_passes = ps->o.blur_kernel_count > 1; - const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST); - const bool have_stencil = glIsEnabled(GL_STENCIL_TEST); - bool ret = false; - - // Calculate copy region size - glx_blur_cache_t ibc = {.width = 0, .height = 0}; - if (!pbc) - pbc = &ibc; - - int mdx = dx, mdy = dy, mwidth = width, mheight = height; - // log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight); - - /* - if (ps->o.resize_damage > 0) { - int inc_x = 0, inc_y = 0; - for (int i = 0; i < MAX_BLUR_PASS; ++i) { - XFixed *kern = ps->o.blur_kerns[i]; - if (!kern) break; - inc_x += XFIXED_TO_DOUBLE(kern[0]) / 2; - inc_y += XFIXED_TO_DOUBLE(kern[1]) / 2; - } - inc_x = min2(ps->o.resize_damage, inc_x); - inc_y = min2(ps->o.resize_damage, inc_y); - - mdx = max2(dx - inc_x, 0); - mdy = max2(dy - inc_y, 0); - int mdx2 = min2(dx + width + inc_x, ps->root_width), - mdy2 = min2(dy + height + inc_y, ps->root_height); - mwidth = mdx2 - mdx; - mheight = mdy2 - mdy; - } - */ - - GLenum tex_tgt = GL_TEXTURE_RECTANGLE; - if (ps->psglx->has_texture_non_power_of_two) - tex_tgt = GL_TEXTURE_2D; - - // Free textures if size inconsistency discovered - if (mwidth != pbc->width || mheight != pbc->height) - free_glx_bc_resize(ps, pbc); - - // Generate FBO and textures if needed - if (!pbc->textures[0]) - pbc->textures[0] = glx_gen_texture(tex_tgt, mwidth, mheight); - GLuint tex_scr = pbc->textures[0]; - if (more_passes && !pbc->textures[1]) - pbc->textures[1] = glx_gen_texture(tex_tgt, mwidth, mheight); - pbc->width = mwidth; - pbc->height = mheight; - GLuint tex_scr2 = pbc->textures[1]; - if (more_passes && !pbc->fbo) - glGenFramebuffers(1, &pbc->fbo); - const GLuint fbo = pbc->fbo; - - if (!tex_scr || (more_passes && !tex_scr2)) { - log_error("Failed to allocate texture."); - goto glx_blur_dst_end; - } - if (more_passes && !fbo) { - log_error("Failed to allocate framebuffer."); - goto glx_blur_dst_end; - } - - // Read destination pixels into a texture - glEnable(tex_tgt); - glBindTexture(tex_tgt, tex_scr); - glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, mheight); - /* - if (tex_scr2) { - glBindTexture(tex_tgt, tex_scr2); - glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, mdy, mwidth, dx - mdx); - glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy + height, - mwidth, mdy + mheight - dy - height); - glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, mdx, dy, dx - mdx, height); - glx_copy_region_to_tex(ps, tex_tgt, mdx, mdy, dx + width, dy, - mdx + mwidth - dx - width, height); - } */ - - // Texture scaling factor - GLfloat texfac_x = 1.0f, texfac_y = 1.0f; - if (tex_tgt == GL_TEXTURE_2D) { - texfac_x /= (GLfloat)mwidth; - texfac_y /= (GLfloat)mheight; - } - - // Paint it back - if (more_passes) { - glDisable(GL_STENCIL_TEST); - glDisable(GL_SCISSOR_TEST); - } - - bool last_pass = false; - for (int i = 0; i < ps->o.blur_kernel_count; ++i) { - last_pass = (i == ps->o.blur_kernel_count - 1); - const glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i]; - assert(ppass->prog); - - assert(tex_scr); - glBindTexture(tex_tgt, tex_scr); - - if (!last_pass) { - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, tex_scr2, 0); - glDrawBuffer(GL_COLOR_ATTACHMENT0); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - log_error("Framebuffer attachment failed."); - goto glx_blur_dst_end; - } - } else { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDrawBuffer(GL_BACK); - if (have_scissors) - glEnable(GL_SCISSOR_TEST); - if (have_stencil) - glEnable(GL_STENCIL_TEST); - } - - // Color negation for testing... - // 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); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glUseProgram(ppass->prog); - if (ppass->unifm_offset_x >= 0) - glUniform1f(ppass->unifm_offset_x, texfac_x); - if (ppass->unifm_offset_y >= 0) - glUniform1f(ppass->unifm_offset_y, texfac_y); - if (ppass->unifm_factor_center >= 0) - glUniform1f(ppass->unifm_factor_center, factor_center); - - P_PAINTREG_START(crect) { - auto rx = (GLfloat)(crect.x1 - mdx) * texfac_x; - auto ry = (GLfloat)(mheight - (crect.y1 - mdy)) * texfac_y; - auto rxe = rx + (GLfloat)(crect.x2 - crect.x1) * texfac_x; - auto rye = ry - (GLfloat)(crect.y2 - crect.y1) * texfac_y; - auto rdx = (GLfloat)(crect.x1 - mdx); - auto rdy = (GLfloat)(mheight - crect.y1 + mdy); - if (last_pass) { - rdx = (GLfloat)crect.x1; - rdy = (GLfloat)(ps->root_height - crect.y1); - } - auto rdxe = rdx + (GLfloat)(crect.x2 - crect.x1); - auto rdye = rdy - (GLfloat)(crect.y2 - crect.y1); - - // log_trace("%f, %f, %f, %f -> %f, %f, %f, %f", rx, ry, - // rxe, rye, rdx, - // rdy, rdxe, rdye); - - glTexCoord2f(rx, ry); - glVertex3f(rdx, rdy, z); - - glTexCoord2f(rxe, ry); - glVertex3f(rdxe, rdy, z); - - glTexCoord2f(rxe, rye); - glVertex3f(rdxe, rdye, z); - - glTexCoord2f(rx, rye); - glVertex3f(rdx, rdye, z); - } - P_PAINTREG_END(); - - glUseProgram(0); - - // Swap tex_scr and tex_scr2 - { - GLuint tmp = tex_scr2; - tex_scr2 = tex_scr; - tex_scr = tmp; - } - } - - ret = true; - -glx_blur_dst_end: - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glBindTexture(tex_tgt, 0); - glDisable(tex_tgt); - if (have_scissors) - glEnable(GL_SCISSOR_TEST); - if (have_stencil) - glEnable(GL_STENCIL_TEST); - - if (&ibc == pbc) { - free_glx_bc(ps, pbc); - } - - gl_check_err(); - - return ret; -} - -// TODO(bhagwan) this is a mess and needs a more consistent way of getting the border -// pixel I tried looking for a notify event for XCB_CW_BORDER_PIXEL (in -// xcb_create_window()) or a way to get the pixels from xcb_render_picture_t but the -// documentation for the xcb_xrender extension is literaly non existent... -// -// NOTE(yshui) There is no consistent way to get the "border" color of a X window. From -// the WM's perspective there are multiple ways to implement window borders. Using -// glReadPixel is probably the most reliable way. -void glx_read_border_pixel(int root_height, int root_width, int x, int y, int width, - int height, float *ppixel) { - assert(ppixel); - - // Reset the color so the shader doesn't use it - ppixel[0] = ppixel[1] = ppixel[2] = ppixel[3] = -1.0F; - - // First try bottom left corner past the - // circle radius (after the rounded corner ends) - auto screen_x = x; - auto screen_y = root_height - height - y; - - // X is out of bounds - // move to the right side - if (screen_x < 0) { - screen_x += width; - } - - // Y is out of bounds - // move to to top part - if (screen_y < 0) { - screen_y += height; - } - - // All corners are out of bounds, give up - if (screen_x < 0 || screen_y < 0 || screen_x >= root_width || screen_y >= root_height) { - return; - } - - // Invert Y-axis so we can query border color from texture (0,0) - glReadPixels(screen_x, screen_y, 1, 1, GL_RGBA, GL_FLOAT, (void *)ppixel); - - log_trace("xy(%d, %d), glxy(%d %d) wh(%d %d), border_col(%.2f, %.2f, %.2f, %.2f)", - x, y, screen_x, screen_y, width, height, (float)ppixel[0], - (float)ppixel[1], (float)ppixel[2], (float)ppixel[3]); - - gl_check_err(); -} - -bool glx_round_corners_dst(session_t *ps, struct managed_win *w, - const glx_texture_t *ptex, int dx, int dy, int width, - int height, float z, float cr, const region_t *reg_tgt) { - assert(ps->psglx->round_passes->prog); - bool ret = false; - - // log_warn("dxy(%d, %d) wh(%d %d) rwh(%d %d) b(%d), f(%d)", - // dx, dy, width, height, ps->root_width, ps->root_height, w->g.border_width, - // w->focused); - - int mdx = dx, mdy = dy, mwidth = width, mheight = height; - log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight); - - if (w->g.border_width > 0) { - glx_read_border_pixel(ps->root_height, ps->root_width, dx, dy, width, - height, &w->border_col[0]); - } - - { - const glx_round_pass_t *ppass = ps->psglx->round_passes; - assert(ppass->prog); - - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - glUseProgram(ppass->prog); - - // If caller specified a texture use it as source - log_trace("ptex: %p wh(%d %d) %d %d", ptex, ptex->width, ptex->height, - ptex->target, ptex->texture); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(ptex->target, ptex->texture); - - if (ppass->unifm_tex_scr >= 0) { - glUniform1i(ppass->unifm_tex_scr, (GLint)0); - } - if (ppass->unifm_radius >= 0) { - glUniform1f(ppass->unifm_radius, cr); - } - if (ppass->unifm_texcoord >= 0) { - glUniform2f(ppass->unifm_texcoord, (float)dx, (float)dy); - } - if (ppass->unifm_texsize >= 0) { - glUniform2f(ppass->unifm_texsize, (float)mwidth, (float)mheight); - } - if (ppass->unifm_borderw >= 0) { - // Don't render rounded border if we don't know the border color - glUniform1f(ppass->unifm_borderw, - w->border_col[0] != -1. ? (GLfloat)w->g.border_width : 0); - } - if (ppass->unifm_borderc >= 0) { - glUniform4f(ppass->unifm_borderc, w->border_col[0], - w->border_col[1], w->border_col[2], w->border_col[3]); - } - if (ppass->unifm_resolution >= 0) { - glUniform2f(ppass->unifm_resolution, (float)ps->root_width, - (float)ps->root_height); - } - - // Painting - { - P_PAINTREG_START(crect) { - // texture-local coordinates - auto rx = (GLfloat)(crect.x1 - dx); - auto ry = (GLfloat)(crect.y1 - dy); - auto rxe = rx + (GLfloat)(crect.x2 - crect.x1); - auto rye = ry + (GLfloat)(crect.y2 - crect.y1); - if (GL_TEXTURE_2D == ptex->target) { - rx = rx / (GLfloat)width; - ry = ry / (GLfloat)height; - rxe = rxe / (GLfloat)width; - rye = rye / (GLfloat)height; - } - - // coordinates for the texture in the target - auto rdx = (GLfloat)crect.x1; - auto rdy = (GLfloat)(ps->root_height - crect.y1); - auto rdxe = (GLfloat)rdx + (GLfloat)(crect.x2 - crect.x1); - auto rdye = (GLfloat)rdy - (GLfloat)(crect.y2 - crect.y1); - - // Invert Y if needed, this may not work as expected, - // though. I don't have such a FBConfig to test with. - ry = 1.0F - ry; - rye = 1.0F - rye; - - // log_trace("Rect %d (i:%d): %f, %f, %f, %f -> %f, %f, - // %f, %f", ri ,ptex ? ptex->y_inverted : -1, rx, ry, - // rxe, - // rye, rdx, rdy, rdxe, rdye); - - glTexCoord2f(rx, ry); - glVertex3f(rdx, rdy, z); - - glTexCoord2f(rxe, ry); - glVertex3f(rdxe, rdy, z); - - glTexCoord2f(rxe, rye); - glVertex3f(rdxe, rdye, z); - - glTexCoord2f(rx, rye); - glVertex3f(rdx, rdye, z); - } - P_PAINTREG_END(); - } - - glUseProgram(0); - glDisable(GL_BLEND); - } - - ret = true; - - glBindTexture(ptex->target, 0); - glDisable(ptex->target); - glDisable(GL_BLEND); - - gl_check_err(); - - return ret; -} - -bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z, - GLfloat factor, const region_t *reg_tgt) { - // It's possible to dim in glx_render(), but it would be over-complicated - // considering all those mess in color negation and modulation - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(0.0f, 0.0f, 0.0f, factor); - - P_PAINTREG_START(crect) { - // XXX what does all of these variables mean? - GLint rdx = crect.x1; - GLint rdy = ps->root_height - crect.y1; - GLint rdxe = rdx + (crect.x2 - crect.x1); - GLint rdye = rdy - (crect.y2 - crect.y1); - - glVertex3i(rdx, rdy, z); - glVertex3i(rdxe, rdy, z); - glVertex3i(rdxe, rdye, z); - glVertex3i(rdx, rdye, z); - } - P_PAINTREG_END(); - - glColor4f(0.0f, 0.0f, 0.0f, 0.0f); - glDisable(GL_BLEND); - - gl_check_err(); - - return true; -} - -/** - * @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 argb, bool neg, - const region_t *reg_tgt, const glx_prog_main_t *pprogram) { - if (!ptex || !ptex->texture) { - log_error("Missing texture."); - return false; - } - - const bool has_prog = pprogram && pprogram->prog; - bool dual_texture = false; - - // It's required by legacy versions of OpenGL to enable texture target - // before specifying environment. Thanks to madsy for telling me. - glEnable(ptex->target); - - // Enable blending if needed - if (opacity < 1.0 || argb) { - - glEnable(GL_BLEND); - - // Needed for handling opacity of ARGB texture - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - // This is all weird, but X Render is using premultiplied ARGB format, and - // we need to use those things to correct it. Thanks to derhass for help. - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4d(opacity, opacity, opacity, opacity); - } - - if (!has_prog) { - // The default, fixed-function path - // Color negation - if (neg) { - // Simple color negation - if (!glIsEnabled(GL_BLEND)) { - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(GL_COPY_INVERTED); - } - // ARGB texture color negation - else if (argb) { - dual_texture = true; - - // Use two texture stages because the calculation is too - // complicated, thanks to madsy for providing code Texture - // stage 0 - glActiveTexture(GL_TEXTURE0); - - // Negation for premultiplied color: color = A - C - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - - // Pass texture alpha through - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - - // Texture stage 1 - glActiveTexture(GL_TEXTURE1); - glEnable(ptex->target); - glBindTexture(ptex->target, ptex->texture); - - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); - - glActiveTexture(GL_TEXTURE0); - } - // RGB blend color negation - else { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, - GL_ONE_MINUS_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - - // Modulation with constant factor - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); - } - } - } else { - // Programmable path - assert(pprogram->prog); - glUseProgram(pprogram->prog); - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - if (pprogram->unifm_opacity >= 0) - glUniform1f(pprogram->unifm_opacity, (float)opacity); - if (pprogram->unifm_invert_color >= 0) - glUniform1i(pprogram->unifm_invert_color, neg); - if (pprogram->unifm_tex >= 0) - glUniform1i(pprogram->unifm_tex, 0); - if (pprogram->unifm_time >= 0) - glUniform1f(pprogram->unifm_time, (float)ts.tv_sec * 1000.0f + - (float)ts.tv_nsec / 1.0e6f); - } - - // log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d", x, y, width, height, - // dx, dy, ptex->width, ptex->height, z); - - // Bind texture - glBindTexture(ptex->target, ptex->texture); - if (dual_texture) { - glActiveTexture(GL_TEXTURE1); - glBindTexture(ptex->target, ptex->texture); - glActiveTexture(GL_TEXTURE0); - } - - // Painting - { - P_PAINTREG_START(crect) { - // texture-local coordinates - auto rx = (GLfloat)(crect.x1 - dx + x); - auto ry = (GLfloat)(crect.y1 - dy + y); - auto rxe = rx + (GLfloat)(crect.x2 - crect.x1); - auto rye = ry + (GLfloat)(crect.y2 - crect.y1); - // Rectangle textures have [0-w] [0-h] while 2D texture has [0-1] - // [0-1] Thanks to amonakov for pointing out! - if (GL_TEXTURE_2D == ptex->target) { - rx = rx / (GLfloat)ptex->width; - ry = ry / (GLfloat)ptex->height; - rxe = rxe / (GLfloat)ptex->width; - rye = rye / (GLfloat)ptex->height; - } - - // coordinates for the texture in the target - GLint rdx = crect.x1; - GLint rdy = ps->root_height - crect.y1; - GLint rdxe = rdx + (crect.x2 - crect.x1); - GLint rdye = rdy - (crect.y2 - crect.y1); - - // 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.0f - ry; - rye = 1.0f - rye; - } - - // log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d", ri, rx, - // ry, rxe, rye, - // rdx, rdy, rdxe, rdye); - -#define P_TEXCOORD(cx, cy) \ - { \ - if (dual_texture) { \ - glMultiTexCoord2f(GL_TEXTURE0, cx, cy); \ - glMultiTexCoord2f(GL_TEXTURE1, cx, cy); \ - } else \ - glTexCoord2f(cx, cy); \ - } - P_TEXCOORD(rx, ry); - glVertex3i(rdx, rdy, z); - - P_TEXCOORD(rxe, ry); - glVertex3i(rdxe, rdy, z); - - P_TEXCOORD(rxe, rye); - glVertex3i(rdxe, rdye, z); - - P_TEXCOORD(rx, rye); - glVertex3i(rdx, rdye, z); - } - P_PAINTREG_END(); - } - - // Cleanup - glBindTexture(ptex->target, 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); - glDisable(ptex->target); - - if (dual_texture) { - glActiveTexture(GL_TEXTURE1); - glBindTexture(ptex->target, 0); - glDisable(ptex->target); - glActiveTexture(GL_TEXTURE0); - } - - if (has_prog) - glUseProgram(0); - - gl_check_err(); - - return true; -} |