Merge pull request #76 from yshui/split-backends-part1
First step of split backend into modules
This commit is contained in:
commit
068611e2e7
|
@ -27,7 +27,7 @@ commands:
|
|||
- ".git"
|
||||
- run:
|
||||
name: config
|
||||
command: CC=<< parameters.cc >> meson << parameters.build-config >> --werror . build
|
||||
command: CC=<< parameters.cc >> meson << parameters.build-config >> -Dnew_backends=true --werror . build
|
||||
- run:
|
||||
name: build
|
||||
command: ninja -C build
|
||||
|
|
|
@ -5,9 +5,11 @@ option('regex', type: 'boolean', value: true, description: 'Enable regex support
|
|||
|
||||
option('vsync_drm', type: 'boolean', value: false, description: 'Enable support for using drm for vsync')
|
||||
|
||||
option('opengl', type: 'boolean', value: true, description: 'Enable features that require opengl (opengl backend, and opengl vsync methods)')
|
||||
option('opengl', type: 'boolean', value: false, description: 'Enable features that require opengl (opengl backend, and opengl vsync methods)')
|
||||
option('dbus', type: 'boolean', value: true, description: 'Enable suport for D-Bus remote control')
|
||||
|
||||
option('xrescheck', type: 'boolean', value: false, description: 'Enable X resource leak checker (for debug only)')
|
||||
|
||||
option('build_docs', type: 'boolean', value: false, description: 'Build documentation and man pages')
|
||||
|
||||
option('new_backends', type: 'boolean', value: false, description: 'Does not really do anything right now')
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#include "backend.h"
|
||||
|
||||
backend_info_t *backend_list[NUM_BKEND] = {[BKEND_XRENDER] = &xrender_backend};
|
||||
|
||||
bool default_is_win_transparent(void *backend_data, win *w, void *win_data) {
|
||||
return w->mode != WMODE_SOLID;
|
||||
}
|
||||
|
||||
bool default_is_frame_transparent(void *backend_data, win *w, void *win_data) {
|
||||
return w->frame_opacity != 1;
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) 2018, Yuxuan Shui <yshuiv7@gmail.com>
|
||||
|
||||
#pragma once
|
||||
#include "common.h"
|
||||
#include "region.h"
|
||||
|
||||
typedef struct backend_info {
|
||||
|
||||
// =========== Initialization ===========
|
||||
|
||||
/// Initialize the backend, prepare for rendering to the target window.
|
||||
/// Here is how you should choose target window:
|
||||
/// 1) if ps->overlay is not XCB_NONE, use that
|
||||
/// 2) use ps->root otherwise
|
||||
/// XXX make the target window a parameter
|
||||
void *(*init)(session_t *ps) __attribute__((nonnull(1)));
|
||||
void (*deinit)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2)));
|
||||
|
||||
/// Called when rendering will be stopped for an unknown amount of
|
||||
/// time (e.g. screen is unredirected). Free some resources.
|
||||
void (*pause)(void *backend_data, session_t *ps);
|
||||
|
||||
/// Called before rendering is resumed
|
||||
void (*resume)(void *backend_data, session_t *ps);
|
||||
|
||||
/// Called when root property changed, returns the new
|
||||
/// backend_data. Even if the backend_data changed, all
|
||||
/// the existing win_data returned by prepare_win should
|
||||
/// remain valid.
|
||||
///
|
||||
/// Optional
|
||||
void *(*root_change)(void *backend_data, session_t *ps);
|
||||
|
||||
// =========== Rendering ============
|
||||
|
||||
/// Called before any compose() calls.
|
||||
///
|
||||
/// Usually the backend should clear the buffer, or paint a background
|
||||
/// on the buffer (usually the wallpaper).
|
||||
///
|
||||
/// Optional?
|
||||
void (*prepare)(void *backend_data, session_t *ps, const region_t *reg_paint);
|
||||
|
||||
/// Paint the content of the window onto the (possibly buffered)
|
||||
/// target picture. Always called after render_win(). Maybe called
|
||||
/// multiple times between render_win() and finish_render_win().
|
||||
/// The origin is the top left of the window, exclude the shadow,
|
||||
/// (dst_x, dst_y) refers to where the origin should be in the target
|
||||
/// buffer.
|
||||
void (*compose)(void *backend_data, session_t *ps, win *w, void *win_data,
|
||||
int dst_x, int dst_y, const region_t *reg_paint);
|
||||
|
||||
/// Blur a given region on of the target.
|
||||
bool (*blur)(void *backend_data, session_t *ps, double opacity, const region_t *)
|
||||
__attribute__((nonnull(1, 2, 4)));
|
||||
|
||||
/// Present the buffered target picture onto the screen. If target
|
||||
/// is not buffered, this should be NULL.
|
||||
///
|
||||
/// Optional
|
||||
void (*present)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2)));
|
||||
|
||||
/**
|
||||
* Render the content of a window into an opaque
|
||||
* data structure. Dimming, shadow and color inversion is handled
|
||||
* here.
|
||||
*
|
||||
* This function is allowed to allocate additional resource needed
|
||||
* for rendering.
|
||||
*
|
||||
* Params:
|
||||
* reg_paint = the paint region, meaning painting should only
|
||||
* be happening within that region. It's in global
|
||||
* coordinates. If NULL, the region of paint is the
|
||||
* whole screen.
|
||||
*/
|
||||
void (*render_win)(void *backend_data, session_t *ps, win *w, void *win_data,
|
||||
const region_t *reg_paint);
|
||||
|
||||
/// Free resource allocated for rendering. After this function is
|
||||
/// called, compose() won't be called before render_win is called
|
||||
/// another time.
|
||||
///
|
||||
/// Optional
|
||||
void (*finish_render_win)(void *backend_data, session_t *ps, win *w,
|
||||
void *win_data);
|
||||
|
||||
// ============ Resource management ===========
|
||||
|
||||
// XXX Thoughts: calling release_win and prepare_win for every config notify
|
||||
// is wasteful, since there can be multiple such notifies per drawing.
|
||||
// But if we don't, it can mean there will be a state where is window is
|
||||
// mapped and visible, but there is no win_data attached to it. We don't
|
||||
// want to break that assumption.
|
||||
|
||||
/// Create a structure to stored additional data needed for rendering a
|
||||
/// window, later used for render() and compose().
|
||||
///
|
||||
/// Backend can assume this function will only be called with visible
|
||||
/// InputOutput windows, and only be called when screen is redirected.
|
||||
///
|
||||
/// Backend can assume size, shape and visual of the window won't change between
|
||||
/// prepare_win() and release_win().
|
||||
void *(*prepare_win)(void *backend_data, session_t *ps, win *w)
|
||||
__attribute__((nonnull(1, 2, 3)));
|
||||
|
||||
/// Free resources allocated by prepare()
|
||||
void (*release_win)(void *backend_data, session_t *ps, win *w, void *win_data)
|
||||
__attribute__((nonnull(1, 2, 3)));
|
||||
|
||||
// =========== Query ===========
|
||||
|
||||
/// Return if a window has transparent content. Guaranteed to only
|
||||
/// be called after render_win is called.
|
||||
bool (*is_win_transparent)(void *backend_data, win *w, void *win_data)
|
||||
__attribute__((nonnull(1, 2)));
|
||||
|
||||
/// Return if the frame window has transparent content. Guaranteed to
|
||||
/// only be called after render_win is called.
|
||||
bool (*is_frame_transparent)(void *backend_data, win *w, void *win_data)
|
||||
__attribute__((nonnull(1, 2)));
|
||||
} backend_info_t;
|
||||
|
||||
extern backend_info_t xrender_backend;
|
||||
extern backend_info_t glx_backend;
|
||||
extern backend_info_t *backend_list[NUM_BKEND];
|
||||
|
||||
bool default_is_win_transparent(void *, win *, void *);
|
||||
bool default_is_frame_transparent(void *, win *, void *);
|
|
@ -0,0 +1,116 @@
|
|||
#include <xcb/xcb_image.h>
|
||||
|
||||
#include "render.h"
|
||||
#include "backend_common.h"
|
||||
|
||||
/**
|
||||
* Generate a 1x1 <code>Picture</code> of a particular color.
|
||||
*/
|
||||
xcb_render_picture_t
|
||||
solid_picture(session_t *ps, bool argb, double a, double r, double g, double b) {
|
||||
xcb_pixmap_t pixmap;
|
||||
xcb_render_picture_t picture;
|
||||
xcb_render_create_picture_value_list_t pa;
|
||||
xcb_render_color_t col;
|
||||
xcb_rectangle_t rect;
|
||||
|
||||
pixmap = x_create_pixmap(ps, argb ? 32 : 8, ps->root, 1, 1);
|
||||
if (!pixmap)
|
||||
return None;
|
||||
|
||||
pa.repeat = True;
|
||||
picture = x_create_picture_with_standard_and_pixmap(
|
||||
ps, argb ? XCB_PICT_STANDARD_ARGB_32 : XCB_PICT_STANDARD_A_8, pixmap,
|
||||
XCB_RENDER_CP_REPEAT, &pa);
|
||||
|
||||
if (!picture) {
|
||||
xcb_free_pixmap(ps->c, pixmap);
|
||||
return None;
|
||||
}
|
||||
|
||||
col.alpha = a * 0xffff;
|
||||
col.red = r * 0xffff;
|
||||
col.green = g * 0xffff;
|
||||
col.blue = b * 0xffff;
|
||||
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.width = 1;
|
||||
rect.height = 1;
|
||||
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, picture, col, 1, &rect);
|
||||
xcb_free_pixmap(ps->c, pixmap);
|
||||
|
||||
return picture;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate shadow <code>Picture</code> for a window.
|
||||
*/
|
||||
bool build_shadow(session_t *ps, double opacity, const int width, const int height,
|
||||
xcb_render_picture_t shadow_pixel, xcb_pixmap_t *pixmap,
|
||||
xcb_render_picture_t *pict) {
|
||||
xcb_image_t *shadow_image = NULL;
|
||||
xcb_pixmap_t shadow_pixmap = None, shadow_pixmap_argb = None;
|
||||
xcb_render_picture_t shadow_picture = None, shadow_picture_argb = None;
|
||||
xcb_gcontext_t gc = None;
|
||||
|
||||
shadow_image = make_shadow(ps, opacity, width, height);
|
||||
if (!shadow_image) {
|
||||
log_error("Failed to make shadow");
|
||||
return false;
|
||||
}
|
||||
|
||||
shadow_pixmap =
|
||||
x_create_pixmap(ps, 8, ps->root, shadow_image->width, shadow_image->height);
|
||||
shadow_pixmap_argb =
|
||||
x_create_pixmap(ps, 32, ps->root, shadow_image->width, shadow_image->height);
|
||||
|
||||
if (!shadow_pixmap || !shadow_pixmap_argb) {
|
||||
log_error("Failed to create shadow pixmaps");
|
||||
goto shadow_picture_err;
|
||||
}
|
||||
|
||||
shadow_picture = x_create_picture_with_standard_and_pixmap(
|
||||
ps, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
|
||||
shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
|
||||
ps, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
|
||||
if (!shadow_picture || !shadow_picture_argb)
|
||||
goto shadow_picture_err;
|
||||
|
||||
gc = xcb_generate_id(ps->c);
|
||||
xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);
|
||||
|
||||
xcb_image_put(ps->c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, shadow_pixel, shadow_picture,
|
||||
shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width,
|
||||
shadow_image->height);
|
||||
|
||||
*pixmap = shadow_pixmap_argb;
|
||||
*pict = shadow_picture_argb;
|
||||
|
||||
xcb_free_gc(ps->c, gc);
|
||||
xcb_image_destroy(shadow_image);
|
||||
xcb_free_pixmap(ps->c, shadow_pixmap);
|
||||
xcb_render_free_picture(ps->c, shadow_picture);
|
||||
|
||||
return true;
|
||||
|
||||
shadow_picture_err:
|
||||
if (shadow_image)
|
||||
xcb_image_destroy(shadow_image);
|
||||
if (shadow_pixmap)
|
||||
xcb_free_pixmap(ps->c, shadow_pixmap);
|
||||
if (shadow_pixmap_argb)
|
||||
xcb_free_pixmap(ps->c, shadow_pixmap_argb);
|
||||
if (shadow_picture)
|
||||
xcb_render_free_picture(ps->c, shadow_picture);
|
||||
if (shadow_picture_argb)
|
||||
xcb_render_free_picture(ps->c, shadow_picture_argb);
|
||||
if (gc)
|
||||
xcb_free_gc(ps->c, gc);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// vim: set noet sw=8 ts=8 :
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
#include <xcb/xcb_image.h>
|
||||
#include "common.h"
|
||||
|
||||
bool build_shadow(session_t *ps, double opacity, const int width, const int height,
|
||||
xcb_render_picture_t shadow_pixel, xcb_pixmap_t *pixmap,
|
||||
xcb_render_picture_t *pict);
|
||||
|
||||
xcb_render_picture_t
|
||||
solid_picture(session_t *ps, bool argb, double a, double r, double g, double b);
|
|
@ -0,0 +1,575 @@
|
|||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "log.h"
|
||||
|
||||
#include "backend/gl/gl_common.h"
|
||||
|
||||
GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
|
||||
log_trace("===\n%s\n===", shader_str);
|
||||
|
||||
bool success = false;
|
||||
GLuint shader = glCreateShader(shader_type);
|
||||
if (!shader) {
|
||||
log_error("Failed to create shader with type %#x.", shader_type);
|
||||
goto end;
|
||||
}
|
||||
glShaderSource(shader, 1, &shader_str, NULL);
|
||||
glCompileShader(shader);
|
||||
|
||||
// Get shader status
|
||||
{
|
||||
GLint status = GL_FALSE;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (GL_FALSE == status) {
|
||||
GLint log_len = 0;
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
|
||||
if (log_len) {
|
||||
char log[log_len + 1];
|
||||
glGetShaderInfoLog(shader, log_len, NULL, log);
|
||||
log_error("Failed to compile shader with type %d: %s",
|
||||
shader_type, log);
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (shader && !success) {
|
||||
glDeleteShader(shader);
|
||||
shader = 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
GLuint gl_create_program(const GLuint *const shaders, int nshaders) {
|
||||
bool success = false;
|
||||
GLuint program = glCreateProgram();
|
||||
if (!program) {
|
||||
log_error("Failed to create program.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nshaders; ++i)
|
||||
glAttachShader(program, shaders[i]);
|
||||
glLinkProgram(program);
|
||||
|
||||
// Get program status
|
||||
{
|
||||
GLint status = GL_FALSE;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
||||
if (GL_FALSE == status) {
|
||||
GLint log_len = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
|
||||
if (log_len) {
|
||||
char log[log_len + 1];
|
||||
glGetProgramInfoLog(program, log_len, NULL, log);
|
||||
log_error("Failed to link program: %s", log);
|
||||
}
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (program) {
|
||||
for (int i = 0; i < nshaders; ++i)
|
||||
glDetachShader(program, shaders[i]);
|
||||
}
|
||||
if (program && !success) {
|
||||
glDeleteProgram(program);
|
||||
program = 0;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a program from vertex and fragment shader strings.
|
||||
*/
|
||||
GLuint
|
||||
gl_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 = gl_create_shader(GL_VERTEX_SHADER, vert_shader_str);
|
||||
if (frag_shader_str)
|
||||
frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, frag_shader_str);
|
||||
|
||||
{
|
||||
GLuint shaders[2];
|
||||
unsigned 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 = gl_create_program(shaders, count);
|
||||
}
|
||||
|
||||
if (vert_shader)
|
||||
glDeleteShader(vert_shader);
|
||||
if (frag_shader)
|
||||
glDeleteShader(frag_shader);
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get tightly packed RGB888 data from GL front buffer.
|
||||
*
|
||||
* Don't expect any sort of decent performance.
|
||||
*
|
||||
* @returns tightly packed RGB888 data of the size of the screen,
|
||||
* to be freed with `free()`
|
||||
*/
|
||||
unsigned char *gl_take_screenshot(session_t *ps, int *out_length) {
|
||||
int length = 3 * ps->root_width * ps->root_height;
|
||||
GLint unpack_align_old = 0;
|
||||
glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_align_old);
|
||||
assert(unpack_align_old > 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
unsigned char *buf = ccalloc(length, unsigned char);
|
||||
glReadBuffer(GL_FRONT);
|
||||
glReadPixels(0, 0, ps->root_width, ps->root_height, GL_RGB, GL_UNSIGNED_BYTE, buf);
|
||||
glReadBuffer(GL_BACK);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_align_old);
|
||||
if (out_length)
|
||||
*out_length = sizeof(unsigned char) * length;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Render a region with texture data.
|
||||
*/
|
||||
bool gl_compose(const gl_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 gl_win_shader_t *shader) {
|
||||
if (!ptex || !ptex->texture) {
|
||||
log_error("Missing texture.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT ==
|
||||
// ps->psglx->fbconfigs[ptex->depth]->texture_fmt);
|
||||
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);
|
||||
glColor4f(opacity, opacity, opacity, opacity);
|
||||
}
|
||||
|
||||
// Programmable path
|
||||
assert(shader->prog);
|
||||
glUseProgram(shader->prog);
|
||||
if (shader->unifm_opacity >= 0)
|
||||
glUniform1f(shader->unifm_opacity, opacity);
|
||||
if (shader->unifm_invert_color >= 0)
|
||||
glUniform1i(shader->unifm_invert_color, neg);
|
||||
if (shader->unifm_tex >= 0)
|
||||
glUniform1i(shader->unifm_tex, 0);
|
||||
|
||||
// log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d\n",
|
||||
// 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) {
|
||||
// Calculate texture coordinates
|
||||
GLfloat texture_x1 = (double)(crect.x1 - dx + x);
|
||||
GLfloat texture_y1 = (double)(crect.y1 - dy + y);
|
||||
GLfloat texture_x2 = texture_x1 + (double)(crect.x2 - crect.x1);
|
||||
GLfloat texture_y2 = texture_y1 + (double)(crect.y2 - crect.y1);
|
||||
|
||||
if (GL_TEXTURE_2D == ptex->target) {
|
||||
// GL_TEXTURE_2D coordinates are 0-1
|
||||
texture_x1 /= ptex->width;
|
||||
texture_y1 /= ptex->height;
|
||||
texture_x2 /= ptex->width;
|
||||
texture_y2 /= ptex->height;
|
||||
}
|
||||
|
||||
// Vertex coordinates
|
||||
GLint vx1 = crect.x1;
|
||||
GLint vy1 = crect.y1;
|
||||
GLint vx2 = crect.x2;
|
||||
GLint vy2 = crect.y2;
|
||||
|
||||
// X pixmaps might be Y inverted, invert the texture coordinates
|
||||
if (ptex->y_inverted) {
|
||||
texture_y1 = 1.0 - texture_y1;
|
||||
texture_y2 = 1.0 - texture_y2;
|
||||
}
|
||||
|
||||
// log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d",
|
||||
// ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
|
||||
|
||||
GLfloat texture_x[] = {texture_x1, texture_x2, texture_x2, texture_x1};
|
||||
GLfloat texture_y[] = {texture_y1, texture_y1, texture_y2, texture_y2};
|
||||
GLint vx[] = {vx1, vx2, vx2, vx1};
|
||||
GLint vy[] = {vy1, vy1, vy2, vy2};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
glTexCoord2f(texture_x[i], texture_y[i]);
|
||||
glVertex3i(vx[i], vy[i], z);
|
||||
}
|
||||
}
|
||||
P_PAINTREG_END();
|
||||
|
||||
// Cleanup
|
||||
glBindTexture(ptex->target, 0);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
glDisable(ptex->target);
|
||||
|
||||
if (dual_texture) {
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(ptex->target, 0);
|
||||
glDisable(ptex->target);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
glUseProgram(0);
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gl_dim_reg(session_t *ps, int dx, int dy, int width, int height, float z,
|
||||
GLfloat factor, const region_t *reg_tgt) {
|
||||
// It's possible to dim in glx_render(), but it would be over-complicated
|
||||
// considering all those mess in color negation and modulation
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, factor);
|
||||
|
||||
{
|
||||
P_PAINTREG_START(crect) {
|
||||
glVertex3i(crect.x1, crect.y1, z);
|
||||
glVertex3i(crect.x2, crect.y1, z);
|
||||
glVertex3i(crect.x2, crect.y2, z);
|
||||
glVertex3i(crect.x1, crect.y2, z);
|
||||
}
|
||||
P_PAINTREG_END();
|
||||
}
|
||||
|
||||
glEnd();
|
||||
|
||||
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline int gl_gen_texture(GLenum tex_tgt, int width, int height, GLuint *tex) {
|
||||
glGenTextures(1, tex);
|
||||
if (!*tex)
|
||||
return -1;
|
||||
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 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur contents in a particular region.
|
||||
*
|
||||
* XXX seems to be way to complex for what it does
|
||||
*/
|
||||
|
||||
// Blur the area sized width x height starting at dx x dy
|
||||
bool gl_blur_dst(session_t *ps, const gl_cap_t *cap, int dx, int dy, int width,
|
||||
int height, float z, GLfloat factor_center, const region_t *reg_tgt,
|
||||
gl_blur_cache_t *pbc, const gl_blur_shader_t *pass, int npasses) {
|
||||
const bool more_passes = npasses > 1;
|
||||
|
||||
// these should be arguments
|
||||
const bool have_scissors = glIsEnabled(GL_SCISSOR_TEST);
|
||||
const bool have_stencil = glIsEnabled(GL_STENCIL_TEST);
|
||||
bool ret = false;
|
||||
|
||||
// Calculate copy region size
|
||||
gl_blur_cache_t ibc = {.width = 0, .height = 0};
|
||||
if (!pbc)
|
||||
pbc = &ibc;
|
||||
|
||||
// log_trace("(): %d, %d, %d, %d\n", dx, dy, width, height);
|
||||
|
||||
GLenum tex_tgt = GL_TEXTURE_RECTANGLE;
|
||||
if (cap->non_power_of_two_texture)
|
||||
tex_tgt = GL_TEXTURE_2D;
|
||||
|
||||
// Free textures if size inconsistency discovered
|
||||
if (width != pbc->width || height != pbc->height) {
|
||||
glDeleteTextures(1, &pbc->textures[0]);
|
||||
glDeleteTextures(1, &pbc->textures[1]);
|
||||
pbc->width = pbc->height = 0;
|
||||
pbc->textures[0] = pbc->textures[1] = 0;
|
||||
}
|
||||
|
||||
// Generate FBO and textures if needed
|
||||
if (!pbc->textures[0])
|
||||
gl_gen_texture(tex_tgt, width, height, &pbc->textures[0]);
|
||||
GLuint tex_scr = pbc->textures[0];
|
||||
if (npasses > 1 && !pbc->textures[1])
|
||||
gl_gen_texture(tex_tgt, width, height, &pbc->textures[1]);
|
||||
pbc->width = width;
|
||||
pbc->height = height;
|
||||
GLuint tex_scr2 = pbc->textures[1];
|
||||
if (npasses > 1 && !pbc->fbo)
|
||||
glGenFramebuffers(1, &pbc->fbo);
|
||||
const GLuint fbo = pbc->fbo;
|
||||
|
||||
if (!tex_scr || (npasses > 1 && !tex_scr2)) {
|
||||
log_error("Failed to allocate texture.");
|
||||
goto end;
|
||||
}
|
||||
if (npasses > 1 && !fbo) {
|
||||
log_error("Failed to allocate framebuffer.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Read destination pixels into a texture
|
||||
glEnable(tex_tgt);
|
||||
glBindTexture(tex_tgt, tex_scr);
|
||||
|
||||
// Copy the area to be blurred into tmp buffer
|
||||
glCopyTexSubImage2D(tex_tgt, 0, 0, 0, dx, dy, width, height);
|
||||
|
||||
// Texture scaling factor
|
||||
GLfloat texfac_x = 1.0f, texfac_y = 1.0f;
|
||||
if (tex_tgt == GL_TEXTURE_2D) {
|
||||
texfac_x /= width;
|
||||
texfac_y /= height;
|
||||
}
|
||||
|
||||
// Paint it back
|
||||
if (more_passes) {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
for (int i = 0; i < npasses; ++i) {
|
||||
assert(i < MAX_BLUR_PASS - 1);
|
||||
const gl_blur_shader_t *curr = &pass[i];
|
||||
assert(curr->prog);
|
||||
|
||||
assert(tex_scr);
|
||||
glBindTexture(tex_tgt, tex_scr);
|
||||
|
||||
if (i < npasses - 1) {
|
||||
// not last pass, draw into framebuffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
// XXX not fixing bug during porting
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, tex_scr2,
|
||||
0); // XXX wrong, should use tex_tgt
|
||||
glDrawBuffers(1, (GLenum[]){GL_COLOR_ATTACHMENT0});
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
|
||||
GL_FRAMEBUFFER_COMPLETE) {
|
||||
log_error("Framebuffer attachment failed.");
|
||||
goto end;
|
||||
}
|
||||
} else {
|
||||
// last pass, draw directly into the back buffer
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDrawBuffers(1, (GLenum[]){GL_BACK});
|
||||
if (have_scissors)
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
if (have_stencil)
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glUseProgram(curr->prog);
|
||||
if (curr->unifm_offset_x >= 0)
|
||||
glUniform1f(curr->unifm_offset_x, texfac_x);
|
||||
if (curr->unifm_offset_y >= 0)
|
||||
glUniform1f(curr->unifm_offset_y, texfac_y);
|
||||
if (curr->unifm_factor_center >= 0)
|
||||
glUniform1f(curr->unifm_factor_center, factor_center);
|
||||
|
||||
// XXX use multiple draw calls is probably going to be slow than
|
||||
// just simply blur the whole area.
|
||||
|
||||
P_PAINTREG_START(crect) {
|
||||
// Texture coordinates
|
||||
const GLfloat texture_x1 = (crect.x1 - dx) * texfac_x;
|
||||
const GLfloat texture_y1 = (crect.y1 - dy) * texfac_y;
|
||||
const GLfloat texture_x2 =
|
||||
texture_x1 + (crect.x2 - crect.x1) * texfac_x;
|
||||
const GLfloat texture_y2 =
|
||||
texture_y1 + (crect.y2 - crect.y1) * texfac_y;
|
||||
|
||||
// Vertex coordinates
|
||||
// For passes before the last one, we are drawing into a buffer,
|
||||
// so (dx, dy) from source maps to (0, 0)
|
||||
GLfloat vx1 = crect.x1 - dx;
|
||||
GLfloat vy1 = crect.y1 - dy;
|
||||
if (i == npasses - 1) {
|
||||
// For last pass, we are drawing back to source, so we
|
||||
// don't need to map
|
||||
vx1 = crect.x1;
|
||||
vy1 = crect.y1;
|
||||
}
|
||||
GLfloat vx2 = vx1 + (crect.x2 - crect.x1);
|
||||
GLfloat vy2 = vy1 + (crect.y2 - crect.y1);
|
||||
|
||||
GLfloat texture_x[] = {texture_x1, texture_x2, texture_x2,
|
||||
texture_x1};
|
||||
GLfloat texture_y[] = {texture_y1, texture_y1, texture_y2,
|
||||
texture_y2};
|
||||
GLint vx[] = {vx1, vx2, vx2, vx1};
|
||||
GLint vy[] = {vy1, vy1, vy2, vy2};
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
glTexCoord2f(texture_x[j], texture_y[j]);
|
||||
glVertex3i(vx[j], vy[j], z);
|
||||
}
|
||||
}
|
||||
P_PAINTREG_END();
|
||||
|
||||
glUseProgram(0);
|
||||
|
||||
// Swap tex_scr and tex_scr2
|
||||
GLuint tmp = tex_scr2;
|
||||
tex_scr2 = tex_scr;
|
||||
tex_scr = tmp;
|
||||
}
|
||||
|
||||
ret = true;
|
||||
|
||||
end:
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(tex_tgt, 0);
|
||||
glDisable(tex_tgt);
|
||||
if (have_scissors)
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
if (have_stencil)
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
|
||||
if (&ibc == pbc) {
|
||||
glDeleteTextures(1, &pbc->textures[0]);
|
||||
glDeleteTextures(1, &pbc->textures[1]);
|
||||
glDeleteFramebuffers(1, &pbc->fbo);
|
||||
}
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clipping region on the target window.
|
||||
*/
|
||||
void gl_set_clip(const region_t *reg) {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
if (!reg)
|
||||
return;
|
||||
|
||||
int nrects;
|
||||
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
|
||||
|
||||
if (nrects == 1) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(rects[0].x1, rects[0].y2, rects[0].x2 - rects[0].x1,
|
||||
rects[0].y2 - rects[0].y1);
|
||||
}
|
||||
|
||||
gl_check_err();
|
||||
}
|
||||
|
||||
static GLint glGetUniformLocationChecked(GLint prog, const char *name) {
|
||||
GLint ret = glGetUniformLocation(prog, name);
|
||||
if (ret < 0)
|
||||
log_error("Failed to get location of uniform '%s'. Might be troublesome.",
|
||||
name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a GLSL main program from shader strings.
|
||||
*/
|
||||
int gl_win_shader_from_string(session_t *ps, const char *vshader_str,
|
||||
const char *fshader_str, gl_win_shader_t *ret) {
|
||||
// Build program
|
||||
ret->prog = gl_create_program_from_str(vshader_str, fshader_str);
|
||||
if (!ret->prog) {
|
||||
log_error("Failed to create GLSL program.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get uniform addresses
|
||||
ret->unifm_opacity = glGetUniformLocationChecked(ret->prog, "opacity");
|
||||
ret->unifm_invert_color = glGetUniformLocationChecked(ret->prog, "invert_color");
|
||||
ret->unifm_tex = glGetUniformLocationChecked(ret->prog, "tex");
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to run on root window size change.
|
||||
*/
|
||||
void gl_resize(int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(0, width, 0, height, -1000.0, 1000.0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
}
|
||||
|
||||
static void attr_unused gl_destroy_win_shader(session_t *ps, gl_win_shader_t *shader) {
|
||||
assert(shader);
|
||||
assert(shader->prog);
|
||||
glDeleteProgram(shader->prog);
|
||||
shader->prog = 0;
|
||||
shader->unifm_opacity = -1;
|
||||
shader->unifm_invert_color = -1;
|
||||
shader->unifm_tex = -1;
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// Program and uniforms for window shader
|
||||
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;
|
||||
} gl_win_shader_t;
|
||||
|
||||
// Program and uniforms for blur shader
|
||||
typedef struct {
|
||||
/// Fragment shader for blur.
|
||||
GLuint frag_shader;
|
||||
/// GLSL program for blur.
|
||||
GLuint prog;
|
||||
/// Location of uniform "offset_x" in blur GLSL program.
|
||||
GLint unifm_offset_x;
|
||||
/// Location of uniform "offset_y" in blur GLSL program.
|
||||
GLint unifm_offset_y;
|
||||
/// Location of uniform "factor_center" in blur GLSL program.
|
||||
GLint unifm_factor_center;
|
||||
} gl_blur_shader_t;
|
||||
|
||||
/// @brief Wrapper of a binded GLX texture.
|
||||
typedef struct gl_texture {
|
||||
GLuint texture;
|
||||
GLenum target;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned depth;
|
||||
bool y_inverted;
|
||||
} gl_texture_t;
|
||||
|
||||
// OpenGL capabilities
|
||||
typedef struct gl_cap {
|
||||
bool non_power_of_two_texture;
|
||||
} gl_cap_t;
|
||||
|
||||
typedef struct {
|
||||
/// Framebuffer used for blurring.
|
||||
GLuint fbo;
|
||||
/// Textures used for blurring.
|
||||
GLuint textures[2];
|
||||
/// Width of the textures.
|
||||
int width;
|
||||
/// Height of the textures.
|
||||
int height;
|
||||
} gl_blur_cache_t;
|
||||
|
||||
#define GL_PROG_MAIN_INIT \
|
||||
{ .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, }
|
||||
|
||||
GLuint gl_create_shader(GLenum shader_type, const char *shader_str);
|
||||
GLuint gl_create_program(const GLuint *const shaders, int nshaders);
|
||||
GLuint
|
||||
gl_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str);
|
||||
|
||||
bool gl_load_prog_main(session_t *ps, const char *vshader_str, const char *fshader_str,
|
||||
gl_win_shader_t *pprogram);
|
||||
|
||||
unsigned char *gl_take_screenshot(session_t *ps, int *out_length);
|
||||
void gl_resize(int width, int height);
|
||||
|
||||
/**
|
||||
* Get a textual representation of an OpenGL error.
|
||||
*/
|
||||
static inline const char *gl_get_err_str(GLenum err) {
|
||||
switch (err) {
|
||||
CASESTRRET(GL_NO_ERROR);
|
||||
CASESTRRET(GL_INVALID_ENUM);
|
||||
CASESTRRET(GL_INVALID_VALUE);
|
||||
CASESTRRET(GL_INVALID_OPERATION);
|
||||
CASESTRRET(GL_INVALID_FRAMEBUFFER_OPERATION);
|
||||
CASESTRRET(GL_OUT_OF_MEMORY);
|
||||
CASESTRRET(GL_STACK_UNDERFLOW);
|
||||
CASESTRRET(GL_STACK_OVERFLOW);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for GLX error.
|
||||
*
|
||||
* http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/
|
||||
*/
|
||||
static inline void gl_check_err_(const char *func, int line) {
|
||||
GLenum err = GL_NO_ERROR;
|
||||
|
||||
while (GL_NO_ERROR != (err = glGetError())) {
|
||||
const char *errtext = gl_get_err_str(err);
|
||||
if (errtext) {
|
||||
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
||||
"GLX error at line %d: %s", line, errtext);
|
||||
} else {
|
||||
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
||||
"GLX error at line %d: %d", line, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define gl_check_err() gl_check_err_(__func__, __LINE__)
|
||||
|
||||
/**
|
||||
* Check if a GLX extension exists.
|
||||
*/
|
||||
static inline bool gl_has_extension(session_t *ps, const char *ext) {
|
||||
GLint nexts = 0;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &nexts);
|
||||
if (!nexts) {
|
||||
log_error("Failed get GL extension list.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nexts; i++) {
|
||||
const char *exti = (const char *)glGetStringi(GL_EXTENSIONS, i);
|
||||
if (strcmp(ext, exti) == 0)
|
||||
return true;
|
||||
}
|
||||
log_info("Missing GL extension %s.", ext);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
|
||||
if (shader->prog)
|
||||
glDeleteShader(shader->prog);
|
||||
if (shader->frag_shader)
|
||||
glDeleteShader(shader->frag_shader);
|
||||
|
||||
shader->prog = 0;
|
||||
shader->frag_shader = 0;
|
||||
}
|
||||
|
||||
#define P_PAINTREG_START(var) \
|
||||
do { \
|
||||
region_t reg_new; \
|
||||
int nrects; \
|
||||
const rect_t *rects; \
|
||||
pixman_region32_init_rect(®_new, dx, dy, width, height); \
|
||||
pixman_region32_intersect(®_new, ®_new, (region_t *)reg_tgt); \
|
||||
rects = pixman_region32_rectangles(®_new, &nrects); \
|
||||
glBegin(GL_QUADS); \
|
||||
\
|
||||
for (int ri = 0; ri < nrects; ++ri) { \
|
||||
rect_t var = rects[ri];
|
||||
|
||||
#define P_PAINTREG_END() \
|
||||
} \
|
||||
glEnd(); \
|
||||
pixman_region32_fini(®_new); \
|
||||
} \
|
||||
while (0)
|
|
@ -0,0 +1,882 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Compton - a compositor for X11
|
||||
*
|
||||
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
||||
*
|
||||
* Copyright (c) 2011-2013, Christopher Jeffrey
|
||||
* See LICENSE-mit for more information.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <GL/glx.h>
|
||||
#include "backend/gl/glx.h"
|
||||
#include "backend/backend.h"
|
||||
#include "backend/gl/gl_common.h"
|
||||
|
||||
/// @brief Wrapper of a GLX FBConfig.
|
||||
typedef struct {
|
||||
GLXFBConfig cfg;
|
||||
GLint texture_fmt;
|
||||
GLint texture_tgts;
|
||||
bool y_inverted;
|
||||
} glx_fbconfig_t;
|
||||
|
||||
struct _glx_win_data {
|
||||
gl_texture_t texture;
|
||||
GLXPixmap glpixmap;
|
||||
xcb_pixmap_t pixmap;
|
||||
};
|
||||
|
||||
struct _glx_data {
|
||||
int glx_event;
|
||||
int glx_error;
|
||||
GLXContext ctx;
|
||||
gl_cap_t cap;
|
||||
gl_blur_shader_t blur_shader[MAX_BLUR_PASS];
|
||||
|
||||
void (*glXBindTexImage)(Display *display, GLXDrawable drawable, int buffer,
|
||||
const int *attrib_list);
|
||||
void (*glXReleaseTexImage)(Display *display, GLXDrawable drawable, int buffer);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a GLX extension exists.
|
||||
*/
|
||||
static inline bool glx_has_extension(session_t *ps, const char *ext) {
|
||||
const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
|
||||
if (!glx_exts) {
|
||||
log_error("Failed get GLX extension list.");
|
||||
return false;
|
||||
}
|
||||
|
||||
int len = strlen(ext);
|
||||
char *found = strstr(glx_exts, ext);
|
||||
if (!found)
|
||||
log_info("Missing GLX extension %s.", ext);
|
||||
|
||||
// Make sure extension names are not crazy...
|
||||
assert(found[len] == ' ' || found[len] == 0);
|
||||
return found != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Release binding of a texture.
|
||||
*/
|
||||
void glx_release_pixmap(struct _glx_data *gd, Display *dpy, struct _glx_win_data *wd) {
|
||||
// Release binding
|
||||
if (wd->glpixmap && wd->texture.texture) {
|
||||
glBindTexture(wd->texture.target, wd->texture.texture);
|
||||
gd->glXReleaseTexImage(dpy, wd->glpixmap, GLX_FRONT_LEFT_EXT);
|
||||
glBindTexture(wd->texture.target, 0);
|
||||
}
|
||||
|
||||
// Free GLX Pixmap
|
||||
if (wd->glpixmap) {
|
||||
glXDestroyPixmap(dpy, wd->glpixmap);
|
||||
wd->glpixmap = 0;
|
||||
}
|
||||
|
||||
gl_check_err();
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a glx_texture_t.
|
||||
*/
|
||||
static void glx_release_win(struct _glx_data *gd, Display *dpy, gl_texture_t *ptex) {
|
||||
glx_release_pixmap(gd, dpy, ptex);
|
||||
glDeleteTextures(1, &ptex->texture);
|
||||
|
||||
// Free structure itself
|
||||
free(ptex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free GLX part of win.
|
||||
*/
|
||||
static inline void free_win_res_glx(session_t *ps, win *w) {
|
||||
/*free_paint_glx(ps, &w->paint);*/
|
||||
/*free_paint_glx(ps, &w->shadow_paint);*/
|
||||
/*free_glx_bc(ps, &w->glx_blur_cache);*/
|
||||
}
|
||||
>>>>>>> 4bc5ef8... wip glx backend:src/backend/gl/glx.c
|
||||
|
||||
static inline int glx_cmp_fbconfig_cmpattr(session_t *ps, const glx_fbconfig_t *pfbc_a,
|
||||
const glx_fbconfig_t *pfbc_b, int attr) {
|
||||
int attr_a = 0, attr_b = 0;
|
||||
|
||||
// TODO: Error checking
|
||||
glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a);
|
||||
glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b);
|
||||
|
||||
return attr_a - attr_b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two GLX FBConfig's to find the preferred one.
|
||||
*/
|
||||
static int glx_cmp_fbconfig(session_t *ps, const glx_fbconfig_t *pfbc_a,
|
||||
const glx_fbconfig_t *pfbc_b) {
|
||||
int result = 0;
|
||||
|
||||
if (!pfbc_a)
|
||||
return -1;
|
||||
if (!pfbc_b)
|
||||
return 1;
|
||||
int tmpattr;
|
||||
|
||||
// Avoid 10-bit colors
|
||||
glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, GLX_RED_SIZE, &tmpattr);
|
||||
if (tmpattr != 8)
|
||||
return -1;
|
||||
|
||||
glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, GLX_RED_SIZE, &tmpattr);
|
||||
if (tmpattr != 8)
|
||||
return 1;
|
||||
|
||||
#define P_CMPATTR_LT(attr) \
|
||||
{ \
|
||||
if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) \
|
||||
return -result; \
|
||||
}
|
||||
#define P_CMPATTR_GT(attr) \
|
||||
{ \
|
||||
if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) \
|
||||
return result; \
|
||||
}
|
||||
|
||||
P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT);
|
||||
P_CMPATTR_LT(GLX_DOUBLEBUFFER);
|
||||
P_CMPATTR_LT(GLX_STENCIL_SIZE);
|
||||
P_CMPATTR_LT(GLX_DEPTH_SIZE);
|
||||
P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the FBConfig of given depth.
|
||||
*/
|
||||
static inline void
|
||||
glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) {
|
||||
// Make sure the depth is sane
|
||||
if (depth < 0 || depth > OPENGL_MAX_DEPTH)
|
||||
return;
|
||||
|
||||
// Compare new FBConfig with current one
|
||||
if (glx_cmp_fbconfig(ps, ps->psglx->fbconfigs[depth], pfbcfg) < 0) {
|
||||
log_debug(
|
||||
"(depth %d): %p overrides %p, target %#x.\n", depth, pfbcfg->cfg,
|
||||
ps->psglx->fbconfigs[depth] ? ps->psglx->fbconfigs[depth]->cfg : 0,
|
||||
pfbcfg->texture_tgts);
|
||||
if (!ps->psglx->fbconfigs[depth]) {
|
||||
ps->psglx->fbconfigs[depth] = cmalloc(glx_fbconfig_t);
|
||||
}
|
||||
(*ps->psglx->fbconfigs[depth]) = *pfbcfg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GLX FBConfigs for all depths.
|
||||
*/
|
||||
static bool glx_update_fbconfig(session_t *ps) {
|
||||
// Acquire all FBConfigs and loop through them
|
||||
int nele = 0;
|
||||
GLXFBConfig *pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele);
|
||||
|
||||
for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) {
|
||||
glx_fbconfig_t fbinfo = {
|
||||
.cfg = *pcur,
|
||||
.texture_fmt = 0,
|
||||
.texture_tgts = 0,
|
||||
.y_inverted = false,
|
||||
};
|
||||
int id = (int)(pcur - pfbcfgs);
|
||||
int depth = 0, depth_alpha = 0, val = 0;
|
||||
|
||||
// Skip over multi-sampled visuals
|
||||
// http://people.freedesktop.org/~glisse/0001-glx-do-not-use-multisample-visual-config-for-front-o.patch
|
||||
#ifdef GLX_SAMPLES
|
||||
if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_SAMPLES, &val) &&
|
||||
val > 1)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if (Success !=
|
||||
glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth) ||
|
||||
Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE,
|
||||
&depth_alpha)) {
|
||||
log_error("Failed to retrieve buffer size and alpha size of "
|
||||
"FBConfig %d.",
|
||||
id);
|
||||
continue;
|
||||
}
|
||||
if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur,
|
||||
GLX_BIND_TO_TEXTURE_TARGETS_EXT,
|
||||
&fbinfo.texture_tgts)) {
|
||||
log_error("Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of "
|
||||
"FBConfig %d.",
|
||||
id);
|
||||
continue;
|
||||
}
|
||||
|
||||
int visualdepth = 0;
|
||||
{
|
||||
XVisualInfo *pvi = glXGetVisualFromFBConfig(ps->dpy, *pcur);
|
||||
if (!pvi) {
|
||||
// On nvidia-drivers-325.08 this happens slightly too often...
|
||||
// log_error("Failed to retrieve X Visual of FBConfig %d.", id);
|
||||
continue;
|
||||
}
|
||||
visualdepth = pvi->depth;
|
||||
cxfree(pvi);
|
||||
}
|
||||
|
||||
bool rgb = false;
|
||||
bool rgba = false;
|
||||
|
||||
if (depth >= 32 && depth_alpha &&
|
||||
Success == glXGetFBConfigAttrib(ps->dpy, *pcur,
|
||||
GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) &&
|
||||
val)
|
||||
rgba = true;
|
||||
|
||||
if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur,
|
||||
GLX_BIND_TO_TEXTURE_RGB_EXT, &val) &&
|
||||
val)
|
||||
rgb = true;
|
||||
|
||||
if (Success ==
|
||||
glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val))
|
||||
fbinfo.y_inverted = val;
|
||||
|
||||
{
|
||||
int tgtdpt = depth - depth_alpha;
|
||||
if (tgtdpt == visualdepth && tgtdpt < 32 && rgb) {
|
||||
fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT;
|
||||
glx_update_fbconfig_bydepth(ps, tgtdpt, &fbinfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (depth == visualdepth && rgba) {
|
||||
fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT;
|
||||
glx_update_fbconfig_bydepth(ps, depth, &fbinfo);
|
||||
}
|
||||
}
|
||||
|
||||
cxfree(pfbcfgs);
|
||||
|
||||
// Sanity checks
|
||||
if (!ps->psglx->fbconfigs[ps->depth]) {
|
||||
log_error("No FBConfig found for default depth %d.", ps->depth);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ps->psglx->fbconfigs[32]) {
|
||||
log_error("No FBConfig found for depth 32. Expect crazy things.");
|
||||
}
|
||||
|
||||
log_trace("%d-bit: %p, 32-bit: %p", ps->depth,
|
||||
ps->psglx->fbconfigs[ps->depth]->cfg, ps->psglx->fbconfigs[32]->cfg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_GLX_DEBUG_CONTEXT
|
||||
static inline GLXFBConfig
|
||||
get_fbconfig_from_visualinfo(session_t *ps, const XVisualInfo *visualinfo) {
|
||||
int nelements = 0;
|
||||
GLXFBConfig *fbconfigs = glXGetFBConfigs(ps->dpy, visualinfo->screen, &nelements);
|
||||
for (int i = 0; i < nelements; ++i) {
|
||||
int visual_id = 0;
|
||||
if (Success == glXGetFBConfigAttrib(ps->dpy, fbconfigs[i], GLX_VISUAL_ID,
|
||||
&visual_id) &&
|
||||
visual_id == visualinfo->visualid)
|
||||
return fbconfigs[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
glx_debug_msg_callback(GLenum source, GLenum type, GLuint id, GLenum severity,
|
||||
GLsizei length, const GLchar *message, GLvoid *userParam) {
|
||||
log_trace("(): source 0x%04X, type 0x%04X, id %u, severity 0x%0X, \"%s\"", source,
|
||||
type, id, severity, message);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Destroy GLX related resources.
|
||||
*/
|
||||
void glx_deinit(void *backend_data, session_t *ps) {
|
||||
struct _glx_data *gd = backend_data;
|
||||
|
||||
// Free all GLX resources of windows
|
||||
for (win *w = ps->list; w; w = w->next)
|
||||
free_win_res_glx(ps, w);
|
||||
|
||||
// Free GLSL shaders/programs
|
||||
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
|
||||
gl_free_blur_shader(&gd->blur_shader[i]);
|
||||
}
|
||||
|
||||
glx_free_prog_main(ps, &ps->o.glx_prog_win);
|
||||
|
||||
gl_check_err();
|
||||
|
||||
// Free FBConfigs
|
||||
for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) {
|
||||
free(ps->psglx->fbconfigs[i]);
|
||||
ps->psglx->fbconfigs[i] = NULL;
|
||||
}
|
||||
|
||||
// Destroy GLX context
|
||||
if (gd->ctx) {
|
||||
glXDestroyContext(ps->dpy, gd->ctx);
|
||||
gd->ctx = 0;
|
||||
}
|
||||
|
||||
free(gd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize OpenGL.
|
||||
*/
|
||||
void *glx_init(session_t *ps) {
|
||||
bool success = false;
|
||||
auto gd = ccalloc(1, struct _glx_data);
|
||||
XVisualInfo *pvis = NULL;
|
||||
|
||||
// Check for GLX extension
|
||||
if (!glXQueryExtension(ps->dpy, &gd->glx_event, &gd->glx_error)) {
|
||||
log_error("No GLX extension.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Get XVisualInfo
|
||||
int nitems = 0;
|
||||
XVisualInfo vreq = {.visualid = ps->vis};
|
||||
pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
|
||||
if (!pvis) {
|
||||
log_error("Failed to acquire XVisualInfo for current visual.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Ensure the visual is double-buffered
|
||||
int value = 0;
|
||||
if (glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
|
||||
log_error("Root visual is not a GL visual.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
|
||||
log_error("Root visual is not a double buffered GL visual.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Ensure GLX_EXT_texture_from_pixmap exists
|
||||
if (!glx_has_extension(ps, "GLX_EXT_texture_from_pixmap"))
|
||||
goto end;
|
||||
|
||||
// Initialize GLX data structure
|
||||
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
|
||||
gd->blur_shader[i] = (gl_blur_shader_t){.frag_shader = -1,
|
||||
.prog = -1,
|
||||
.unifm_offset_x = -1,
|
||||
.unifm_offset_y = -1,
|
||||
.unifm_factor_center = -1};
|
||||
}
|
||||
|
||||
// Get GLX context
|
||||
gd->ctx = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
|
||||
|
||||
if (!gd->ctx) {
|
||||
log_error("Failed to get GLX context.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Attach GLX context
|
||||
GLXDrawable tgt = ps->overlay;
|
||||
if (!tgt) {
|
||||
tgt = ps->root;
|
||||
}
|
||||
if (!glXMakeCurrent(ps->dpy, tgt, gd->ctx)) {
|
||||
log_error("Failed to attach GLX context.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_GLX_DEBUG_CONTEXT
|
||||
f_DebugMessageCallback p_DebugMessageCallback =
|
||||
(f_DebugMessageCallback)glXGetProcAddress((const GLubyte *)"glDebugMessageCal"
|
||||
"lback");
|
||||
if (!p_DebugMessageCallback) {
|
||||
log_error("Failed to get glDebugMessageCallback(0.");
|
||||
goto glx_init_end;
|
||||
}
|
||||
p_DebugMessageCallback(glx_debug_msg_callback, ps);
|
||||
#endif
|
||||
|
||||
// Ensure we have a stencil buffer. X Fixes does not guarantee rectangles
|
||||
// in regions don't overlap, so we must use stencil buffer to make sure
|
||||
// we don't paint a region for more than one time, I think?
|
||||
if (!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 end;
|
||||
}
|
||||
}
|
||||
|
||||
// Check GL_ARB_texture_non_power_of_two, requires a GLX context and
|
||||
// must precede FBConfig fetching
|
||||
gd->cap.non_power_of_two_texture = gl_has_extension(ps, "GL_ARB_texture_non_"
|
||||
"power_of_two");
|
||||
|
||||
// Acquire function addresses
|
||||
#if 0
|
||||
psglx->glStringMarkerGREMEDY = (f_StringMarkerGREMEDY)
|
||||
glXGetProcAddress((const GLubyte *) "glStringMarkerGREMEDY");
|
||||
psglx->glFrameTerminatorGREMEDY = (f_FrameTerminatorGREMEDY)
|
||||
glXGetProcAddress((const GLubyte *) "glFrameTerminatorGREMEDY");
|
||||
#endif
|
||||
|
||||
gd->glXBindTexImage = (void *)glXGetProcAddress((const GLubyte *)"glXBindTexImage"
|
||||
"EXT");
|
||||
gd->glXReleaseTexImage = (void *)glXGetProcAddress((const GLubyte *)"glXReleaseTe"
|
||||
"xImageEXT");
|
||||
if (!gd->glXBindTexImage || !gd->glXReleaseTexImage) {
|
||||
log_error("Failed to acquire glXBindTexImageEXT() and/or "
|
||||
"glXReleaseTexImageEXT(), make sure your OpenGL supports"
|
||||
"GLX_EXT_texture_from_pixmap");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// Acquire FBConfigs
|
||||
if (!glx_update_fbconfig(ps))
|
||||
goto end;
|
||||
|
||||
// Render preparations
|
||||
gl_resize(ps->root_width, ps->root_height);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (!ps->o.glx_no_stencil) {
|
||||
// Initialize stencil buffer
|
||||
glClear(GL_STENCIL_BUFFER_BIT);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glStencilMask(0x1);
|
||||
glStencilFunc(GL_EQUAL, 0x1, 0x1);
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
// glXSwapBuffers(ps->dpy, get_tgt_window(ps));
|
||||
|
||||
success = true;
|
||||
|
||||
end:
|
||||
cxfree(pvis);
|
||||
|
||||
if (!success) {
|
||||
glx_deinit(gd, ps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return gd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize GLX blur filter.
|
||||
*/
|
||||
bool glx_init_blur(session_t *ps) {
|
||||
assert(ps->o.blur_kerns[0]);
|
||||
|
||||
// Allocate PBO if more than one blur kernel is present
|
||||
if (ps->o.blur_kerns[1]) {
|
||||
// Try to generate a framebuffer
|
||||
GLuint fbo = 0;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
if (!fbo) {
|
||||
log_error("Failed to generate Framebuffer. Cannot do "
|
||||
"multi-pass blur with GLX backend.");
|
||||
return false;
|
||||
}
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
}
|
||||
|
||||
{
|
||||
char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
|
||||
// Enforce LC_NUMERIC locale "C" here to make sure decimal point is sane
|
||||
// Thanks to hiciu for reporting.
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
|
||||
static const char *FRAG_SHADER_BLUR_PREFIX = "#version 110\n"
|
||||
"%s"
|
||||
"uniform float offset_x;\n"
|
||||
"uniform float offset_y;\n"
|
||||
"uniform float "
|
||||
"factor_center;\n"
|
||||
"uniform %s tex_scr;\n"
|
||||
"\n"
|
||||
"void main() {\n"
|
||||
" vec4 sum = vec4(0.0, "
|
||||
"0.0, 0.0, 0.0);\n";
|
||||
static const char *FRAG_SHADER_BLUR_ADD = " sum += float(%.7g) * "
|
||||
"%s(tex_scr, "
|
||||
"vec2(gl_TexCoord[0].x + "
|
||||
"offset_x * float(%d), "
|
||||
"gl_TexCoord[0].y + offset_y * "
|
||||
"float(%d)));\n";
|
||||
static const char *FRAG_SHADER_BLUR_ADD_GPUSHADER4 = " sum += "
|
||||
"float(%.7g) * "
|
||||
"%sOffset(tex_scr, "
|
||||
"vec2(gl_TexCoord[0]"
|
||||
".x, "
|
||||
"gl_TexCoord[0].y), "
|
||||
"ivec2(%d, %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 = strdup("");
|
||||
if (use_texture_rect)
|
||||
mstrextend(&extension, "#extension GL_ARB_texture_rectangle : "
|
||||
"require\n");
|
||||
if (ps->o.glx_use_gpushader4) {
|
||||
mstrextend(&extension, "#extension GL_EXT_gpu_shader4 : "
|
||||
"require\n");
|
||||
shader_add = FRAG_SHADER_BLUR_ADD_GPUSHADER4;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_BLUR_PASS && ps->o.blur_kerns[i]; ++i) {
|
||||
xcb_render_fixed_t *kern = ps->o.blur_kerns[i];
|
||||
if (!kern)
|
||||
break;
|
||||
|
||||
glx_blur_pass_t *ppass = &ps->psglx->blur_passes[i];
|
||||
|
||||
// Build shader
|
||||
{
|
||||
int wid = XFIXED_TO_DOUBLE(kern[0]),
|
||||
hei = XFIXED_TO_DOUBLE(kern[1]);
|
||||
int nele = wid * hei - 1;
|
||||
unsigned int len =
|
||||
strlen(FRAG_SHADER_BLUR_PREFIX) +
|
||||
strlen(sampler_type) + strlen(extension) +
|
||||
(strlen(shader_add) + strlen(texture_func) + 42) *
|
||||
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 < hei; ++j) {
|
||||
for (int k = 0; k < wid; ++k) {
|
||||
if (hei / 2 == j && wid / 2 == k)
|
||||
continue;
|
||||
double val = XFIXED_TO_DOUBLE(
|
||||
kern[2 + j * wid + k]);
|
||||
if (0.0 == val)
|
||||
continue;
|
||||
sum += val;
|
||||
sprintf(pc, shader_add, val, texture_func,
|
||||
k - wid / 2, j - hei / 2);
|
||||
pc += strlen(pc);
|
||||
assert(strlen(shader_str) < len);
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum);
|
||||
assert(strlen(shader_str) < len);
|
||||
ppass->frag_shader =
|
||||
glx_create_shader(GL_FRAGMENT_SHADER, shader_str);
|
||||
free(shader_str);
|
||||
}
|
||||
|
||||
if (!ppass->frag_shader) {
|
||||
log_error("Failed to create fragment shader %d.", i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build program
|
||||
ppass->prog = glx_create_program(&ppass->frag_shader, 1);
|
||||
if (!ppass->prog) {
|
||||
log_error("Failed to create GLSL program.");
|
||||
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 "'. " \
|
||||
"Mig" \
|
||||
"ht " \
|
||||
"be " \
|
||||
"tro" \
|
||||
"ubl" \
|
||||
"eso" \
|
||||
"me" \
|
||||
".", \
|
||||
i); \
|
||||
} \
|
||||
}
|
||||
|
||||
P_GET_UNIFM_LOC("factor_center", unifm_factor_center);
|
||||
if (!ps->o.glx_use_gpushader4) {
|
||||
P_GET_UNIFM_LOC("offset_x", unifm_offset_x);
|
||||
P_GET_UNIFM_LOC("offset_y", unifm_offset_y);
|
||||
}
|
||||
|
||||
#undef P_GET_UNIFM_LOC
|
||||
}
|
||||
free(extension);
|
||||
|
||||
// Restore LC_NUMERIC
|
||||
setlocale(LC_NUMERIC, lc_numeric_old);
|
||||
free(lc_numeric_old);
|
||||
}
|
||||
|
||||
glx_check_err(ps);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a X pixmap to an OpenGL texture.
|
||||
*/
|
||||
bool glx_render_win(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
|
||||
unsigned width, unsigned height, unsigned depth) {
|
||||
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID)
|
||||
return true;
|
||||
|
||||
if (!pixmap) {
|
||||
log_error("Binding to an empty pixmap %#010x. This can't work.", pixmap);
|
||||
return false;
|
||||
}
|
||||
|
||||
glx_texture_t *ptex = *pptex;
|
||||
bool need_release = true;
|
||||
|
||||
// Allocate structure
|
||||
if (!ptex) {
|
||||
static const glx_texture_t GLX_TEX_DEF = {
|
||||
.texture = 0,
|
||||
.glpixmap = 0,
|
||||
.pixmap = 0,
|
||||
.target = 0,
|
||||
.width = 0,
|
||||
.height = 0,
|
||||
.depth = 0,
|
||||
.y_inverted = false,
|
||||
};
|
||||
|
||||
ptex = cmalloc(glx_texture_t);
|
||||
memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
|
||||
*pptex = ptex;
|
||||
}
|
||||
|
||||
// Release pixmap if parameters are inconsistent
|
||||
if (ptex->texture && ptex->pixmap != pixmap) {
|
||||
glx_release_pixmap(ps, ptex);
|
||||
}
|
||||
|
||||
// Create GLX pixmap
|
||||
if (!ptex->glpixmap) {
|
||||
need_release = false;
|
||||
|
||||
// Retrieve pixmap parameters, if they aren't provided
|
||||
if (!(width && height && depth)) {
|
||||
Window rroot = None;
|
||||
int rx = 0, ry = 0;
|
||||
unsigned rbdwid = 0;
|
||||
if (!XGetGeometry(ps->dpy, pixmap, &rroot, &rx, &ry, &width,
|
||||
&height, &rbdwid, &depth)) {
|
||||
log_error("Failed to query info of pixmap %#010x.", pixmap);
|
||||
return false;
|
||||
}
|
||||
if (depth > OPENGL_MAX_DEPTH) {
|
||||
log_error("Requested depth %d higher than max possible "
|
||||
"depth %d.",
|
||||
depth, OPENGL_MAX_DEPTH);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const glx_fbconfig_t *pcfg = ps->psglx->fbconfigs[depth];
|
||||
if (!pcfg) {
|
||||
log_error("Couldn't find FBConfig with requested depth %d", depth);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Choose a suitable texture target for our pixmap.
|
||||
// Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
|
||||
// of the bits in texture_tgts
|
||||
GLenum tex_tgt = 0;
|
||||
if (GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts &&
|
||||
ps->psglx->has_texture_non_power_of_two)
|
||||
tex_tgt = GLX_TEXTURE_2D_EXT;
|
||||
else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & pcfg->texture_tgts)
|
||||
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
||||
else if (!(GLX_TEXTURE_2D_BIT_EXT & pcfg->texture_tgts))
|
||||
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
||||
else
|
||||
tex_tgt = GLX_TEXTURE_2D_EXT;
|
||||
|
||||
log_debug("depth %d, tgt %#x, rgba %d\n", depth, tex_tgt,
|
||||
(GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt));
|
||||
|
||||
GLint attrs[] = {
|
||||
GLX_TEXTURE_FORMAT_EXT,
|
||||
pcfg->texture_fmt,
|
||||
GLX_TEXTURE_TARGET_EXT,
|
||||
tex_tgt,
|
||||
0,
|
||||
};
|
||||
|
||||
ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs);
|
||||
ptex->pixmap = pixmap;
|
||||
ptex->target =
|
||||
(GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE);
|
||||
ptex->width = width;
|
||||
ptex->height = height;
|
||||
ptex->depth = depth;
|
||||
ptex->y_inverted = pcfg->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);
|
||||
glTexParameteri(ptex->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(ptex->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glBindTexture(ptex->target, 0);
|
||||
|
||||
ptex->texture = texture;
|
||||
}
|
||||
if (!ptex->texture) {
|
||||
log_error("Failed to allocate texture.");
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(ptex->target, ptex->texture);
|
||||
|
||||
// The specification requires rebinding whenever the content changes...
|
||||
// We can't follow this, too slow.
|
||||
if (need_release)
|
||||
ps->psglx->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap,
|
||||
GLX_FRONT_LEFT_EXT);
|
||||
|
||||
ps->psglx->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
|
||||
// Cleanup
|
||||
glBindTexture(ptex->target, 0);
|
||||
glDisable(ptex->target);
|
||||
|
||||
glx_check_err(ps);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Preprocess function before start painting.
|
||||
*/
|
||||
void
|
||||
glx_paint_pre(session_t *ps, region_t *preg) {
|
||||
ps->psglx->z = 0.0;
|
||||
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Get buffer age
|
||||
bool trace_damage = (ps->o.glx_swap_method < 0 || ps->o.glx_swap_method > 1);
|
||||
|
||||
// Trace raw damage regions
|
||||
region_t newdamage;
|
||||
pixman_region32_init(&newdamage);
|
||||
if (trace_damage)
|
||||
copy_region(&newdamage, preg);
|
||||
|
||||
// We use GLX buffer_age extension to decide which pixels in
|
||||
// the back buffer is reusable, and limit our redrawing
|
||||
int buffer_age = 0;
|
||||
|
||||
// Query GLX_EXT_buffer_age for buffer age
|
||||
if (ps->o.glx_swap_method == SWAPM_BUFFER_AGE) {
|
||||
unsigned val = 0;
|
||||
glXQueryDrawable(ps->dpy, get_tgt_window(ps),
|
||||
GLX_BACK_BUFFER_AGE_EXT, &val);
|
||||
buffer_age = val;
|
||||
}
|
||||
|
||||
// Buffer age too high
|
||||
if (buffer_age > CGLX_MAX_BUFFER_AGE + 1)
|
||||
buffer_age = 0;
|
||||
|
||||
assert(buffer_age >= 0);
|
||||
|
||||
if (buffer_age) {
|
||||
// Determine paint area
|
||||
for (int i = 0; i < buffer_age - 1; ++i)
|
||||
pixman_region32_union(preg, preg, &ps->all_damage_last[i]);
|
||||
} else
|
||||
// buffer_age == 0 means buffer age is not available, paint everything
|
||||
copy_region(preg, &ps->screen_reg);
|
||||
|
||||
if (trace_damage) {
|
||||
// XXX use a circular queue instead of memmove
|
||||
pixman_region32_fini(&ps->all_damage_last[CGLX_MAX_BUFFER_AGE - 1]);
|
||||
memmove(ps->all_damage_last + 1, ps->all_damage_last,
|
||||
(CGLX_MAX_BUFFER_AGE - 1) * sizeof(region_t *));
|
||||
ps->all_damage_last[0] = newdamage;
|
||||
}
|
||||
|
||||
glx_set_clip(ps, preg);
|
||||
|
||||
#ifdef DEBUG_GLX_PAINTREG
|
||||
glx_render_color(ps, 0, 0, ps->root_width, ps->root_height, 0, *preg, NULL);
|
||||
#endif
|
||||
|
||||
glx_check_err(ps);
|
||||
}
|
||||
#endif
|
||||
|
||||
backend_info_t glx_backend = {
|
||||
.init = glx_init,
|
||||
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Compton - a compositor for X11
|
||||
*
|
||||
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
||||
*
|
||||
* Copyright (c) 2011-2013, Christopher Jeffrey
|
||||
* See LICENSE-mit for more information.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ctype.h>
|
||||
#include <locale.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glx.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void
|
||||
glx_destroy(session_t *ps);
|
||||
|
||||
bool
|
||||
glx_reinit(session_t *ps, bool need_render);
|
||||
|
||||
void
|
||||
glx_on_root_change(session_t *ps);
|
||||
|
||||
bool
|
||||
glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
|
||||
unsigned width, unsigned height, unsigned depth);
|
||||
|
||||
void
|
||||
glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
|
||||
|
||||
void glx_paint_pre(session_t *ps, region_t *preg)
|
||||
__attribute__((nonnull(1, 2)));
|
||||
|
||||
/**
|
||||
* Check if a texture is binded, or is binded to the given pixmap.
|
||||
*/
|
||||
static inline bool
|
||||
glx_tex_binded(const glx_texture_t *ptex, xcb_pixmap_t pixmap) {
|
||||
return ptex && ptex->glpixmap && ptex->texture
|
||||
&& (!pixmap || pixmap == ptex->pixmap);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# enable xrender
|
||||
|
||||
if get_option('new_backends')
|
||||
srcs += [ files('xrender.c', 'backend.c', 'backend_common.c') ]
|
||||
|
||||
# enable opengl
|
||||
if get_option('opengl')
|
||||
srcs += [ files('gl/gl_common.c') ]
|
||||
deps += [ dependency('gl', required: true) ]
|
||||
cflags += [ '-DGL_GLEXT_PROTOTYPES' ]
|
||||
endif
|
||||
endif
|
|
@ -0,0 +1,464 @@
|
|||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "backend/backend.h"
|
||||
#include "backend_common.h"
|
||||
#include "utils.h"
|
||||
#include "win.h"
|
||||
|
||||
#define auto __auto_type
|
||||
|
||||
typedef struct _xrender_data {
|
||||
/// The painting target drawable
|
||||
xcb_drawable_t target_draw;
|
||||
/// The painting target, it is either the root or the overlay
|
||||
xcb_render_picture_t target;
|
||||
/// A buffer of the image to paint
|
||||
xcb_render_picture_t target_buffer;
|
||||
/// The original root window content, usually the wallpaper.
|
||||
/// We save it so we don't loss the wallpaper when we paint over
|
||||
/// it.
|
||||
xcb_render_picture_t root_pict;
|
||||
/// Pictures of pixel of different alpha value, used as a mask to
|
||||
/// paint transparent images
|
||||
xcb_render_picture_t alpha_pict[256];
|
||||
|
||||
// XXX don't know if these are really needed
|
||||
|
||||
/// 1x1 white picture
|
||||
xcb_render_picture_t white_pixel;
|
||||
/// 1x1 black picture
|
||||
xcb_render_picture_t black_pixel;
|
||||
|
||||
/// 1x1 picture of the shadow color
|
||||
xcb_render_picture_t shadow_pixel;
|
||||
} xrender_data;
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Paint root window content.
|
||||
*/
|
||||
static void
|
||||
paint_root(session_t *ps, const region_t *reg_paint) {
|
||||
if (!ps->root_tile_paint.pixmap)
|
||||
get_root_tile(ps);
|
||||
|
||||
paint_region(ps, NULL, 0, 0, ps->root_width, ps->root_height, 1.0, reg_paint,
|
||||
ps->root_tile_paint.pict);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct _xrender_win_data {
|
||||
// Pixmap that the client window draws to,
|
||||
// it will contain the content of client window.
|
||||
xcb_pixmap_t pixmap;
|
||||
// A Picture links to the Pixmap
|
||||
xcb_render_picture_t pict;
|
||||
// A buffer used for rendering
|
||||
xcb_render_picture_t buffer;
|
||||
// The rendered content of the window (dimmed, inverted
|
||||
// color, etc.). This is either `buffer` or `pict`
|
||||
xcb_render_picture_t rendered_pict;
|
||||
xcb_pixmap_t shadow_pixmap;
|
||||
xcb_render_picture_t shadow_pict;
|
||||
};
|
||||
|
||||
static void compose(void *backend_data, session_t *ps, win *w, void *win_data, int dst_x,
|
||||
int dst_y, const region_t *reg_paint) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
struct _xrender_win_data *wd = win_data;
|
||||
bool blend = default_is_frame_transparent(NULL, w, win_data) ||
|
||||
default_is_win_transparent(NULL, w, win_data);
|
||||
int op = (blend ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
|
||||
auto alpha_pict = xd->alpha_pict[(int)(((double)w->opacity / OPAQUE) * 255.0)];
|
||||
|
||||
// XXX Move shadow drawing into a separate function,
|
||||
// also do shadow excluding outside of backend
|
||||
// XXX This is needed to implement full-shadow
|
||||
if (w->shadow) {
|
||||
// Put shadow on background
|
||||
region_t shadow_reg = win_extents_by_val(w);
|
||||
region_t bshape = win_get_bounding_shape_global_by_val(w);
|
||||
region_t reg_tmp;
|
||||
pixman_region32_init(®_tmp);
|
||||
// Shadow doesn't need to be painted underneath the body of the window
|
||||
// Because no one can see it
|
||||
pixman_region32_subtract(®_tmp, &shadow_reg, w->reg_ignore);
|
||||
|
||||
// Mask out the region we don't want shadow on
|
||||
if (pixman_region32_not_empty(&ps->shadow_exclude_reg))
|
||||
pixman_region32_subtract(®_tmp, ®_tmp,
|
||||
&ps->shadow_exclude_reg);
|
||||
|
||||
// Might be worth while to crop the region to shadow border
|
||||
pixman_region32_intersect_rect(®_tmp, ®_tmp, w->g.x + w->shadow_dx,
|
||||
w->g.y + w->shadow_dy, w->shadow_width,
|
||||
w->shadow_height);
|
||||
|
||||
// Crop the shadow to the damage region. If we draw out side of
|
||||
// the damage region, we could be drawing over perfectly good
|
||||
// content, and destroying it.
|
||||
pixman_region32_intersect(®_tmp, ®_tmp, (region_t *)reg_paint);
|
||||
|
||||
#ifdef CONFIG_XINERAMA
|
||||
if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
|
||||
w->xinerama_scr < ps->xinerama_nscrs)
|
||||
// There can be a window where number of screens is updated,
|
||||
// but the screen number attached to the windows have not.
|
||||
//
|
||||
// Window screen number will be updated eventually, so here we
|
||||
// just check to make sure we don't access out of bounds.
|
||||
pixman_region32_intersect(
|
||||
®_tmp, ®_tmp, &ps->xinerama_scr_regs[w->xinerama_scr]);
|
||||
#endif
|
||||
|
||||
// Mask out the body of the window from the shadow
|
||||
// Doing it here instead of in make_shadow() for saving GPU
|
||||
// power and handling shaped windows (XXX unconfirmed)
|
||||
pixman_region32_subtract(®_tmp, ®_tmp, &bshape);
|
||||
pixman_region32_fini(&bshape);
|
||||
|
||||
// Detect if the region is empty before painting
|
||||
if (pixman_region32_not_empty(®_tmp)) {
|
||||
x_set_picture_clip_region(ps, xd->target_buffer, 0, 0, ®_tmp);
|
||||
xcb_render_composite(
|
||||
ps->c, XCB_RENDER_PICT_OP_OVER, wd->shadow_pict, alpha_pict,
|
||||
xd->target_buffer, 0, 0, 0, 0, dst_x + w->shadow_dx,
|
||||
dst_y + w->shadow_dy, w->shadow_width, w->shadow_height);
|
||||
}
|
||||
pixman_region32_fini(®_tmp);
|
||||
pixman_region32_fini(&shadow_reg);
|
||||
}
|
||||
|
||||
// Clip region of rendered_pict might be set during rendering, clear it to make
|
||||
// sure we get everything into the buffer
|
||||
x_clear_picture_clip_region(ps, wd->rendered_pict);
|
||||
|
||||
x_set_picture_clip_region(ps, xd->target_buffer, 0, 0, reg_paint);
|
||||
xcb_render_composite(ps->c, op, wd->rendered_pict, alpha_pict, xd->target_buffer,
|
||||
0, 0, 0, 0, dst_x, dst_y, w->widthb, w->heightb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset filter on a <code>Picture</code>.
|
||||
*/
|
||||
static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
|
||||
const char *filter = "Nearest";
|
||||
xcb_render_set_picture_filter(ps->c, p, strlen(filter), filter, 0, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
blur(void *backend_data, session_t *ps, double opacity, const region_t *reg_paint) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
const pixman_box32_t *reg = pixman_region32_extents((region_t *)reg_paint);
|
||||
const int height = reg->y2 - reg->y1;
|
||||
const int width = reg->x2 - reg->x1;
|
||||
|
||||
// Create a buffer for storing blurred picture, make it just big enough
|
||||
// for the blur region
|
||||
xcb_render_picture_t tmp_picture[2] = {
|
||||
x_create_picture_with_visual(ps, width, height, ps->vis, 0, NULL),
|
||||
x_create_picture_with_visual(ps, width, height, ps->vis, 0, NULL)};
|
||||
|
||||
region_t clip;
|
||||
pixman_region32_init(&clip);
|
||||
pixman_region32_copy(&clip, (region_t *)reg_paint);
|
||||
pixman_region32_translate(&clip, -reg->x1, -reg->y1);
|
||||
|
||||
if (!tmp_picture[0] || !tmp_picture[1]) {
|
||||
log_error("Failed to build intermediate Picture.");
|
||||
return false;
|
||||
}
|
||||
|
||||
x_set_picture_clip_region(ps, tmp_picture[0], 0, 0, &clip);
|
||||
x_set_picture_clip_region(ps, tmp_picture[1], 0, 0, &clip);
|
||||
|
||||
// The multipass blur implemented here is not correct, but this is what old
|
||||
// compton did anyway. XXX
|
||||
xcb_render_picture_t src_pict = xd->target_buffer, dst_pict = tmp_picture[0];
|
||||
auto alpha_pict = xd->alpha_pict[(int)(opacity * 255)];
|
||||
int current = 0;
|
||||
int src_x = reg->x1, src_y = reg->y1;
|
||||
|
||||
// For more than 1 pass, we do:
|
||||
// target_buffer -(pass 1)-> tmp0 -(pass 2)-> tmp1 ...
|
||||
// -(pass n-1)-> tmp0 or tmp1 -(pass n)-> target_buffer
|
||||
// For 1 pass, we do
|
||||
// target_buffer -(pass 1)-> tmp0 -(copy)-> target_buffer
|
||||
int i;
|
||||
for (i = 0; ps->o.blur_kerns[i]; i++) {
|
||||
assert(i < MAX_BLUR_PASS - 1);
|
||||
xcb_render_fixed_t *convolution_blur = ps->o.blur_kerns[i];
|
||||
int kwid = XFIXED_TO_DOUBLE(convolution_blur[0]),
|
||||
khei = XFIXED_TO_DOUBLE(convolution_blur[1]);
|
||||
|
||||
// Copy from source picture to destination. The filter must
|
||||
// be applied on source picture, to get the nearby pixels outside the
|
||||
// window.
|
||||
xcb_render_set_picture_filter(
|
||||
ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION,
|
||||
kwid * khei + 2, convolution_blur);
|
||||
|
||||
if (ps->o.blur_kerns[i + 1] || i == 0) {
|
||||
// This is not the last pass, or this is the first pass
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
||||
XCB_NONE, dst_pict, src_x, src_y, 0, 0, 0, 0,
|
||||
width, height);
|
||||
} else {
|
||||
// This is the last pass, and this is also not the first
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict,
|
||||
alpha_pict, xd->target_buffer, 0, 0, 0, 0,
|
||||
reg->x1, reg->y1, width, height);
|
||||
}
|
||||
|
||||
xrfilter_reset(ps, src_pict);
|
||||
|
||||
src_pict = tmp_picture[current];
|
||||
dst_pict = tmp_picture[!current];
|
||||
src_x = 0;
|
||||
src_y = 0;
|
||||
current = !current;
|
||||
}
|
||||
|
||||
// There is only 1 pass
|
||||
if (i == 1) {
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
|
||||
xd->target_buffer, 0, 0, 0, 0, reg->x1, reg->y1,
|
||||
width, height);
|
||||
}
|
||||
|
||||
xcb_render_free_picture(ps->c, tmp_picture[0]);
|
||||
xcb_render_free_picture(ps->c, tmp_picture[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void render_win(void *backend_data, session_t *ps, win *w, void *win_data,
|
||||
const region_t *reg_paint) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
struct _xrender_win_data *wd = win_data;
|
||||
xcb_drawable_t draw = wd->pixmap;
|
||||
if (!draw)
|
||||
draw = w->id;
|
||||
|
||||
w->pixmap_damaged = false;
|
||||
|
||||
region_t reg_paint_local;
|
||||
pixman_region32_init(®_paint_local);
|
||||
pixman_region32_copy(®_paint_local, (region_t *)reg_paint);
|
||||
pixman_region32_translate(®_paint_local, -w->g.x, -w->g.y);
|
||||
|
||||
if (!w->invert_color && w->frame_opacity == 1 && !w->dim) {
|
||||
// No extra processing needed
|
||||
wd->rendered_pict = wd->pict;
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't want to modify the content of the original window when we process
|
||||
// it, so we create a buffer.
|
||||
if (wd->buffer == XCB_NONE) {
|
||||
wd->buffer = x_create_picture_with_pictfmt(ps, w->widthb, w->heightb,
|
||||
w->pictfmt, 0, NULL);
|
||||
}
|
||||
|
||||
// Copy the content of the window over to the buffer
|
||||
x_clear_picture_clip_region(ps, wd->buffer);
|
||||
wd->rendered_pict = wd->buffer;
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, wd->pict, None,
|
||||
wd->rendered_pict, 0, 0, 0, 0, 0, 0, w->widthb, w->heightb);
|
||||
|
||||
if (w->invert_color) {
|
||||
// Handle invert color
|
||||
x_set_picture_clip_region(ps, wd->rendered_pict, 0, 0, ®_paint_local);
|
||||
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, None, wd->rendered_pict, 0, 0, 0, 0,
|
||||
0, 0, w->widthb, w->heightb);
|
||||
// We use an extra PictOpInReverse operation to get correct pixel
|
||||
// alpha. There could be a better solution.
|
||||
if (win_has_alpha(w))
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
wd->pict, None, wd->rendered_pict, 0, 0, 0,
|
||||
0, 0, 0, w->widthb, w->heightb);
|
||||
}
|
||||
|
||||
const double dopacity = get_opacity_percent(w);
|
||||
if (w->frame_opacity != 1) {
|
||||
// Handle transparent frame
|
||||
// Step 1: clip paint area to frame
|
||||
region_t frame_reg;
|
||||
pixman_region32_init(&frame_reg);
|
||||
pixman_region32_copy(&frame_reg, &w->bounding_shape);
|
||||
|
||||
region_t body_reg = win_get_region_noframe_local_by_val(w);
|
||||
pixman_region32_subtract(&frame_reg, &frame_reg, &body_reg);
|
||||
|
||||
// Draw the frame with frame opacity
|
||||
xcb_render_picture_t alpha_pict =
|
||||
xd->alpha_pict[(int)(w->frame_opacity * dopacity * 255)];
|
||||
x_set_picture_clip_region(ps, wd->rendered_pict, 0, 0, &frame_reg);
|
||||
|
||||
// Step 2: multiply alpha value
|
||||
// XXX test
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->white_pixel,
|
||||
alpha_pict, wd->rendered_pict, 0, 0, 0, 0, 0, 0,
|
||||
w->widthb, w->heightb);
|
||||
}
|
||||
|
||||
if (w->dim) {
|
||||
// Handle dimming
|
||||
|
||||
double dim_opacity = ps->o.inactive_dim;
|
||||
if (!ps->o.inactive_dim_fixed)
|
||||
dim_opacity *= get_opacity_percent(w);
|
||||
|
||||
xcb_render_color_t color = {
|
||||
.red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity};
|
||||
|
||||
// Dim the actually content of window
|
||||
xcb_rectangle_t rect = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = w->widthb,
|
||||
.height = w->heightb,
|
||||
};
|
||||
|
||||
x_clear_picture_clip_region(ps, wd->rendered_pict);
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER,
|
||||
wd->rendered_pict, color, 1, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
static void *prepare_win(void *backend_data, session_t *ps, win *w) {
|
||||
auto wd = ccalloc(1, struct _xrender_win_data);
|
||||
struct _xrender_data *xd = backend_data;
|
||||
assert(w->a.map_state == XCB_MAP_STATE_VIEWABLE);
|
||||
if (ps->has_name_pixmap) {
|
||||
wd->pixmap = xcb_generate_id(ps->c);
|
||||
xcb_composite_name_window_pixmap_checked(ps->c, w->id, wd->pixmap);
|
||||
}
|
||||
|
||||
xcb_drawable_t draw = wd->pixmap;
|
||||
if (!draw)
|
||||
draw = w->id;
|
||||
|
||||
log_trace("%s %x", w->name, wd->pixmap);
|
||||
wd->pict = x_create_picture_with_pictfmt_and_pixmap(ps, w->pictfmt, draw, 0, NULL);
|
||||
wd->buffer = XCB_NONE;
|
||||
|
||||
// XXX delay allocating shadow pict until compose() will dramatical
|
||||
// improve performance, probably because otherwise shadow pict
|
||||
// can be created and destroyed multiple times per draw.
|
||||
//
|
||||
// However doing that breaks a assumption the backend API makes (i.e.
|
||||
// either all needed data is here, or none is), therefore we will
|
||||
// leave this here until we have chance to re-think the backend API
|
||||
if (w->shadow) {
|
||||
xcb_pixmap_t pixmap;
|
||||
build_shadow(ps, 1, w->widthb, w->heightb, xd->shadow_pixel, &pixmap,
|
||||
&wd->shadow_pict);
|
||||
xcb_free_pixmap(ps->c, pixmap);
|
||||
}
|
||||
return wd;
|
||||
}
|
||||
|
||||
static void release_win(void *backend_data, session_t *ps, win *w, void *win_data) {
|
||||
struct _xrender_win_data *wd = win_data;
|
||||
xcb_free_pixmap(ps->c, wd->pixmap);
|
||||
// xcb_free_pixmap(ps->c, wd->shadow_pixmap);
|
||||
xcb_render_free_picture(ps->c, wd->pict);
|
||||
xcb_render_free_picture(ps->c, wd->shadow_pict);
|
||||
if (wd->buffer != XCB_NONE)
|
||||
xcb_render_free_picture(ps->c, wd->buffer);
|
||||
free(wd);
|
||||
}
|
||||
|
||||
static void *init(session_t *ps) {
|
||||
auto xd = ccalloc(1, struct _xrender_data);
|
||||
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
double o = (double)i / 255.0;
|
||||
xd->alpha_pict[i] = solid_picture(ps, false, o, 0, 0, 0);
|
||||
assert(xd->alpha_pict[i] != None);
|
||||
}
|
||||
|
||||
xd->black_pixel = solid_picture(ps, true, 1, 0, 0, 0);
|
||||
xd->white_pixel = solid_picture(ps, true, 1, 1, 1, 1);
|
||||
xd->shadow_pixel = solid_picture(ps, true, 1, ps->o.shadow_red,
|
||||
ps->o.shadow_green, ps->o.shadow_blue);
|
||||
|
||||
if (ps->overlay != XCB_NONE) {
|
||||
xd->target = x_create_picture_with_visual_and_pixmap(
|
||||
ps, ps->vis, ps->overlay, 0, NULL);
|
||||
} else {
|
||||
xcb_render_create_picture_value_list_t pa = {
|
||||
.subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS,
|
||||
};
|
||||
xd->target = x_create_picture_with_visual_and_pixmap(
|
||||
ps, ps->vis, ps->root, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
||||
}
|
||||
|
||||
xd->target_buffer = x_create_picture_with_visual(
|
||||
ps, ps->root_width, ps->root_height, ps->vis, 0, NULL);
|
||||
|
||||
xcb_pixmap_t root_pixmap = x_get_root_back_pixmap(ps);
|
||||
if (root_pixmap == XCB_NONE) {
|
||||
xd->root_pict = solid_picture(ps, false, 1, 0.5, 0.5, 0.5);
|
||||
} else {
|
||||
xd->root_pict = x_create_picture_with_visual_and_pixmap(
|
||||
ps, ps->vis, root_pixmap, 0, NULL);
|
||||
}
|
||||
return xd;
|
||||
}
|
||||
|
||||
static void deinit(void *backend_data, session_t *ps) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
for (int i = 0; i < 256; i++)
|
||||
xcb_render_free_picture(ps->c, xd->alpha_pict[i]);
|
||||
xcb_render_free_picture(ps->c, xd->white_pixel);
|
||||
xcb_render_free_picture(ps->c, xd->black_pixel);
|
||||
free(xd);
|
||||
}
|
||||
|
||||
static void *root_change(void *backend_data, session_t *ps) {
|
||||
deinit(backend_data, ps);
|
||||
return init(ps);
|
||||
}
|
||||
|
||||
static void paint_root(void *backend_data, session_t *ps, const region_t *reg_paint) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
|
||||
// Limit the paint area
|
||||
x_set_picture_clip_region(ps, xd->target_buffer, 0, 0, reg_paint);
|
||||
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->root_pict, XCB_NONE,
|
||||
xd->target_buffer, 0, 0, 0, 0, 0, 0, ps->root_width,
|
||||
ps->root_height);
|
||||
}
|
||||
|
||||
static void present(void *backend_data, session_t *ps) {
|
||||
struct _xrender_data *xd = backend_data;
|
||||
|
||||
// compose() sets clip region, so clear it first to make
|
||||
// sure we update the whole screen.
|
||||
x_clear_picture_clip_region(ps, xd->target_buffer);
|
||||
|
||||
// TODO buffer-age-like optimization might be possible here.
|
||||
// but that will require a different backend API
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->target_buffer, None,
|
||||
xd->target, 0, 0, 0, 0, 0, 0, ps->root_width,
|
||||
ps->root_height);
|
||||
}
|
||||
|
||||
struct backend_info xrender_backend = {
|
||||
.init = init,
|
||||
.deinit = deinit,
|
||||
.blur = blur,
|
||||
.present = present,
|
||||
.prepare = paint_root,
|
||||
.compose = compose,
|
||||
.root_change = root_change,
|
||||
.render_win = render_win,
|
||||
.prepare_win = prepare_win,
|
||||
.release_win = release_win,
|
||||
.is_win_transparent = default_is_win_transparent,
|
||||
.is_frame_transparent = default_is_frame_transparent,
|
||||
};
|
|
@ -83,8 +83,6 @@
|
|||
|
||||
#ifdef CONFIG_OPENGL
|
||||
// libGL
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
|
||||
#include <GL/glx.h>
|
||||
|
||||
// Workarounds for missing definitions in some broken GL drivers, thanks to
|
||||
|
@ -423,6 +421,8 @@ typedef struct session {
|
|||
ev_prepare event_check;
|
||||
/// Signal handler for SIGUSR1
|
||||
ev_signal usr1_signal;
|
||||
/// backend data
|
||||
void *backend_data;
|
||||
/// libev mainloop
|
||||
struct ev_loop *loop;
|
||||
// === Display related ===
|
||||
|
|
|
@ -2536,6 +2536,7 @@ reset_enable(EV_P_ ev_signal *w, int revents) {
|
|||
static session_t *
|
||||
session_init(session_t *ps_old, int argc, char **argv) {
|
||||
static const session_t s_def = {
|
||||
.backend_data = NULL,
|
||||
.dpy = NULL,
|
||||
.scr = 0,
|
||||
.c = NULL,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
deps = [
|
||||
base_deps = [
|
||||
cc.find_library('m'),
|
||||
cc.find_library('ev'),
|
||||
dependency('xcb', version: '>=1.9.2'),
|
||||
|
@ -6,10 +6,12 @@ deps = [
|
|||
|
||||
srcs = [ files('compton.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
|
||||
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
|
||||
'options.c')]
|
||||
'options.c') ]
|
||||
compton_inc = include_directories('.')
|
||||
|
||||
cflags = []
|
||||
|
||||
|
||||
required_package = [
|
||||
'x11', 'x11-xcb', 'xcb-renderutil',
|
||||
'xcb-render', 'xcb-damage', 'xcb-randr',
|
||||
|
@ -18,9 +20,11 @@ required_package = [
|
|||
]
|
||||
|
||||
foreach i : required_package
|
||||
deps += [dependency(i, required: true)]
|
||||
base_deps += [dependency(i, required: true)]
|
||||
endforeach
|
||||
|
||||
deps = []
|
||||
|
||||
if get_option('xinerama')
|
||||
deps += [dependency('xcb-xinerama', required: true)]
|
||||
cflags += ['-DCONFIG_XINERAMA']
|
||||
|
@ -47,7 +51,7 @@ if get_option('vsync_drm')
|
|||
endif
|
||||
|
||||
if get_option('opengl')
|
||||
cflags += ['-DCONFIG_OPENGL']
|
||||
cflags += ['-DCONFIG_OPENGL', '-DGL_GLEXT_PROTOTYPES']
|
||||
deps += [dependency('gl', required: true)]
|
||||
srcs += [ 'opengl.c' ]
|
||||
endif
|
||||
|
@ -63,4 +67,8 @@ if get_option('xrescheck')
|
|||
srcs += [ 'xrescheck.c' ]
|
||||
endif
|
||||
|
||||
executable('compton', srcs, c_args: cflags, dependencies: deps, install: true)
|
||||
subdir('backend')
|
||||
|
||||
executable('compton', srcs, c_args: cflags,
|
||||
dependencies: [ base_deps, deps ],
|
||||
install: true, include_directories: compton_inc)
|
||||
|
|
|
@ -725,6 +725,7 @@ void win_recheck_client(session_t *ps, win *w) {
|
|||
// TODO: probably split into win_new (in win.c) and add_win (in compton.c)
|
||||
bool add_win(session_t *ps, Window id, Window prev) {
|
||||
static const win win_def = {
|
||||
.win_data = NULL,
|
||||
.next = NULL,
|
||||
.prev_trans = NULL,
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
// FIXME shouldn't need this
|
||||
#ifdef CONFIG_OPENGL
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
|
||||
|
@ -75,6 +74,9 @@ typedef enum {
|
|||
/// Structure representing a top-level window compton manages.
|
||||
typedef struct win win;
|
||||
struct win {
|
||||
/// backend data attached to this window. Only available when
|
||||
/// `state` is not UNMAPPED
|
||||
void *win_data;
|
||||
/// Pointer to the next lower window in window stack.
|
||||
win *next;
|
||||
/// Pointer to the next higher window to paint.
|
||||
|
|
Loading…
Reference in New Issue