Move rendering related functions out of compton.c
Moved: * Blur kernel related functions to kernel.c * Vsync related functions to vsync.c * paint related functions to render.c This will make the `split-backend` branch easier to rebase. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
parent
85c5d34ce1
commit
baeb4572ff
67
src/common.h
67
src/common.h
|
@ -138,6 +138,7 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
// === Constants ===
|
// === Constants ===
|
||||||
|
|
||||||
|
@ -354,7 +355,7 @@ typedef struct {
|
||||||
GLint unifm_factor_center;
|
GLint unifm_factor_center;
|
||||||
} glx_blur_pass_t;
|
} glx_blur_pass_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct glx_prog_main {
|
||||||
/// GLSL program.
|
/// GLSL program.
|
||||||
GLuint prog;
|
GLuint prog;
|
||||||
/// Location of uniform "opacity" in window GLSL program.
|
/// Location of uniform "opacity" in window GLSL program.
|
||||||
|
@ -379,11 +380,6 @@ typedef uint32_t glx_prog_main_t;
|
||||||
|
|
||||||
#define PAINT_INIT { .pixmap = None, .pict = None }
|
#define PAINT_INIT { .pixmap = None, .pict = None }
|
||||||
|
|
||||||
typedef struct conv {
|
|
||||||
int size;
|
|
||||||
double data[];
|
|
||||||
} conv;
|
|
||||||
|
|
||||||
/// Linked list type of atoms.
|
/// Linked list type of atoms.
|
||||||
typedef struct _latom {
|
typedef struct _latom {
|
||||||
Atom atom;
|
Atom atom;
|
||||||
|
@ -1308,18 +1304,6 @@ bkend_use_glx(session_t *ps) {
|
||||||
|| BKEND_XR_GLX_HYBRID == ps->o.backend;
|
|| BKEND_XR_GLX_HYBRID == ps->o.backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there's a GLX context.
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
glx_has_context(session_t *ps) {
|
|
||||||
#ifdef CONFIG_OPENGL
|
|
||||||
return ps->psglx && ps->psglx->context;
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a window is really focused.
|
* Check if a window is really focused.
|
||||||
*/
|
*/
|
||||||
|
@ -1483,53 +1467,6 @@ vsync_deinit(session_t *ps);
|
||||||
*/
|
*/
|
||||||
///@{
|
///@{
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a GLX texture.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
free_texture_r(session_t *ps, GLuint *ptexture) {
|
|
||||||
if (*ptexture) {
|
|
||||||
assert(glx_has_context(ps));
|
|
||||||
glDeleteTextures(1, ptexture);
|
|
||||||
*ptexture = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a GLX Framebuffer object.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
free_glx_fbo(session_t *ps, GLuint *pfbo) {
|
|
||||||
#ifdef CONFIG_OPENGL
|
|
||||||
if (*pfbo) {
|
|
||||||
glDeleteFramebuffers(1, pfbo);
|
|
||||||
*pfbo = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
assert(!*pfbo);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
|
||||||
/**
|
|
||||||
* Free data in glx_blur_cache_t on resize.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) {
|
|
||||||
free_texture_r(ps, &pbc->textures[0]);
|
|
||||||
free_texture_r(ps, &pbc->textures[1]);
|
|
||||||
pbc->width = 0;
|
|
||||||
pbc->height = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a glx_blur_cache_t
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
|
|
||||||
free_glx_fbo(ps, &pbc->fbo);
|
|
||||||
free_glx_bc_resize(ps, pbc);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
1492
src/compton.c
1492
src/compton.c
File diff suppressed because it is too large
Load Diff
105
src/compton.h
105
src/compton.h
|
@ -34,6 +34,7 @@
|
||||||
#include "x.h"
|
#include "x.h"
|
||||||
#include "c2.h"
|
#include "c2.h"
|
||||||
#include "log.h" // XXX clean up
|
#include "log.h" // XXX clean up
|
||||||
|
#include "render.h"
|
||||||
|
|
||||||
// == Functions ==
|
// == Functions ==
|
||||||
// TODO move static inline functions that are only used in compton.c, into
|
// TODO move static inline functions that are only used in compton.c, into
|
||||||
|
@ -53,16 +54,6 @@ win *find_toplevel2(session_t *ps, Window wid);
|
||||||
|
|
||||||
void map_win(session_t *ps, Window id);
|
void map_win(session_t *ps, Window id);
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset filter on a <code>Picture</code>.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
|
|
||||||
#define FILTER "Nearest"
|
|
||||||
xcb_render_set_picture_filter(ps->c, p, strlen(FILTER), FILTER, 0, NULL);
|
|
||||||
#undef FILTER
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtract two unsigned long values.
|
* Subtract two unsigned long values.
|
||||||
*
|
*
|
||||||
|
@ -103,17 +94,6 @@ array_wid_exists(const Window *arr, int count, Window wid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a <code>Picture</code>.
|
|
||||||
*/
|
|
||||||
inline static void
|
|
||||||
free_picture(session_t *ps, xcb_render_picture_t *p) {
|
|
||||||
if (*p) {
|
|
||||||
xcb_render_free_picture(ps->c, *p);
|
|
||||||
*p = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a condition list.
|
* Destroy a condition list.
|
||||||
*/
|
*/
|
||||||
|
@ -123,29 +103,7 @@ free_wincondlst(c2_lptr_t **pcondlst) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifndef CONFIG_OPENGL
|
||||||
/**
|
|
||||||
* Bind texture in paint_t if we are using GLX backend.
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
paint_bind_tex(session_t *ps, paint_t *ppaint,
|
|
||||||
unsigned wid, unsigned hei, unsigned depth, bool force)
|
|
||||||
{
|
|
||||||
if (!ppaint->pixmap)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))
|
|
||||||
return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static inline bool
|
|
||||||
paint_bind_tex(session_t *ps, paint_t *ppaint,
|
|
||||||
unsigned wid, unsigned hei, unsigned depth, bool force)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static inline void
|
static inline void
|
||||||
free_paint_glx(session_t *ps, paint_t *p) {}
|
free_paint_glx(session_t *ps, paint_t *p) {}
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -156,18 +114,6 @@ free_texture(session_t *ps, glx_texture_t **t) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Free paint_t.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
free_paint(session_t *ps, paint_t *ppaint) {
|
|
||||||
free_paint_glx(ps, ppaint);
|
|
||||||
free_picture(ps, &ppaint->pict);
|
|
||||||
if (ppaint->pixmap)
|
|
||||||
xcb_free_pixmap(ps->c, ppaint->pixmap);
|
|
||||||
ppaint->pixmap = XCB_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a XTextProperty of a single string.
|
* Create a XTextProperty of a single string.
|
||||||
*/
|
*/
|
||||||
|
@ -220,59 +166,14 @@ dump_drawable(session_t *ps, Drawable drawable) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate a pixmap.
|
|
||||||
*
|
|
||||||
* Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
|
|
||||||
* are better ways.
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
validate_pixmap(session_t *ps, xcb_pixmap_t pxmap) {
|
|
||||||
if (!pxmap) return false;
|
|
||||||
|
|
||||||
Window rroot = None;
|
|
||||||
int rx = 0, ry = 0;
|
|
||||||
unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0;
|
|
||||||
return XGetGeometry(ps->dpy, pxmap, &rroot, &rx, &ry,
|
|
||||||
&rwid, &rhei, &rborder, &rdepth) && rwid && rhei;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate pixmap of a window, and destroy pixmap and picture if invalid.
|
* Validate pixmap of a window, and destroy pixmap and picture if invalid.
|
||||||
*/
|
*/
|
||||||
static inline void
|
static inline void
|
||||||
win_validate_pixmap(session_t *ps, win *w) {
|
win_validate_pixmap(session_t *ps, win *w) {
|
||||||
// Destroy pixmap and picture, if invalid
|
// Destroy pixmap and picture, if invalid
|
||||||
if (!validate_pixmap(ps, w->paint.pixmap))
|
if (!x_validate_pixmap(ps, w->paint.pixmap))
|
||||||
free_paint(ps, &w->paint);
|
free_paint(ps, &w->paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize a convolution kernel.
|
|
||||||
*/
|
|
||||||
static inline void
|
|
||||||
normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *kern) {
|
|
||||||
double sum = 0.0;
|
|
||||||
for (int i = 0; i < wid * hei; ++i)
|
|
||||||
sum += XFIXED_TO_DOUBLE(kern[i]);
|
|
||||||
double factor = 1.0 / sum;
|
|
||||||
for (int i = 0; i < wid * hei; ++i)
|
|
||||||
kern[i] = DOUBLE_TO_XFIXED(XFIXED_TO_DOUBLE(kern[i]) * factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
|
||||||
/**
|
|
||||||
* Ensure we have a GLX context.
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
ensure_glx_context(session_t *ps) {
|
|
||||||
// Create GLX context
|
|
||||||
if (!glx_has_context(ps))
|
|
||||||
glx_init(ps, false);
|
|
||||||
|
|
||||||
return ps->psglx->context;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// vim: set et sw=2 :
|
// vim: set et sw=2 :
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A picture will help
|
||||||
|
*
|
||||||
|
* -center 0 width width+center
|
||||||
|
* -center +-----+-------------------+-----+
|
||||||
|
* | | | |
|
||||||
|
* | | | |
|
||||||
|
* 0 +-----+-------------------+-----+
|
||||||
|
* | | | |
|
||||||
|
* | | | |
|
||||||
|
* | | | |
|
||||||
|
* height +-----+-------------------+-----+
|
||||||
|
* | | | |
|
||||||
|
* height+ | | | |
|
||||||
|
* center +-----+-------------------+-----+
|
||||||
|
*/
|
||||||
|
|
||||||
|
double
|
||||||
|
sum_kernel(conv *map, int x, int y, int width, int height) {
|
||||||
|
int fx, fy;
|
||||||
|
double *g_data;
|
||||||
|
double *g_line = map->data;
|
||||||
|
int g_size = map->size;
|
||||||
|
int center = g_size / 2;
|
||||||
|
int fx_start, fx_end;
|
||||||
|
int fy_start, fy_end;
|
||||||
|
double v;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute set of filter values which are "in range",
|
||||||
|
* that's the set with:
|
||||||
|
* 0 <= x + (fx-center) && x + (fx-center) < width &&
|
||||||
|
* 0 <= y + (fy-center) && y + (fy-center) < height
|
||||||
|
*
|
||||||
|
* 0 <= x + (fx - center) x + fx - center < width
|
||||||
|
* center - x <= fx fx < width + center - x
|
||||||
|
*/
|
||||||
|
|
||||||
|
fx_start = center - x;
|
||||||
|
if (fx_start < 0)
|
||||||
|
fx_start = 0;
|
||||||
|
fx_end = width + center - x;
|
||||||
|
if (fx_end > g_size)
|
||||||
|
fx_end = g_size;
|
||||||
|
|
||||||
|
fy_start = center - y;
|
||||||
|
if (fy_start < 0)
|
||||||
|
fy_start = 0;
|
||||||
|
fy_end = height + center - y;
|
||||||
|
if (fy_end > g_size)
|
||||||
|
fy_end = g_size;
|
||||||
|
|
||||||
|
g_line = g_line + fy_start * g_size + fx_start;
|
||||||
|
|
||||||
|
v = 0;
|
||||||
|
|
||||||
|
for (fy = fy_start; fy < fy_end; fy++) {
|
||||||
|
g_data = g_line;
|
||||||
|
g_line += g_size;
|
||||||
|
|
||||||
|
for (fx = fx_start; fx < fx_end; fx++) {
|
||||||
|
v += *g_data++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v > 1)
|
||||||
|
v = 1;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double __attribute__((const)) gaussian(double r, double x, double y) {
|
||||||
|
// Formula can be found here:
|
||||||
|
// https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
|
||||||
|
// Except a special case for r == 0 to produce sharp shadows
|
||||||
|
if (r == 0)
|
||||||
|
return 1;
|
||||||
|
return exp(-0.5 * (x * x + y * y) / (r * r)) / (2 * M_PI * r * r);
|
||||||
|
}
|
||||||
|
|
||||||
|
conv *gaussian_kernel(double r) {
|
||||||
|
conv *c;
|
||||||
|
int size = r * 2 + 1;
|
||||||
|
int center = size / 2;
|
||||||
|
double t;
|
||||||
|
|
||||||
|
c = cvalloc(sizeof(conv) + size * size * sizeof(double));
|
||||||
|
c->size = size;
|
||||||
|
t = 0.0;
|
||||||
|
|
||||||
|
/*printf_errf("(): %f", r);*/
|
||||||
|
for (int y = 0; y < size; y++) {
|
||||||
|
for (int x = 0; x < size; x++) {
|
||||||
|
double g = gaussian(r, x - center, y - center);
|
||||||
|
t += g;
|
||||||
|
c->data[y * size + x] = g;
|
||||||
|
/*printf("%f ", c->data[y*size+x]);*/
|
||||||
|
}
|
||||||
|
/*printf("\n");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < size; y++) {
|
||||||
|
for (int x = 0; x < size; x++) {
|
||||||
|
c->data[y * size + x] /= t;
|
||||||
|
/*printf("%f ", c->data[y*size+x]);*/
|
||||||
|
}
|
||||||
|
/*printf("\n");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: set noet sw=8 ts=8 :
|
|
@ -0,0 +1,19 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// Code for generating convolution kernels
|
||||||
|
|
||||||
|
typedef struct conv {
|
||||||
|
int size;
|
||||||
|
double data[];
|
||||||
|
} conv;
|
||||||
|
|
||||||
|
/// Calculate the sum of a rectangle part of the convolution kernel
|
||||||
|
/// the rectangle is defined by top left (x, y), and a size (width x height)
|
||||||
|
double
|
||||||
|
sum_kernel(conv *map, int x, int y, int width, int height);
|
||||||
|
|
||||||
|
/// Create a kernel with gaussian distribution of radius r
|
||||||
|
conv *gaussian_kernel(double r);
|
|
@ -4,7 +4,8 @@ deps = [
|
||||||
dependency('xcb', version: '>=1.9.2'),
|
dependency('xcb', version: '>=1.9.2'),
|
||||||
]
|
]
|
||||||
|
|
||||||
srcs = ['compton.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'diagnostic.c', 'string_utils.c']
|
srcs = [ files('compton.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c',
|
||||||
|
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c')]
|
||||||
|
|
||||||
cflags = []
|
cflags = []
|
||||||
|
|
||||||
|
|
64
src/opengl.h
64
src/opengl.h
|
@ -199,6 +199,70 @@ glx_create_program_from_str(const char *vert_shader_str,
|
||||||
unsigned char *
|
unsigned char *
|
||||||
glx_take_screenshot(session_t *ps, int *out_length);
|
glx_take_screenshot(session_t *ps, int *out_length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there's a GLX context.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
glx_has_context(session_t *ps) {
|
||||||
|
return ps->psglx && ps->psglx->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure we have a GLX context.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
ensure_glx_context(session_t *ps) {
|
||||||
|
// Create GLX context
|
||||||
|
if (!glx_has_context(ps))
|
||||||
|
glx_init(ps, false);
|
||||||
|
|
||||||
|
return ps->psglx->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a GLX texture.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
free_texture_r(session_t *ps, GLuint *ptexture) {
|
||||||
|
if (*ptexture) {
|
||||||
|
assert(glx_has_context(ps));
|
||||||
|
glDeleteTextures(1, ptexture);
|
||||||
|
*ptexture = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a GLX Framebuffer object.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
free_glx_fbo(session_t *ps, GLuint *pfbo) {
|
||||||
|
if (*pfbo) {
|
||||||
|
glDeleteFramebuffers(1, pfbo);
|
||||||
|
*pfbo = 0;
|
||||||
|
}
|
||||||
|
assert(!*pfbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free data in glx_blur_cache_t on resize.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) {
|
||||||
|
free_texture_r(ps, &pbc->textures[0]);
|
||||||
|
free_texture_r(ps, &pbc->textures[1]);
|
||||||
|
pbc->width = 0;
|
||||||
|
pbc->height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a glx_blur_cache_t
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
|
||||||
|
free_glx_fbo(ps, &pbc->fbo);
|
||||||
|
free_glx_bc_resize(ps, pbc);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a glx_texture_t.
|
* Free a glx_texture_t.
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,32 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xcb/render.h>
|
||||||
|
#include "region.h"
|
||||||
|
|
||||||
|
typedef struct _glx_texture glx_texture_t;
|
||||||
|
typedef struct glx_prog_main glx_prog_main_t;
|
||||||
|
typedef struct win win;
|
||||||
|
typedef struct session session_t;
|
||||||
|
|
||||||
|
typedef struct paint {
|
||||||
|
xcb_pixmap_t pixmap;
|
||||||
|
xcb_render_picture_t pict;
|
||||||
|
glx_texture_t *ptex;
|
||||||
|
} paint_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
|
||||||
|
double opacity, bool argb, bool neg,
|
||||||
|
xcb_render_picture_t pict, glx_texture_t *ptex,
|
||||||
|
const region_t *reg_paint, const glx_prog_main_t *pprogram);
|
||||||
|
void
|
||||||
|
paint_one(session_t *ps, win *w, const region_t *reg_paint);
|
||||||
|
|
||||||
|
void
|
||||||
|
paint_all(session_t *ps, region_t *region, const region_t *region_real, win * const t);
|
||||||
|
|
||||||
|
void free_picture(xcb_connection_t *c, xcb_render_picture_t *p);
|
||||||
|
|
||||||
|
void free_paint(session_t *ps, paint_t *ppaint);
|
|
@ -1,3 +1,6 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
|
|
||||||
|
/// Function pointers to init VSync modes.
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "opengl.h"
|
||||||
|
|
||||||
|
#include "vsync.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize DRM VSync.
|
||||||
|
*
|
||||||
|
* @return true for success, false otherwise
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
vsync_drm_init(session_t *ps) {
|
||||||
|
#ifdef CONFIG_VSYNC_DRM
|
||||||
|
// Should we always open card0?
|
||||||
|
if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) {
|
||||||
|
printf_errf("(): Failed to open device.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vsync_drm_wait(ps))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
printf_errf("(): Program not compiled with DRM VSync support.");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize OpenGL VSync.
|
||||||
|
*
|
||||||
|
* Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e
|
||||||
|
* Possible original source: http://www.inb.uni-luebeck.de/~boehme/xvideo_sync.html
|
||||||
|
*
|
||||||
|
* @return true for success, false otherwise
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
vsync_opengl_init(session_t *ps) {
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
if (!ensure_glx_context(ps))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!glx_hasglxext(ps, "GLX_SGI_video_sync")) {
|
||||||
|
printf_errf("(): Your driver doesn't support SGI_video_sync, giving up.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get video sync functions
|
||||||
|
if (!ps->psglx->glXGetVideoSyncSGI)
|
||||||
|
ps->psglx->glXGetVideoSyncSGI = (f_GetVideoSync)
|
||||||
|
glXGetProcAddress((const GLubyte *) "glXGetVideoSyncSGI");
|
||||||
|
if (!ps->psglx->glXWaitVideoSyncSGI)
|
||||||
|
ps->psglx->glXWaitVideoSyncSGI = (f_WaitVideoSync)
|
||||||
|
glXGetProcAddress((const GLubyte *) "glXWaitVideoSyncSGI");
|
||||||
|
if (!ps->psglx->glXWaitVideoSyncSGI || !ps->psglx->glXGetVideoSyncSGI) {
|
||||||
|
printf_errf("(): Failed to get glXWait/GetVideoSyncSGI function.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
printf_errf("(): Program not compiled with OpenGL VSync support.");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vsync_opengl_oml_init(session_t *ps) {
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
if (!ensure_glx_context(ps))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!glx_hasglxext(ps, "GLX_OML_sync_control")) {
|
||||||
|
printf_errf("(): Your driver doesn't support OML_sync_control, giving up.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get video sync functions
|
||||||
|
if (!ps->psglx->glXGetSyncValuesOML)
|
||||||
|
ps->psglx->glXGetSyncValuesOML = (f_GetSyncValuesOML)
|
||||||
|
glXGetProcAddress ((const GLubyte *) "glXGetSyncValuesOML");
|
||||||
|
if (!ps->psglx->glXWaitForMscOML)
|
||||||
|
ps->psglx->glXWaitForMscOML = (f_WaitForMscOML)
|
||||||
|
glXGetProcAddress ((const GLubyte *) "glXWaitForMscOML");
|
||||||
|
if (!ps->psglx->glXGetSyncValuesOML || !ps->psglx->glXWaitForMscOML) {
|
||||||
|
printf_errf("(): Failed to get OML_sync_control functions.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
printf_errf("(): Program not compiled with OpenGL VSync support.");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vsync_opengl_swc_swap_interval(session_t *ps, unsigned int interval) {
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
if (!ensure_glx_context(ps))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!ps->psglx->glXSwapIntervalProc && !ps->psglx->glXSwapIntervalMESAProc) {
|
||||||
|
if (glx_hasglxext(ps, "GLX_MESA_swap_control")) {
|
||||||
|
ps->psglx->glXSwapIntervalMESAProc = (f_SwapIntervalMESA)
|
||||||
|
glXGetProcAddress ((const GLubyte *) "glXSwapIntervalMESA");
|
||||||
|
} else if (glx_hasglxext(ps, "GLX_SGI_swap_control")) {
|
||||||
|
ps->psglx->glXSwapIntervalProc = (f_SwapIntervalSGI)
|
||||||
|
glXGetProcAddress ((const GLubyte *) "glXSwapIntervalSGI");
|
||||||
|
} else {
|
||||||
|
printf_errf("(): Your driver doesn't support SGI_swap_control nor MESA_swap_control, giving up.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ps->psglx->glXSwapIntervalMESAProc)
|
||||||
|
ps->psglx->glXSwapIntervalMESAProc(interval);
|
||||||
|
else if (ps->psglx->glXSwapIntervalProc)
|
||||||
|
ps->psglx->glXSwapIntervalProc(interval);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vsync_opengl_swc_init(session_t *ps) {
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
if (!bkend_use_glx(ps)) {
|
||||||
|
printf_errf("(): OpenGL swap control requires the GLX backend.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vsync_opengl_swc_swap_interval(ps, 1)) {
|
||||||
|
printf_errf("(): Failed to load a swap control extension.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
printf_errf("(): Program not compiled with OpenGL VSync support.");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vsync_opengl_mswc_init(session_t *ps) {
|
||||||
|
printf_errf("(): opengl-mswc is deprecated, please use opengl-swc instead.");
|
||||||
|
return vsync_opengl_swc_init(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool (*const VSYNC_FUNCS_INIT[])(session_t *ps) = {
|
||||||
|
[VSYNC_DRM ] = vsync_drm_init,
|
||||||
|
[VSYNC_OPENGL ] = vsync_opengl_init,
|
||||||
|
[VSYNC_OPENGL_OML ] = vsync_opengl_oml_init,
|
||||||
|
[VSYNC_OPENGL_SWC ] = vsync_opengl_swc_init,
|
||||||
|
[VSYNC_OPENGL_MSWC ] = vsync_opengl_mswc_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_VSYNC_DRM
|
||||||
|
/**
|
||||||
|
* Wait for next VSync, DRM method.
|
||||||
|
*
|
||||||
|
* Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vsync_drm_wait(session_t *ps) {
|
||||||
|
int ret = -1;
|
||||||
|
drm_wait_vblank_t vbl;
|
||||||
|
|
||||||
|
vbl.request.type = _DRM_VBLANK_RELATIVE,
|
||||||
|
vbl.request.sequence = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl);
|
||||||
|
vbl.request.type &= ~_DRM_VBLANK_RELATIVE;
|
||||||
|
} while (ret && errno == EINTR);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
fprintf(stderr, "vsync_drm_wait(): VBlank ioctl did not work, "
|
||||||
|
"unimplemented in this drmver?\n");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
/**
|
||||||
|
* Wait for next VSync, OpenGL method.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vsync_opengl_wait(session_t *ps) {
|
||||||
|
unsigned vblank_count = 0;
|
||||||
|
|
||||||
|
ps->psglx->glXGetVideoSyncSGI(&vblank_count);
|
||||||
|
ps->psglx->glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count);
|
||||||
|
// I see some code calling glXSwapIntervalSGI(1) afterwards, is it required?
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for next VSync, OpenGL OML method.
|
||||||
|
*
|
||||||
|
* https://mail.gnome.org/archives/clutter-list/2012-November/msg00031.html
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vsync_opengl_oml_wait(session_t *ps) {
|
||||||
|
int64_t ust = 0, msc = 0, sbc = 0;
|
||||||
|
|
||||||
|
ps->psglx->glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
|
||||||
|
ps->psglx->glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2,
|
||||||
|
&ust, &msc, &sbc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Function pointers to wait for VSync.
|
||||||
|
int (*const VSYNC_FUNCS_WAIT[])(session_t *ps) = {
|
||||||
|
#ifdef CONFIG_VSYNC_DRM
|
||||||
|
[VSYNC_DRM ] = vsync_drm_wait,
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
[VSYNC_OPENGL ] = vsync_opengl_wait,
|
||||||
|
[VSYNC_OPENGL_OML ] = vsync_opengl_oml_wait,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
static void
|
||||||
|
vsync_opengl_swc_deinit(session_t *ps) {
|
||||||
|
vsync_opengl_swc_swap_interval(ps, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// Function pointers to deinitialize VSync.
|
||||||
|
void (*const VSYNC_FUNCS_DEINIT[])(session_t *ps) = {
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
|
[VSYNC_OPENGL_SWC ] = vsync_opengl_swc_deinit,
|
||||||
|
[VSYNC_OPENGL_MSWC ] = vsync_opengl_swc_deinit,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize current VSync method.
|
||||||
|
*/
|
||||||
|
bool vsync_init(session_t *ps) {
|
||||||
|
// Mesa turns on swap control by default, undo that
|
||||||
|
if (bkend_use_glx(ps))
|
||||||
|
vsync_opengl_swc_swap_interval(ps, 0);
|
||||||
|
|
||||||
|
if (ps->o.vsync && VSYNC_FUNCS_INIT[ps->o.vsync]
|
||||||
|
&& !VSYNC_FUNCS_INIT[ps->o.vsync](ps)) {
|
||||||
|
ps->o.vsync = VSYNC_NONE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for next VSync.
|
||||||
|
*/
|
||||||
|
void vsync_wait(session_t *ps) {
|
||||||
|
if (!ps->o.vsync)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (VSYNC_FUNCS_WAIT[ps->o.vsync])
|
||||||
|
VSYNC_FUNCS_WAIT[ps->o.vsync](ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deinitialize current VSync method.
|
||||||
|
*/
|
||||||
|
void vsync_deinit(session_t *ps) {
|
||||||
|
if (ps->o.vsync && VSYNC_FUNCS_DEINIT[ps->o.vsync])
|
||||||
|
VSYNC_FUNCS_DEINIT[ps->o.vsync](ps);
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct session session_t;
|
||||||
|
|
||||||
|
bool vsync_init(session_t *ps);
|
||||||
|
void vsync_wait(session_t *ps);
|
||||||
|
void vsync_deinit(session_t *ps);
|
32
src/win.c
32
src/win.c
|
@ -1289,3 +1289,35 @@ void win_ev_stop(session_t *ps, win *w) {
|
||||||
xcb_shape_select_input(ps->c, w->id, 0));
|
xcb_shape_select_input(ps->c, w->id, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set fade callback of a window, and possibly execute the previous
|
||||||
|
* callback.
|
||||||
|
*
|
||||||
|
* If a callback can cause rendering result to change, it should call
|
||||||
|
* `queue_redraw`.
|
||||||
|
*
|
||||||
|
* @param exec_callback whether the previous callback is to be executed
|
||||||
|
*/
|
||||||
|
void win_set_fade_callback(session_t *ps, win **_w,
|
||||||
|
void (*callback) (session_t *ps, win **w), bool exec_callback) {
|
||||||
|
win *w = *_w;
|
||||||
|
void (*old_callback) (session_t *ps, win **w) = w->fade_callback;
|
||||||
|
|
||||||
|
w->fade_callback = callback;
|
||||||
|
// Must be the last line as the callback could destroy w!
|
||||||
|
if (exec_callback && old_callback)
|
||||||
|
old_callback(ps, _w);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute fade callback of a window if fading finished.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
win_check_fade_finished(session_t *ps, win **_w) {
|
||||||
|
win *w = *_w;
|
||||||
|
if (w->fade_callback && w->opacity == w->opacity_tgt) {
|
||||||
|
// Must be the last line as the callback could destroy w!
|
||||||
|
win_set_fade_callback(ps, _w, NULL, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
26
src/win.h
26
src/win.h
|
@ -16,17 +16,11 @@
|
||||||
#include "x.h"
|
#include "x.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "c2.h"
|
#include "c2.h"
|
||||||
|
#include "render.h"
|
||||||
|
|
||||||
typedef struct session session_t;
|
typedef struct session session_t;
|
||||||
typedef struct _glx_texture glx_texture_t;
|
typedef struct _glx_texture glx_texture_t;
|
||||||
|
|
||||||
// FIXME not the best place for this type
|
|
||||||
typedef struct {
|
|
||||||
xcb_pixmap_t pixmap;
|
|
||||||
xcb_render_picture_t pict;
|
|
||||||
glx_texture_t *ptex;
|
|
||||||
} paint_t;
|
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
// FIXME this type should be in opengl.h
|
// FIXME this type should be in opengl.h
|
||||||
// it is very unideal for it to be here
|
// it is very unideal for it to be here
|
||||||
|
@ -335,6 +329,24 @@ void
|
||||||
win_update_frame_extents(session_t *ps, win *w, Window client);
|
win_update_frame_extents(session_t *ps, win *w, Window client);
|
||||||
bool add_win(session_t *ps, Window id, Window prev);
|
bool add_win(session_t *ps, Window id, Window prev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set fade callback of a window, and possibly execute the previous
|
||||||
|
* callback.
|
||||||
|
*
|
||||||
|
* If a callback can cause rendering result to change, it should call
|
||||||
|
* `queue_redraw`.
|
||||||
|
*
|
||||||
|
* @param exec_callback whether the previous callback is to be executed
|
||||||
|
*/
|
||||||
|
void win_set_fade_callback(session_t *ps, win **_w,
|
||||||
|
void (*callback) (session_t *ps, win **w), bool exec_callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute fade callback of a window if fading finished.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
win_check_fade_finished(session_t *ps, win **_w);
|
||||||
|
|
||||||
// Stop receiving events (except ConfigureNotify, XXX why?) from a window
|
// Stop receiving events (except ConfigureNotify, XXX why?) from a window
|
||||||
void win_ev_stop(session_t *ps, win *w);
|
void win_ev_stop(session_t *ps, win *w);
|
||||||
|
|
||||||
|
|
17
src/x.c
17
src/x.c
|
@ -377,3 +377,20 @@ x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t
|
||||||
free(err);
|
free(err);
|
||||||
return XCB_NONE;
|
return XCB_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a pixmap.
|
||||||
|
*
|
||||||
|
* Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
|
||||||
|
* are better ways.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
x_validate_pixmap(session_t *ps, xcb_pixmap_t pxmap) {
|
||||||
|
if (!pxmap) return false;
|
||||||
|
|
||||||
|
Window rroot = None;
|
||||||
|
int rx = 0, ry = 0;
|
||||||
|
unsigned rwid = 0, rhei = 0, rborder = 0, rdepth = 0;
|
||||||
|
return XGetGeometry(ps->dpy, pxmap, &rroot, &rx, &ry,
|
||||||
|
&rwid, &rhei, &rborder, &rdepth) && rwid && rhei;
|
||||||
|
}
|
||||||
|
|
3
src/x.h
3
src/x.h
|
@ -137,6 +137,9 @@ x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_
|
||||||
xcb_pixmap_t
|
xcb_pixmap_t
|
||||||
x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height);
|
x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height);
|
||||||
|
|
||||||
|
bool
|
||||||
|
x_validate_pixmap(session_t *ps, xcb_pixmap_t pxmap);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a <code>winprop_t</code>.
|
* Free a <code>winprop_t</code>.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue