Feature: #183 custom window shader & #193 --no-fading-destroyed-argb

- Add --glx-fshader-win, which specifies a custom fragment shader for
  painting windows. compton-default-fshader-win.glsl is the shader with
  default behavior, and compton-fake-transparency-fshader-win.glsl
  provides a template of fake transparency. (#183)

- Add --force-win-blend to force all windows to be painted with
  blending.

- Add --no-fading-destroyed-argb, as a workaround of bugs in some WMs.
  (#193)
This commit is contained in:
Richard Grenville 2014-05-16 15:18:17 +08:00
parent bb55706c05
commit 81c677f28b
7 changed files with 375 additions and 91 deletions

View File

@ -0,0 +1,11 @@
uniform float opacity;
uniform bool invert_color;
uniform sampler2D tex;
void main() {
vec4 c = texture2D(tex, gl_TexCoord[0]);
if (invert_color)
c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a);
c *= opacity;
gl_FragColor = c;
}

View File

@ -0,0 +1,19 @@
uniform float opacity;
uniform bool invert_color;
uniform sampler2D tex;
void main() {
vec4 c = texture2D(tex, gl_TexCoord[0]);
{
// Change vec4(1.0, 1.0, 1.0, 1.0) to your desired color
vec4 vdiff = abs(vec4(1.0, 1.0, 1.0, 1.0) - c);
float diff = max(max(max(vdiff.r, vdiff.g), vdiff.b), vdiff.a);
// Change 0.8 to your desired opacity
if (diff < 0.001)
c *= 0.8;
}
if (invert_color)
c = vec4(vec3(c.a, c.a, c.a) - vec3(c), c.a);
c *= opacity;
gl_FragColor = c;
}

View File

@ -48,6 +48,7 @@ fading = true;
fade-in-step = 0.03; fade-in-step = 0.03;
fade-out-step = 0.03; fade-out-step = 0.03;
# no-fading-openclose = true; # no-fading-openclose = true;
# no-fading-destroyed-argb = true;
fade-exclude = [ ]; fade-exclude = [ ];
# Other # Other
@ -84,5 +85,5 @@ glx-swap-method = "undefined";
# Window type settings # Window type settings
wintypes: wintypes:
{ {
tooltip = { fade = true; shadow = false; opacity = 0.75; focus = true; }; tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; };
}; };

View File

