2018-10-03 17:14:51 -04:00
|
|
|
// SPDX-License-Identifier: MIT
|
2013-03-15 11:16:23 -04:00
|
|
|
/*
|
|
|
|
* Compton - a compositor for X11
|
|
|
|
*
|
|
|
|
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011-2013, Christopher Jeffrey
|
2018-10-03 17:24:12 -04:00
|
|
|
* See LICENSE-mit for more information.
|
2013-03-15 11:16:23 -04:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
#include <stdio.h>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <stdlib.h>
|
2019-03-10 08:34:37 -04:00
|
|
|
#include <string.h>
|
2020-03-10 03:29:38 -04:00
|
|
|
#include <time.h>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <xcb/render.h>
|
2019-03-10 08:34:37 -04:00
|
|
|
#include <xcb/xcb.h>
|
2019-01-20 11:53:39 -05:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
#include "backend/gl/gl_common.h"
|
|
|
|
#include "backend/gl/glx.h"
|
|
|
|
#include "common.h"
|
2018-12-15 13:47:21 -05:00
|
|
|
#include "compiler.h"
|
2019-01-18 18:30:44 -05:00
|
|
|
#include "config.h"
|
2019-03-10 08:34:37 -04:00
|
|
|
#include "kernel.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "region.h"
|
|
|
|
#include "string_utils.h"
|
2019-06-06 02:37:48 -04:00
|
|
|
#include "uthash_extra.h"
|
2019-01-20 11:53:39 -05:00
|
|
|
#include "utils.h"
|
|
|
|
#include "win.h"
|
2018-12-15 12:53:17 -05:00
|
|
|
|
2013-03-15 11:16:23 -04:00
|
|
|
#include "opengl.h"
|
|
|
|
|
2019-05-05 20:24:38 -04:00
|
|
|
#ifndef GL_TEXTURE_RECTANGLE
|
|
|
|
#define GL_TEXTURE_RECTANGLE 0x84F5
|
|
|
|
#endif
|
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
static inline XVisualInfo *get_visualinfo_from_visual(session_t *ps, xcb_visualid_t visual) {
|
|
|
|
XVisualInfo vreq = {.visualid = visual};
|
|
|
|
int nitems = 0;
|
2018-09-23 14:10:46 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
return XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems);
|
2018-09-23 14:10:46 -04:00
|
|
|
}
|
|
|
|
|
2013-03-15 11:16:23 -04:00
|
|
|
/**
|
|
|
|
* Initialize OpenGL.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
bool glx_init(session_t *ps, bool need_render) {
|
|
|
|
bool success = false;
|
|
|
|
XVisualInfo *pvis = NULL;
|
|
|
|
|
|
|
|
// Check for GLX extension
|
|
|
|
if (!ps->glx_exists) {
|
2020-03-31 01:07:09 -04:00
|
|
|
log_error("No GLX extension.");
|
|
|
|
goto glx_init_end;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get XVisualInfo
|
2023-06-29 00:39:36 -04:00
|
|
|
pvis = get_visualinfo_from_visual(ps, ps->c.screen_info->root_visual);
|
2019-03-10 08:34:37 -04:00
|
|
|
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;
|
2023-06-29 00:39:36 -04:00
|
|
|
if (Success != glXGetConfig(ps->c.dpy, pvis, GLX_USE_GL, &value) || !value) {
|
2019-03-10 08:34:37 -04:00
|
|
|
log_error("Root visual is not a GL visual.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
if (Success != glXGetConfig(ps->c.dpy, pvis, GLX_DOUBLEBUFFER, &value) ||
|
|
|
|
!value) {
|
2019-03-10 08:34:37 -04:00
|
|
|
log_error("Root visual is not a double buffered GL visual.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure GLX_EXT_texture_from_pixmap exists
|
2023-09-14 17:43:15 -04:00
|
|
|
if (need_render && !glxext.has_GLX_EXT_texture_from_pixmap) {
|
2019-03-10 08:34:37 -04:00
|
|
|
goto glx_init_end;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// 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));
|
|
|
|
|
2019-06-06 02:37:48 -04:00
|
|
|
// +1 for the zero terminator
|
2019-06-07 16:53:23 -04:00
|
|
|
ps->psglx->blur_passes = ccalloc(ps->o.blur_kernel_count, glx_blur_pass_t);
|
2019-06-06 02:37:48 -04:00
|
|
|
|
2019-06-07 16:53:23 -04:00
|
|
|
for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
|
2019-03-10 08:34:37 -04:00
|
|
|
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;
|
|
|
|
}
|
2020-09-30 11:09:27 -04:00
|
|
|
|
|
|
|
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;
|
2020-04-02 01:56:42 -04:00
|
|
|
ppass->unifm_borderc = -1;
|
2020-09-30 11:09:27 -04:00
|
|
|
ppass->unifm_resolution = -1;
|
2021-04-11 10:25:12 -04:00
|
|
|
ppass->unifm_tex_scr = -1;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
glx_session_t *psglx = ps->psglx;
|
|
|
|
|
|
|
|
if (!psglx->context) {
|
|
|
|
// Get GLX context
|
2014-03-17 11:25:34 -04:00
|
|
|
#ifndef DEBUG_GLX_DEBUG_CONTEXT
|
2023-06-29 00:39:36 -04:00
|
|
|
psglx->context = glXCreateContext(ps->c.dpy, pvis, None, GL_TRUE);
|
2014-03-17 11:25:34 -04:00
|
|
|
#else
|
2019-03-10 08:34:37 -04:00
|
|
|
{
|
|
|
|
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(
|
2023-06-29 00:39:36 -04:00
|
|
|
ps->c.dpy, fbconfig, NULL, GL_TRUE, attrib_list);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2014-03-17 11:25:34 -04:00
|
|
|
#endif
|
2013-03-15 11:16:23 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
if (!psglx->context) {
|
|
|
|
log_error("Failed to get GLX context.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
2013-04-26 02:01:20 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
// Attach GLX context
|
2023-06-29 00:39:36 -04:00
|
|
|
if (!glXMakeCurrent(ps->c.dpy, get_tgt_window(ps), psglx->context)) {
|
2019-03-10 08:34:37 -04:00
|
|
|
log_error("Failed to attach GLX context.");
|
|
|
|
goto glx_init_end;
|
|
|
|
}
|
2014-03-17 11:25:34 -04:00
|
|
|
|
|
|
|
#ifdef DEBUG_GLX_DEBUG_CONTEXT
|
2019-03-10 08:34:37 -04:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2014-03-17 11:25:34 -04:00
|
|
|
#endif
|
2019-03-10 08:34:37 -04: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 && !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
|
2023-09-14 17:43:15 -04:00
|
|
|
if (need_render) {
|
2019-03-10 08:34:37 -04:00
|
|
|
psglx->has_texture_non_power_of_two =
|
2024-02-10 08:28:13 -05:00
|
|
|
epoxy_has_gl_extension("GL_ARB_texture_non_power_of_two");
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// 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
|
2023-09-14 17:43:15 -04:00
|
|
|
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
|
2019-03-10 08:34:37 -04:00
|
|
|
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
2023-06-29 00:39:36 -04:00
|
|
|
// glXSwapBuffers(ps->c.dpy, get_tgt_window(ps));
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
success = true;
|
2013-03-15 11:16:23 -04:00
|
|
|
|
|
|
|
glx_init_end:
|
2019-05-05 20:41:36 -04:00
|
|
|
XFree(pvis);
|
2013-03-15 11:16:23 -04:00
|
|
|
|
2023-09-14 17:43:15 -04:00
|
|
|
if (!success) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glx_destroy(ps);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2013-03-15 11:16:23 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
return success;
|
2013-03-15 11:16:23 -04:00
|
|
|
}
|
|
|
|
|
2019-07-24 21:27:02 -04:00
|
|
|
static void glx_free_prog_main(glx_prog_main_t *pprogram) {
|
2023-09-14 17:43:15 -04:00
|
|
|
if (!pprogram) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
if (pprogram->prog) {
|
|
|
|
glDeleteProgram(pprogram->prog);
|
|
|
|
pprogram->prog = 0;
|
|
|
|
}
|
|
|
|
pprogram->unifm_opacity = -1;
|
|
|
|
pprogram->unifm_invert_color = -1;
|
|
|
|
pprogram->unifm_tex = -1;
|
2014-05-16 03:18:17 -04:00
|
|
|
}
|
|
|
|
|
2013-03-15 11:16:23 -04:00
|
|
|
/**
|
|
|
|
* Destroy GLX related resources.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
void glx_destroy(session_t *ps) {
|
2023-09-14 17:43:15 -04:00
|
|
|
if (!ps->psglx) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// Free all GLX resources of windows
|
2019-04-17 18:14:45 -04:00
|
|
|
win_stack_foreach_managed(w, &ps->window_stack) {
|
2019-03-10 08:34:37 -04:00
|
|
|
free_win_res_glx(ps, w);
|
2019-04-03 03:36:02 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// Free GLSL shaders/programs
|
2019-06-07 16:53:23 -04:00
|
|
|
for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
|
2020-09-30 11:09:27 -04:00
|
|
|
if (ppass->frag_shader) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glDeleteShader(ppass->frag_shader);
|
2020-09-30 11:09:27 -04:00
|
|
|
}
|
|
|
|
if (ppass->prog) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glDeleteProgram(ppass->prog);
|
2020-09-30 11:09:27 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2019-06-06 02:37:48 -04:00
|
|
|
free(ps->psglx->blur_passes);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2020-09-30 11:09:27 -04:00
|
|
|
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);
|
|
|
|
|
2019-07-24 21:27:02 -04:00
|
|
|
glx_free_prog_main(&ps->glx_prog_win);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
|
|
|
// Destroy GLX context
|
|
|
|
if (ps->psglx->context) {
|
2023-06-29 00:39:36 -04:00
|
|
|
glXMakeCurrent(ps->c.dpy, None, NULL);
|
|
|
|
glXDestroyContext(ps->c.dpy, ps->psglx->context);
|
2019-03-10 08:34:37 -04:00
|
|
|
ps->psglx->context = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(ps->psglx);
|
|
|
|
ps->psglx = NULL;
|
2020-04-20 14:10:53 -04:00
|
|
|
ps->argb_fbconfig = NULL;
|
2014-07-28 00:50:15 -04:00
|
|
|
}
|
|
|
|
|
2013-03-15 11:16:23 -04:00
|
|
|
/**
|
|
|
|
* Callback to run on root window size change.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
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();
|
2013-03-16 10:54:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize GLX blur filter.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
bool glx_init_blur(session_t *ps) {
|
2019-06-07 16:53:23 -04:00
|
|
|
assert(ps->o.blur_kernel_count > 0);
|
|
|
|
assert(ps->o.blur_kerns);
|
2019-03-10 08:34:37 -04:00
|
|
|
assert(ps->o.blur_kerns[0]);
|
|
|
|
|
|
|
|
// Allocate PBO if more than one blur kernel is present
|
2019-06-07 16:53:23 -04:00
|
|
|
if (ps->o.blur_kernel_count > 1) {
|
2019-03-10 08:34:37 -04:00
|
|
|
// 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("");
|
|
|
|
}
|
|
|
|
|
2019-06-07 16:53:23 -04:00
|
|
|
for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
|
2019-03-10 08:34:37 -04:00
|
|
|
auto kern = ps->o.blur_kerns[i];
|
|
|
|
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
|
|
|
|
|
|
|
|
// Build shader
|
2019-03-30 05:07:21 -04:00
|
|
|
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) {
|
2023-09-14 17:43:15 -04:00
|
|
|
if (height / 2 == j && width / 2 == k) {
|
2019-03-30 05:07:21 -04:00
|
|
|
continue;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
double val = kern->data[j * width + k];
|
|
|
|
if (val == 0) {
|
|
|
|
continue;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
sum += val;
|
|
|
|
sprintf(pc, shader_add, val, texture_func,
|
|
|
|
k - width / 2, j - height / 2);
|
|
|
|
pc += strlen(pc);
|
|
|
|
assert(strlen(shader_str) < len);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
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);
|
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
2014-05-16 03:18:17 -04:00
|
|
|
|
2013-05-20 06:04:40 -04:00
|
|
|
#undef P_GET_UNIFM_LOC
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
free(extension);
|
2013-03-16 10:54:43 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
// Restore LC_NUMERIC
|
|
|
|
setlocale(LC_NUMERIC, lc_numeric_old);
|
|
|
|
free(lc_numeric_old);
|
|
|
|
}
|
2013-03-20 05:29:45 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
gl_check_err();
|
2013-04-24 21:27:14 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
return true;
|
2013-03-15 11:16:23 -04:00
|
|
|
}
|
|
|
|
|
2020-09-30 11:09:27 -04:00
|
|
|
/**
|
|
|
|
* 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"
|
2020-04-02 01:56:42 -04:00
|
|
|
"uniform vec4 u_borderc;\n"
|
2020-09-30 11:09:27 -04:00
|
|
|
"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"
|
2020-04-02 01:56:42 -04:00
|
|
|
" vec4 u_v4WndBgColor = %s(tex_scr, vec2(gl_TexCoord[0].st));\n"
|
2020-09-30 11:09:27 -04:00
|
|
|
" float u_fRadiusPx = u_radius;\n"
|
2020-04-02 01:56:42 -04:00
|
|
|
" float u_fHalfBorderThickness = u_borderw / 2.0;\n"
|
|
|
|
" vec4 u_v4BorderColor = u_borderc;\n"
|
2020-09-30 11:09:27 -04:00
|
|
|
" 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"
|
2020-04-02 01:56:42 -04:00
|
|
|
" vec4 v4ToColor = u_v4WndBgColor; //Outside color is the "
|
2020-09-30 11:09:27 -04:00
|
|
|
"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"
|
2020-04-02 01:56:42 -04:00
|
|
|
" float fBlendAmount = smoothstep(-1.0, 1.0, fDist);\n"
|
2020-09-30 11:09:27 -04:00
|
|
|
" 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);
|
|
|
|
|
2020-12-17 17:19:53 -05:00
|
|
|
sprintf(shader_str, FRAG_SHADER, extension, sampler_type, texture_func);
|
2020-09-30 11:09:27 -04:00
|
|
|
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);
|
2020-04-02 01:56:42 -04:00
|
|
|
P_GET_UNIFM_LOC("u_borderc", unifm_borderc);
|
2020-09-30 11:09:27 -04:00
|
|
|
P_GET_UNIFM_LOC("u_resolution", unifm_resolution);
|
2021-04-11 10:25:12 -04:00
|
|
|
P_GET_UNIFM_LOC("tex_scr", unifm_tex_scr);
|
2020-09-30 11:09:27 -04:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2014-05-16 03:18:17 -04:00
|
|
|
/**
|
|
|
|
* Load a GLSL main program from shader strings.
|
|
|
|
*/
|
2019-07-24 21:27:02 -04:00
|
|
|
bool glx_load_prog_main(const char *vshader_str, const char *fshader_str,
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
2020-03-10 03:29:38 -04:00
|
|
|
P_GET_UNIFM_LOC("time", unifm_time);
|
2014-05-16 03:18:17 -04:00
|
|
|
#undef P_GET_UNIFM_LOC
|
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
gl_check_err();
|
2014-05-16 03:18:17 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
return true;
|
2014-05-16 03:18:17 -04:00
|
|
|
}
|
|
|
|
|
2020-09-30 11:09:27 -04:00
|
|
|
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;
|
|
|
|
|
2020-04-02 01:56:42 -04:00
|
|
|
// log_trace("Copying xy(%d %d) wh(%d %d) ptex(%p)", x, y, width, height, ptex);
|
2020-09-30 11:09:27 -04:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2013-03-15 11:16:23 -04:00
|
|
|
/**
|
|
|
|
* Bind a X pixmap to an OpenGL texture.
|
|
|
|
*/
|
2019-03-30 05:07:21 -04:00
|
|
|
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) {
|
2023-09-14 17:43:15 -04:00
|
|
|
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return true;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
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
|
2019-03-30 05:07:21 -04:00
|
|
|
int depth = 0;
|
2019-03-10 08:34:37 -04:00
|
|
|
if (!ptex->glpixmap) {
|
|
|
|
need_release = false;
|
|
|
|
|
|
|
|
// Retrieve pixmap parameters, if they aren't provided
|
2019-03-30 05:07:21 -04:00
|
|
|
if (!width || !height) {
|
|
|
|
auto r = xcb_get_geometry_reply(
|
2023-06-29 00:39:36 -04:00
|
|
|
ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL);
|
2019-03-30 05:07:21 -04:00
|
|
|
if (!r) {
|
2019-03-10 08:34:37 -04:00
|
|
|
log_error("Failed to query info of pixmap %#010x.", pixmap);
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
if (r->depth > OPENGL_MAX_DEPTH) {
|
2019-03-10 08:34:37 -04:00
|
|
|
log_error("Requested depth %d higher than %d.", depth,
|
|
|
|
OPENGL_MAX_DEPTH);
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
depth = r->depth;
|
|
|
|
width = r->width;
|
|
|
|
height = r->height;
|
|
|
|
free(r);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 &&
|
2023-09-14 17:43:15 -04:00
|
|
|
ps->psglx->has_texture_non_power_of_two) {
|
2019-03-10 08:34:37 -04:00
|
|
|
tex_tgt = GLX_TEXTURE_2D_EXT;
|
2023-09-14 17:43:15 -04:00
|
|
|
} else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) {
|
2019-03-10 08:34:37 -04:00
|
|
|
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
2023-09-14 17:43:15 -04:00
|
|
|
} else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts)) {
|
2019-03-10 08:34:37 -04:00
|
|
|
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
2023-09-14 17:43:15 -04:00
|
|
|
} else {
|
2019-03-10 08:34:37 -04:00
|
|
|
tex_tgt = GLX_TEXTURE_2D_EXT;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
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,
|
2019-03-30 05:07:21 -04:00
|
|
|
(GLint)tex_tgt,
|
2019-03-10 08:34:37 -04:00
|
|
|
0,
|
|
|
|
};
|
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
ptex->glpixmap = glXCreatePixmap(ps->c.dpy, fbcfg->cfg, pixmap, attrs);
|
2019-03-10 08:34:37 -04:00
|
|
|
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.
|
2023-09-14 17:43:15 -04:00
|
|
|
if (need_release) {
|
2023-06-29 00:39:36 -04:00
|
|
|
glXReleaseTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2023-06-29 00:39:36 -04:00
|
|
|
glXBindTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// Cleanup
|
|
|
|
glBindTexture(ptex->target, 0);
|
|
|
|
glDisable(ptex->target);
|
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
|
|
|
return true;
|
2013-03-15 11:16:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Release binding of a texture.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
|
|
|
|
// Release binding
|
|
|
|
if (ptex->glpixmap && ptex->texture) {
|
|
|
|
glBindTexture(ptex->target, ptex->texture);
|
2023-06-29 00:39:36 -04:00
|
|
|
glXReleaseTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
2019-03-10 08:34:37 -04:00
|
|
|
glBindTexture(ptex->target, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free GLX Pixmap
|
|
|
|
if (ptex->glpixmap) {
|
2023-06-29 00:39:36 -04:00
|
|
|
glXDestroyPixmap(ps->c.dpy, ptex->glpixmap);
|
2019-03-10 08:34:37 -04:00
|
|
|
ptex->glpixmap = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
gl_check_err();
|
2013-03-15 11:16:23 -04:00
|
|
|
}
|
|
|
|
|
2013-03-16 10:54:43 -04:00
|
|
|
/**
|
|
|
|
* Set clipping region on the target window.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
void glx_set_clip(session_t *ps, const region_t *reg) {
|
|
|
|
// Quit if we aren't using stencils
|
2023-09-14 17:43:15 -04:00
|
|
|
if (ps->o.glx_no_stencil) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2013-03-16 10:54:43 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
2013-03-18 07:01:18 -04:00
|
|
|
|
2023-09-14 17:43:15 -04:00
|
|
|
if (!reg) {
|
2019-03-10 08:34:37 -04:00
|
|
|
return;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2013-03-18 07:01:18 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
int nrects;
|
|
|
|
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
|
2013-03-18 07:01:18 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
|
|
|
}
|
2013-03-18 07:01:18 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
gl_check_err();
|
2013-03-16 10:54:43 -04:00
|
|
|
}
|
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
#define P_PAINTREG_START(var) \
|
|
|
|
region_t reg_new; \
|
|
|
|
int nrects; \
|
|
|
|
const rect_t *rects; \
|
2019-03-30 05:07:21 -04:00
|
|
|
assert(width >= 0 && height >= 0); \
|
|
|
|
pixman_region32_init_rect(®_new, dx, dy, (uint)width, (uint)height); \
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
|
|
|
|
2013-05-20 06:04:40 -04:00
|
|
|
/**
|
|
|
|
* Blur contents in a particular region.
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-29 23:56:00 -04:00
|
|
|
*
|
|
|
|
* XXX seems to be way to complex for what it does
|
2013-05-20 06:04:40 -04:00
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
2019-06-07 16:53:23 -04:00
|
|
|
const bool more_passes = ps->o.blur_kernel_count > 1;
|
2019-03-10 08:34:37 -04:00
|
|
|
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};
|
2023-09-14 17:43:15 -04:00
|
|
|
if (!pbc) {
|
2019-03-10 08:34:37 -04:00
|
|
|
pbc = &ibc;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
inc_x = min2(ps->o.resize_damage, inc_x);
|
|
|
|
inc_y = min2(ps->o.resize_damage, inc_y);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
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);
|
2019-03-10 08:34:37 -04:00
|
|
|
mwidth = mdx2 - mdx;
|
|
|
|
mheight = mdy2 - mdy;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
GLenum tex_tgt = GL_TEXTURE_RECTANGLE;
|
2023-09-14 17:43:15 -04:00
|
|
|
if (ps->psglx->has_texture_non_power_of_two) {
|
2019-03-10 08:34:37 -04:00
|
|
|
tex_tgt = GL_TEXTURE_2D;
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// Free textures if size inconsistency discovered
|
2023-09-14 17:43:15 -04:00
|
|
|
if (mwidth != pbc->width || mheight != pbc->height) {
|
2019-03-10 08:34:37 -04:00
|
|
|
free_glx_bc_resize(ps, pbc);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
// Generate FBO and textures if needed
|
2023-09-14 17:43:15 -04:00
|
|
|
if (!pbc->textures[0]) {
|
2019-07-24 21:27:02 -04:00
|
|
|
pbc->textures[0] = glx_gen_texture(tex_tgt, mwidth, mheight);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
GLuint tex_scr = pbc->textures[0];
|
2023-09-14 17:43:15 -04:00
|
|
|
if (more_passes && !pbc->textures[1]) {
|
2019-07-24 21:27:02 -04:00
|
|
|
pbc->textures[1] = glx_gen_texture(tex_tgt, mwidth, mheight);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
pbc->width = mwidth;
|
|
|
|
pbc->height = mheight;
|
|
|
|
GLuint tex_scr2 = pbc->textures[1];
|
2023-09-14 17:43:15 -04:00
|
|
|
if (more_passes && !pbc->fbo) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glGenFramebuffers(1, &pbc->fbo);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
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
|
2023-09-14 17:43:15 -04:00
|
|
|
GLfloat texfac_x = 1.0F, texfac_y = 1.0F;
|
2019-03-30 05:07:21 -04:00
|
|
|
if (tex_tgt == GL_TEXTURE_2D) {
|
|
|
|
texfac_x /= (GLfloat)mwidth;
|
|
|
|
texfac_y /= (GLfloat)mheight;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Paint it back
|
|
|
|
if (more_passes) {
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
glDisable(GL_SCISSOR_TEST);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool last_pass = false;
|
2019-06-07 16:53:23 -04:00
|
|
|
for (int i = 0; i < ps->o.blur_kernel_count; ++i) {
|
|
|
|
last_pass = (i == ps->o.blur_kernel_count - 1);
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
2023-09-14 17:43:15 -04:00
|
|
|
if (have_scissors) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glEnable(GL_SCISSOR_TEST);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
|
|
|
if (have_stencil) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glEnable(GL_STENCIL_TEST);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2023-09-14 17:43:15 -04:00
|
|
|
if (ppass->unifm_offset_x >= 0) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glUniform1f(ppass->unifm_offset_x, texfac_x);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
|
|
|
if (ppass->unifm_offset_y >= 0) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glUniform1f(ppass->unifm_offset_y, texfac_y);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
|
|
|
if (ppass->unifm_factor_center >= 0) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glUniform1f(ppass->unifm_factor_center, factor_center);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
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);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
// log_trace("%f, %f, %f, %f -> %f, %f, %f, %f", rx, ry,
|
|
|
|
// rxe, rye, rdx,
|
|
|
|
// rdy, rdxe, rdye);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
glTexCoord2f(rx, ry);
|
|
|
|
glVertex3f(rdx, rdy, z);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
glTexCoord2f(rxe, ry);
|
|
|
|
glVertex3f(rdxe, rdy, z);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
glTexCoord2f(rxe, rye);
|
|
|
|
glVertex3f(rdxe, rdye, z);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
glTexCoord2f(rx, rye);
|
|
|
|
glVertex3f(rdx, rdye, z);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
P_PAINTREG_END();
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
glUseProgram(0);
|
|
|
|
|
|
|
|
// Swap tex_scr and tex_scr2
|
|
|
|
{
|
|
|
|
GLuint tmp = tex_scr2;
|
|
|
|
tex_scr2 = tex_scr;
|
|
|
|
tex_scr = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = true;
|
2013-05-20 06:04:40 -04:00
|
|
|
|
|
|
|
glx_blur_dst_end:
|
2019-03-10 08:34:37 -04:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
glBindTexture(tex_tgt, 0);
|
|
|
|
glDisable(tex_tgt);
|
2023-09-14 17:43:15 -04:00
|
|
|
if (have_scissors) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glEnable(GL_SCISSOR_TEST);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
|
|
|
if (have_stencil) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glEnable(GL_STENCIL_TEST);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2013-05-20 06:04:40 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
if (&ibc == pbc) {
|
|
|
|
free_glx_bc(ps, pbc);
|
|
|
|
}
|
2013-03-19 08:58:55 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
gl_check_err();
|
2013-04-24 21:27:14 -04:00
|
|
|
|
2019-03-10 08:34:37 -04:00
|
|
|
return ret;
|
2013-03-19 08:58:55 -04:00
|
|
|
}
|
|
|
|
|
2020-04-02 01:56:42 -04:00
|
|
|
// 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
|
2024-01-30 22:09:28 -05:00
|
|
|
// documentation for the xcb_xrender extension is literally non existent...
|
2020-04-02 01:56:42 -04:00
|
|
|
//
|
|
|
|
// 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) {
|
2020-09-30 11:09:27 -04:00
|
|
|
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);
|
|
|
|
|
2020-04-02 01:56:42 -04:00
|
|
|
if (w->g.border_width > 0) {
|
|
|
|
glx_read_border_pixel(ps->root_height, ps->root_width, dx, dy, width,
|
|
|
|
height, &w->border_col[0]);
|
|
|
|
}
|
|
|
|
|
2020-09-30 11:09:27 -04:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2021-04-11 10:25:12 -04:00
|
|
|
if (ppass->unifm_tex_scr >= 0) {
|
|
|
|
glUniform1i(ppass->unifm_tex_scr, (GLint)0);
|
|
|
|
}
|
2020-09-30 11:09:27 -04:00
|
|
|
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) {
|
2020-04-02 01:56:42 -04:00
|
|
|
// 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);
|
2020-09-30 11:09:27 -04:00
|
|
|
}
|
2020-04-02 01:56:42 -04:00
|
|
|
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]);
|
2020-09-30 11:09:27 -04:00
|
|
|
}
|
|
|
|
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.
|
2020-04-02 01:56:42 -04:00
|
|
|
ry = 1.0F - ry;
|
|
|
|
rye = 1.0F - rye;
|
2020-09-30 11:09:27 -04:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
2023-09-14 17:43:15 -04:00
|
|
|
glColor4f(0.0F, 0.0F, 0.0F, factor);
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2019-03-30 05:07:21 -04:00
|
|
|
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);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2019-03-30 05:07:21 -04:00
|
|
|
P_PAINTREG_END();
|
2019-03-10 08:34:37 -04:00
|
|
|
|
2023-09-14 17:43:15 -04:00
|
|
|
glColor4f(0.0F, 0.0F, 0.0F, 0.0F);
|
2019-03-10 08:34:37 -04:00
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
|
|
|
return true;
|
2013-03-24 23:36:39 -04:00
|
|
|
}
|
|
|
|
|
2013-03-15 11:16:23 -04:00
|
|
|
/**
|
|
|
|
* @brief Render a region with texture data.
|
|
|
|
*/
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
2019-03-30 05:07:21 -04:00
|
|
|
glColor4d(opacity, opacity, opacity, opacity);
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2020-03-10 03:29:38 -04:00
|
|
|
struct timespec ts;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
2023-09-14 17:43:15 -04:00
|
|
|
if (pprogram->unifm_opacity >= 0) {
|
2019-03-30 05:07:21 -04:00
|
|
|
glUniform1f(pprogram->unifm_opacity, (float)opacity);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
|
|
|
if (pprogram->unifm_invert_color >= 0) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glUniform1i(pprogram->unifm_invert_color, neg);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
|
|
|
if (pprogram->unifm_tex >= 0) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glUniform1i(pprogram->unifm_tex, 0);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
|
|
|
if (pprogram->unifm_time >= 0) {
|
|
|
|
glUniform1f(pprogram->unifm_time, (float)ts.tv_sec * 1000.0F +
|
|
|
|
(float)ts.tv_nsec / 1.0e6F);
|
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2020-09-30 11:09:27 -04:00
|
|
|
// texture-local coordinates
|
2019-03-30 05:07:21 -04:00
|
|
|
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);
|
2019-03-10 08:34:37 -04:00
|
|
|
// 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) {
|
2019-03-30 05:07:21 -04:00
|
|
|
rx = rx / (GLfloat)ptex->width;
|
|
|
|
ry = ry / (GLfloat)ptex->height;
|
|
|
|
rxe = rxe / (GLfloat)ptex->width;
|
|
|
|
rye = rye / (GLfloat)ptex->height;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
2020-09-30 11:09:27 -04:00
|
|
|
|
|
|
|
// coordinates for the texture in the target
|
2019-03-10 08:34:37 -04:00
|
|
|
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) {
|
2023-09-14 17:43:15 -04:00
|
|
|
ry = 1.0F - ry;
|
|
|
|
rye = 1.0F - rye;
|
2019-03-10 08:34:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2023-09-14 17:43:15 -04:00
|
|
|
glColor4f(0.0F, 0.0F, 0.0F, 0.0F);
|
2019-03-10 08:34:37 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-09-14 17:43:15 -04:00
|
|
|
if (has_prog) {
|
2019-03-10 08:34:37 -04:00
|
|
|
glUseProgram(0);
|
2023-09-14 17:43:15 -04:00
|
|
|
}
|
2019-03-10 08:34:37 -04:00
|
|
|
|
|
|
|
gl_check_err();
|
|
|
|
|
|
|
|
return true;
|
2013-03-15 11:16:23 -04:00
|
|
|
}
|