@ -469,6 +469,25 @@ typedef struct {
/// Height of the textures. /// Height of the textures.
int height; int height;
} glx_blur_cache_t; } glx_blur_cache_t;
typedef struct {
/// GLSL program.
GLuint prog;
/// Location of uniform "opacity" in window GLSL program.
GLint unifm_opacity;
/// Location of uniform "invert_color" in blur GLSL program.
GLint unifm_invert_color;
/// Location of uniform "tex" in window GLSL program.
GLint unifm_tex;
} glx_prog_main_t;
#define GLX_PROG_MAIN_INIT { \
.prog = 0, \
.unifm_opacity = -1, \
.unifm_invert_color = -1, \
.unifm_tex = -1, \
}
#endif #endif
typedef struct { typedef struct {
@ -536,10 +555,12 @@ typedef struct _options_t {
int glx_swap_method; int glx_swap_method;
/// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring. /// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring.
bool glx_use_gpushader4; bool glx_use_gpushader4;
/// Whether to try to detect WM windows and mark them as focused. /// Custom fragment shader for painting windows, as a string.
bool mark_wmwin_focused; char *glx_fshader_win_str;
/// Whether to mark override-redirect windows as focused. #ifdef CONFIG_VSYNC_OPENGL_GLSL
bool mark_ovredir_focused; /// Custom GLX program used for painting window.
glx_prog_main_t glx_prog_win;
#endif
/// Whether to fork to background. /// Whether to fork to background.
bool fork_after_register; bool fork_after_register;
/// Whether to detect rounded corners. /// Whether to detect rounded corners.
@ -547,6 +568,8 @@ typedef struct _options_t {
/// Whether to paint on X Composite overlay window instead of root /// Whether to paint on X Composite overlay window instead of root
/// window. /// window.
bool paint_on_overlay; bool paint_on_overlay;
/// Force painting of window content with blending.
bool force_win_blend;
/// Resize damage for a specific number of pixels. /// Resize damage for a specific number of pixels.
int resize_damage; int resize_damage;
/// Whether to unredirect all windows if a full-screen opaque window /// Whether to unredirect all windows if a full-screen opaque window
@ -622,6 +645,8 @@ typedef struct _options_t {
time_ms_t fade_delta; time_ms_t fade_delta;
/// Whether to disable fading on window open/close. /// Whether to disable fading on window open/close.
bool no_fading_openclose; bool no_fading_openclose;
/// Whether to disable fading on ARGB managed destroyed windows.
bool no_fading_destroyed_argb;
/// Fading blacklist. A linked list of conditions. /// Fading blacklist. A linked list of conditions.
c2_lptr_t *fade_blacklist; c2_lptr_t *fade_blacklist;
@ -672,6 +697,10 @@ typedef struct _options_t {
// === Focus related === // === Focus related ===
/// Consider windows of specific types to be always focused. /// Consider windows of specific types to be always focused.
bool wintype_focus[NUM_WINTYPES]; bool wintype_focus[NUM_WINTYPES];
/// Whether to try to detect WM windows and mark them as focused.
bool mark_wmwin_focused;
/// Whether to mark override-redirect windows as focused.
bool mark_ovredir_focused;
/// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window. /// Whether to use EWMH _NET_ACTIVE_WINDOW to find active window.
bool use_ewmh_active_win; bool use_ewmh_active_win;
/// A list of windows always to be considered focused. /// A list of windows always to be considered focused.
@ -1965,6 +1994,14 @@ win_is_fullscreen(session_t *ps, const win *w) {
&& (!w->bounding_shaped || w->rounded_corners); && (!w->bounding_shaped || w->rounded_corners);
} }
/**
* Check if a window will be painted solid.
*/
static inline bool
win_is_solid(session_t *ps, const win *w) {
return WMODE_SOLID == w->mode && !ps->o.force_win_blend;
}
/** /**
* Determine if a window has a specific property. * Determine if a window has a specific property.
* *
@ -2073,6 +2110,13 @@ glx_on_root_change(session_t *ps);
bool bool
glx_init_blur(session_t *ps); glx_init_blur(session_t *ps);
#ifdef CONFIG_VSYNC_OPENGL_GLSL
bool
glx_load_prog_main(session_t *ps,
const char *vshader_str, const char *fshader_str,
glx_prog_main_t *pprogram);
#endif
bool bool
glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap, glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
unsigned width, unsigned height, unsigned depth); unsigned width, unsigned height, unsigned depth);
@ -2108,10 +2152,24 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg); GLfloat factor, XserverRegion reg_tgt, const reg_data_t *pcache_reg);
bool bool
glx_render(session_t *ps, const glx_texture_t *ptex, glx_render_(session_t *ps, const glx_texture_t *ptex,
int x, int y, int dx, int dy, int width, int height, int z, int x, int y, int dx, int dy, int width, int height, int z,
double opacity, bool neg, double opacity, bool argb, bool neg,
XserverRegion reg_tgt, const reg_data_t *pcache_reg); XserverRegion reg_tgt, const reg_data_t *pcache_reg
#ifdef CONFIG_VSYNC_OPENGL_GLSL
, const glx_prog_main_t *pprogram
#endif
);
#ifdef CONFIG_VSYNC_OPENGL_GLSL
#define \
glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \
glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram)
#else
#define \
glx_render(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg, pprogram) \
glx_render_(ps, ptex, x, y, dx, dy, width, height, z, opacity, argb, neg, reg_tgt, pcache_reg)
#endif
void void
glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg); glx_swap_copysubbuffermesa(session_t *ps, XserverRegion reg);
@ -2122,6 +2180,10 @@ glx_create_shader(GLenum shader_type, const char *shader_str);
GLuint GLuint
glx_create_program(const GLuint * const shaders, int nshaders); glx_create_program(const GLuint * const shaders, int nshaders);
GLuint
glx_create_program_from_str(const char *vert_shader_str,
const char *frag_shader_str);
#endif #endif
/** /**

View File

@ -1206,7 +1206,7 @@ paint_preprocess(session_t *ps, win *list) {
// If the window is solid, we add the window region to the // If the window is solid, we add the window region to the
// ignored region // ignored region
if (WMODE_SOLID == w->mode) { if (win_is_solid(ps, w)) {
if (!w->frame_opacity) { if (!w->frame_opacity) {
if (w->border_size) if (w->border_size)
w->reg_ignore = copy_region(ps, w->border_size); w->reg_ignore = copy_region(ps, w->border_size);
@ -1240,7 +1240,7 @@ paint_preprocess(session_t *ps, win *list) {
// is not correctly set. // is not correctly set.
if (ps->o.unredir_if_possible && is_highest && to_paint) { if (ps->o.unredir_if_possible && is_highest && to_paint) {
is_highest = false; is_highest = false;
if (WMODE_SOLID == w->mode if (win_is_solid(ps, w)
&& (!w->frame_opacity || !win_has_frame(w)) && (!w->frame_opacity || !win_has_frame(w))
&& win_is_fullscreen(ps, w) && win_is_fullscreen(ps, w)
&& !w->unredir_if_possible_excluded) && !w->unredir_if_possible_excluded)
@ -1316,7 +1316,7 @@ win_paint_shadow(session_t *ps, win *w,
render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy, render(ps, 0, 0, w->a.x + w->shadow_dx, w->a.y + w->shadow_dy,
w->shadow_width, w->shadow_height, w->shadow_opacity, true, false, w->shadow_width, w->shadow_height, w->shadow_opacity, true, false,
w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg); w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, pcache_reg, NULL);
} }
/** /**
@ -1472,7 +1472,7 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
// Minimize the region we try to blur, if the window itself is not // Minimize the region we try to blur, if the window itself is not
// opaque, only the frame is. // opaque, only the frame is.
XserverRegion reg_noframe = None; XserverRegion reg_noframe = None;
if (WMODE_SOLID == w->mode) { if (win_is_solid(ps, w)) {
XserverRegion reg_all = border_size(ps, w, false); XserverRegion reg_all = border_size(ps, w, false);
reg_noframe = win_get_region_noframe(ps, w, false); reg_noframe = win_get_region_noframe(ps, w, false);
XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe); XFixesSubtractRegion(ps->dpy, reg_noframe, reg_all, reg_noframe);
@ -1496,10 +1496,14 @@ win_blur_background(session_t *ps, win *w, Picture tgt_buffer,
} }
static void static void
render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
double opacity, bool argb, bool neg, double opacity, bool argb, bool neg,
Picture pict, glx_texture_t *ptex, Picture pict, glx_texture_t *ptex,
XserverRegion reg_paint, const reg_data_t *pcache_reg) { XserverRegion reg_paint, const reg_data_t *pcache_reg
#ifdef CONFIG_VSYNC_OPENGL_GLSL
, const glx_prog_main_t *pprogram
#endif
) {
switch (ps->o.backend) { switch (ps->o.backend) {
case BKEND_XRENDER: case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: case BKEND_XR_GLX_HYBRID:
@ -1515,7 +1519,7 @@ render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
#ifdef CONFIG_VSYNC_OPENGL #ifdef CONFIG_VSYNC_OPENGL
case BKEND_GLX: case BKEND_GLX:
glx_render(ps, ptex, x, y, dx, dy, wid, hei, glx_render(ps, ptex, x, y, dx, dy, wid, hei,
ps->glx_z, opacity, neg, reg_paint, pcache_reg); ps->glx_z, opacity, argb, neg, reg_paint, pcache_reg, pprogram);
ps->glx_z += 1; ps->glx_z += 1;
break; break;
#endif #endif
@ -1911,7 +1915,7 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t
if (!is_region_empty(ps, reg_paint, &cache_reg)) { if (!is_region_empty(ps, reg_paint, &cache_reg)) {
set_tgt_clip(ps, reg_paint, &cache_reg); set_tgt_clip(ps, reg_paint, &cache_reg);
// Blur window background // Blur window background
if (w->blur_background && (WMODE_SOLID != w->mode if (w->blur_background && (!win_is_solid(ps, w)
|| (ps->o.blur_background_frame && w->frame_opacity))) { || (ps->o.blur_background_frame && w->frame_opacity))) {
win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg); win_blur_background(ps, w, ps->tgt_buffer.pict, reg_paint, &cache_reg);
} }
@ -1992,7 +1996,8 @@ paint_all(session_t *ps, XserverRegion region, XserverRegion region_real, win *t
glFlush(); glFlush();
glXWaitX(); glXWaitX();
glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0,
ps->root_width, ps->root_height, 0, 1.0, false, region_real, NULL); ps->root_width, ps->root_height, 0, 1.0, false, false,
region_real, NULL, NULL);
// No break here! // No break here!
case BKEND_GLX: case BKEND_GLX:
if (ps->o.glx_use_copysubbuffermesa) if (ps->o.glx_use_copysubbuffermesa)
@ -2396,6 +2401,12 @@ win_determine_fade(session_t *ps, win *w) {
w->fade = w->fade_force; w->fade = w->fade_force;
else if (ps->o.no_fading_openclose && w->in_openclose) else if (ps->o.no_fading_openclose && w->in_openclose)
w->fade = false; w->fade = false;
else if (ps->o.no_fading_destroyed_argb && w->destroyed
&& WMODE_ARGB == w->mode && w->client_win && w->client_win != w->id) {
w->fade = false;
// Prevent it from being overwritten by last-paint value
w->fade_last = false;
}
// Ignore other possible causes of fading state changes after window // Ignore other possible causes of fading state changes after window
// gets unmapped // gets unmapped
else if (IsViewable != w->a.map_state) { else if (IsViewable != w->a.map_state) {
@ -2548,8 +2559,8 @@ win_set_blur_background(session_t *ps, win *w, bool blur_background_new) {
// Only consider window damaged if it's previously painted with background // Only consider window damaged if it's previously painted with background
// blurred // blurred
if ((WMODE_SOLID != w->mode if (!win_is_solid(ps, w)
|| (ps->o.blur_background_frame && w->frame_opacity))) || (ps->o.blur_background_frame && w->frame_opacity))
add_damage_win(ps, w); add_damage_win(ps, w);
} }
@ -3206,6 +3217,9 @@ destroy_win(session_t *ps, Window id) {
w->destroyed = true; w->destroyed = true;
if (ps->o.no_fading_destroyed_argb)
win_determine_fade(ps, w);
// Set fading callback // Set fading callback
set_fade_callback(ps, w, destroy_callback, false); set_fade_callback(ps, w, destroy_callback, false);
@ -4482,6 +4496,9 @@ usage(int ret) {
" Mark windows that have no WM frame as active.\n" " Mark windows that have no WM frame as active.\n"
"--no-fading-openclose\n" "--no-fading-openclose\n"
" Do not fade on window open/close.\n" " Do not fade on window open/close.\n"
"--no-fading-destroyed-argb\n"
" Do not fade destroyed ARGB windows with WM frame. Workaround of bugs\n"
" in Openbox, Fluxbox, etc.\n"
"--shadow-ignore-shaped\n" "--shadow-ignore-shaped\n"
" Do not paint shadows on shaped windows. (Deprecated, use\n" " Do not paint shadows on shaped windows. (Deprecated, use\n"
" --shadow-exclude \'bounding_shaped\' or\n" " --shadow-exclude \'bounding_shaped\' or\n"
@ -4674,6 +4691,12 @@ usage(int ret) {
#else #else
#define WARNING #define WARNING
#endif #endif
"--glx-fshader-win shader\n"
" GLX backend: Use specified GLSL fragment shader for rendering window\n"
" contents.\n"
"--force-win-blend\n"
" Force all windows to be painted with blending. Useful if you have a\n"
" --glx-fshader-win that could turn opaque pixels transparent.\n"
"--dbus\n" "--dbus\n"
" Enable remote control via D-Bus. See the D-BUS API section in the\n" " Enable remote control via D-Bus. See the D-BUS API section in the\n"
" man page for more details." WARNING "\n" " man page for more details." WARNING "\n"
@ -5367,6 +5390,9 @@ parse_config(session_t *ps, struct options_tmp *pcfgtmp) {
wintype_arr_enable(ps->o.wintype_fade); wintype_arr_enable(ps->o.wintype_fade);
// --no-fading-open-close // --no-fading-open-close
lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose); lcfg_lookup_bool(&cfg, "no-fading-openclose", &ps->o.no_fading_openclose);
// --no-fading-destroyed-argb
lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb",
&ps->o.no_fading_destroyed_argb);
// --shadow-red // --shadow-red
config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red); config_lookup_float(&cfg, "shadow-red", &ps->o.shadow_red);
// --shadow-green // --shadow-green
@ -5583,6 +5609,9 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
{ "xrender-sync", no_argument, NULL, 312 }, { "xrender-sync", no_argument, NULL, 312 },
{ "xrender-sync-fence", no_argument, NULL, 313 }, { "xrender-sync-fence", no_argument, NULL, 313 },
{ "show-all-xerrors", no_argument, NULL, 314 }, { "show-all-xerrors", no_argument, NULL, 314 },
{ "no-fading-destroyed-argb", no_argument, NULL, 315 },
{ "force-win-blend", no_argument, NULL, 316 },
{ "glx-fshader-win", required_argument, NULL, 317 },
// Must terminate with a NULL entry // Must terminate with a NULL entry
{ NULL, 0, NULL, 0 }, { NULL, 0, NULL, 0 },
}; };
@ -5838,6 +5867,11 @@ get_cfg(session_t *ps, int argc, char *const *argv, bool first_pass) {
P_CASEBOOL(311, vsync_use_glfinish); P_CASEBOOL(311, vsync_use_glfinish);
P_CASEBOOL(312, xrender_sync); P_CASEBOOL(312, xrender_sync);
P_CASEBOOL(313, xrender_sync_fence); P_CASEBOOL(313, xrender_sync_fence);
P_CASEBOOL(315, no_fading_destroyed_argb);
P_CASEBOOL(316, force_win_blend);
case 317:
ps->o.glx_fshader_win_str = mstrcpy(optarg);
break;
default: default:
usage(1); usage(1);
break; break;
@ -6776,6 +6810,9 @@ session_init(session_t *ps_old, int argc, char **argv) {
.backend = BKEND_XRENDER, .backend = BKEND_XRENDER,
.glx_no_stencil = false, .glx_no_stencil = false,
.glx_copy_from_front = false, .glx_copy_from_front = false,
#ifdef CONFIG_VSYNC_OPENGL_GLSL
.glx_prog_win = GLX_PROG_MAIN_INIT,
#endif
.mark_wmwin_focused = false, .mark_wmwin_focused = false,
.mark_ovredir_focused = false, .mark_ovredir_focused = false,
.fork_after_register = false, .fork_after_register = false,
@ -6818,6 +6855,7 @@ session_init(session_t *ps_old, int argc, char **argv) {
.fade_out_step = 0.03 * OPAQUE, .fade_out_step = 0.03 * OPAQUE,
.fade_delta = 10, .fade_delta = 10,
.no_fading_openclose = false, .no_fading_openclose = false,
.no_fading_destroyed_argb = false,
.fade_blacklist = NULL, .fade_blacklist = NULL,
.wintype_opacity = { 0.0 }, .wintype_opacity = { 0.0 },
@ -7146,6 +7184,17 @@ session_init(session_t *ps_old, int argc, char **argv) {
#endif #endif
} }
// Initialize window GL shader
if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) {
#ifdef CONFIG_VSYNC_OPENGL_GLSL
if (!glx_load_prog_main(ps, NULL, ps->o.glx_fshader_win_str, &ps->o.glx_prog_win))
exit(1);
#else
printf_errf("(): GLSL supported not compiled in, can't load shader.");
exit(1);
#endif
}
// Initialize software optimization // Initialize software optimization
if (ps->o.sw_opti) if (ps->o.sw_opti)
ps->o.sw_opti = swopti_init(ps); ps->o.sw_opti = swopti_init(ps);
@ -7227,7 +7276,6 @@ session_init(session_t *ps_old, int argc, char **argv) {
cxfree(children); cxfree(children);
} }
if (ps->o.track_focus) { if (ps->o.track_focus) {
recheck_focus(ps); recheck_focus(ps);
} }
@ -7395,6 +7443,7 @@ session_destroy(session_t *ps) {
free(ps->pfds_read); free(ps->pfds_read);
free(ps->pfds_write); free(ps->pfds_write);
free(ps->pfds_except); free(ps->pfds_except);
free(ps->o.glx_fshader_win_str);
free_xinerama_info(ps); free_xinerama_info(ps);
#ifdef CONFIG_VSYNC_OPENGL #ifdef CONFIG_VSYNC_OPENGL

View File

@ -675,20 +675,37 @@ static win *
paint_preprocess(session_t *ps, win *list); paint_preprocess(session_t *ps, win *list);
static void static void
render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, render_(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
double opacity, bool argb, bool neg, double opacity, bool argb, bool neg,
Picture pict, glx_texture_t *ptex, Picture pict, glx_texture_t *ptex,
XserverRegion reg_paint, const reg_data_t *pcache_reg); XserverRegion reg_paint, const reg_data_t *pcache_reg
#ifdef CONFIG_VSYNC_OPENGL_GLSL
, const glx_prog_main_t *pprogram
#endif
);
#ifdef CONFIG_VSYNC_OPENGL_GLSL
#define \
render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \
render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram)
#else
#define \
render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg, pprogram) \
render_(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, ptex, reg_paint, pcache_reg)
#endif
static inline void static inline void
win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg, Picture pict) { win_render(session_t *ps, win *w, int x, int y, int wid, int hei,
double opacity, XserverRegion reg_paint, const reg_data_t *pcache_reg,
Picture pict) {
const int dx = (w ? w->a.x: 0) + x; const int dx = (w ? w->a.x: 0) + x;
const int dy = (w ? w->a.y: 0) + y; const int dy = (w ? w->a.y: 0) + y;
const bool argb = (w && w->mode == WMODE_ARGB); const bool argb = (w && (WMODE_ARGB == w->mode || ps->o.force_win_blend));
const bool neg = (w && w->invert_color); const bool neg = (w && w->invert_color);
render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg,
pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint, pcache_reg); pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex),
reg_paint, pcache_reg, (w ? &ps->o.glx_prog_win: NULL));
} }
static inline void static inline void

View File

@ -259,6 +259,23 @@ glx_init_end:
return success; return success;
} }
#ifdef CONFIG_VSYNC_OPENGL_GLSL
static void
glx_free_prog_main(session_t *ps, 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;
}
#endif
/** /**
* Destroy GLX related resources. * Destroy GLX related resources.
*/ */
@ -273,6 +290,10 @@ glx_destroy(session_t *ps) {
if (ppass->prog) if (ppass->prog)
glDeleteProgram(ppass->prog); glDeleteProgram(ppass->prog);
} }
glx_free_prog_main(ps, &ps->o.glx_prog_win);
glx_check_err(ps);
#endif #endif
// Free FBConfigs // Free FBConfigs
@ -439,6 +460,7 @@ glx_init_blur(session_t *ps) {
P_GET_UNIFM_LOC("offset_x", unifm_offset_x); P_GET_UNIFM_LOC("offset_x", unifm_offset_x);
P_GET_UNIFM_LOC("offset_y", unifm_offset_y); P_GET_UNIFM_LOC("offset_y", unifm_offset_y);
} }
#undef P_GET_UNIFM_LOC #undef P_GET_UNIFM_LOC
} }
free(extension); free(extension);
@ -458,6 +480,43 @@ glx_init_blur(session_t *ps) {
#endif #endif
} }
#ifdef CONFIG_VSYNC_OPENGL_GLSL
/**
* Load a GLSL main program from shader strings.
*/
bool
glx_load_prog_main(session_t *ps,
const char *vshader_str, const char *fshader_str,
glx_prog_main_t *pprogram) {
assert(pprogram);
// Build program
pprogram->prog = glx_create_program_from_str(vshader_str, fshader_str);
if (!pprogram->prog) {
printf_errf("(): 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) { \
printf_errf("(): 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);
#undef P_GET_UNIFM_LOC
glx_check_err(ps);
return true;
}
#endif
/** /**
* @brief Update the FBConfig of given depth. * @brief Update the FBConfig of given depth.
*/ */
@ -1314,10 +1373,14 @@ glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z,
* @brief Render a region with texture data. * @brief Render a region with texture data.
*/ */
bool bool
glx_render(session_t *ps, const glx_texture_t *ptex, glx_render_(session_t *ps, const glx_texture_t *ptex,
int x, int y, int dx, int dy, int width, int height, int z, int x, int y, int dx, int dy, int width, int height, int z,
double opacity, bool neg, double opacity, bool argb, bool neg,
XserverRegion reg_tgt, const reg_data_t *pcache_reg) { XserverRegion reg_tgt, const reg_data_t *pcache_reg
#ifdef CONFIG_VSYNC_OPENGL_GLSL
, const glx_prog_main_t *pprogram
#endif
) {
if (!ptex || !ptex->texture) { if (!ptex || !ptex->texture) {
printf_errf("(): Missing texture."); printf_errf("(): Missing texture.");
return false; return false;
@ -1328,8 +1391,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex,
return true; return true;
#endif #endif
const bool argb = (GLX_TEXTURE_FORMAT_RGBA_EXT == argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT ==
ps->glx_fbconfigs[ptex->depth]->texture_fmt); ps->glx_fbconfigs[ptex->depth]->texture_fmt);
#ifdef CONFIG_VSYNC_OPENGL_GLSL
const bool has_prog = pprogram && pprogram->prog;
#endif
bool dual_texture = false; bool dual_texture = false;
// It's required by legacy versions of OpenGL to enable texture target // It's required by legacy versions of OpenGL to enable texture target
@ -1350,77 +1416,96 @@ glx_render(session_t *ps, const glx_texture_t *ptex,
glColor4f(opacity, opacity, opacity, opacity); glColor4f(opacity, opacity, opacity, opacity);
} }
// Color negation #ifdef CONFIG_VSYNC_OPENGL_GLSL
if (neg) { if (!has_prog)
// Simple color negation #endif
if (!glIsEnabled(GL_BLEND)) { {
glEnable(GL_COLOR_LOGIC_OP); // The default, fixed-function path
glLogicOp(GL_COPY_INVERTED); // Color negation
} if (neg) {
// ARGB texture color negation // Simple color negation
else if (argb) { if (!glIsEnabled(GL_BLEND)) {
dual_texture = true; 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, // Use two texture stages because the calculation is too complicated,
// thanks to madsy for providing code // thanks to madsy for providing code
// Texture stage 0 // Texture stage 0
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
// Negation for premultiplied color: color = A - C // Negation for premultiplied color: color = A - C
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Pass texture alpha through // Pass texture alpha through
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); 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); // Texture stage 1
glActiveTexture(GL_TEXTURE1);
glEnable(ptex->target);
glBindTexture(ptex->target, ptex->texture);
// Modulation with constant factor glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
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 // Modulation with constant factor
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0); // Modulation with constant factor
} glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
// RGB blend color negation glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
else { glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
// Modulation with constant factor glActiveTexture(GL_TEXTURE0);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); }
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE); // RGB blend color negation
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); else {
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
// Modulation with constant factor // Modulation with constant factor
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); 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);
}
} }
} }
#ifdef CONFIG_VSYNC_OPENGL_GLSL
else {
// Programmable path
assert(pprogram->prog);
glUseProgram(pprogram->prog);
if (pprogram->unifm_opacity >= 0)
glUniform1f(pprogram->unifm_opacity, opacity);
if (pprogram->unifm_invert_color >= 0)
glUniform1i(pprogram->unifm_invert_color, neg);
if (pprogram->unifm_tex >= 0)
glUniform1i(pprogram->unifm_tex, 0);
}
#endif
#ifdef DEBUG_GLX #ifdef DEBUG_GLX
printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z); printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n", x, y, width, height, dx, dy, ptex->width, ptex->height, z);
@ -1503,6 +1588,11 @@ glx_render(session_t *ps, const glx_texture_t *ptex,
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
#ifdef CONFIG_VSYNC_OPENGL_GLSL
if (has_prog)
glUseProgram(0);
#endif
glx_check_err(ps); glx_check_err(ps);
return true; return true;
@ -1625,7 +1715,7 @@ glx_create_shader(GLenum shader_type, const char *shader_str) {
bool success = false; bool success = false;
GLuint shader = glCreateShader(shader_type); GLuint shader = glCreateShader(shader_type);
if (!shader) { if (!shader) {
printf_errf("(): Failed to create shader with type %d.", shader_type); printf_errf("(): Failed to create shader with type %#x.", shader_type);
goto glx_create_shader_end; goto glx_create_shader_end;
} }
glShaderSource(shader, 1, &shader_str, NULL); glShaderSource(shader, 1, &shader_str, NULL);
@ -1701,5 +1791,40 @@ glx_create_program_end:
return program; return program;
} }
/**
* @brief Create a program from vertex and fragment shader strings.
*/
GLuint
glx_create_program_from_str(const char *vert_shader_str,
const char *frag_shader_str) {
GLuint vert_shader = 0;
GLuint frag_shader = 0;
GLuint prog = 0;
if (vert_shader_str)
vert_shader = glx_create_shader(GL_VERTEX_SHADER, vert_shader_str);
if (frag_shader_str)
frag_shader = glx_create_shader(GL_FRAGMENT_SHADER, frag_shader_str);
{
GLuint shaders[2];
int count = 0;
if (vert_shader)
shaders[count++] = vert_shader;
if (frag_shader)
shaders[count++] = frag_shader;
assert(count <= sizeof(shaders) / sizeof(shaders[0]));
if (count)
prog = glx_create_program(shaders, count);
}
if (vert_shader)
glDeleteShader(vert_shader);
if (frag_shader)
glDeleteShader(frag_shader);
return prog;
}
#endif #endif