Re-did the painting logic, and document it. It is unclear to me what is the previous painting logic. But the current one is basically this: 1. Go through all windows top to bottom, and put visible windows (not unmapped, opacity > 0, etc) into a linked list, from bottom to top 2. Accumulate a region of ignore on each window, which is basically the region of screen that is obscured by all the windows above current one. 3. Paint all the visible windows from bottom to top. Subtract the region of ignore from the painting region. If we need to paint shadow, we subtract the body of the window from the shadow painting region too, because we don't want shadow behind the window. 4. region of ignore is invalidated when window stack change, an window on top moved or changed shape, when window changed between opaque and transparent, etc. Notes: It is unclear whether all the different shapes of a window (extents, noframe, border, bounding shape, etc) are calculated correctly or not. It is unclear if window shape related events are handled correctly or not. Need more testing. Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
331 lines
10 KiB
C
331 lines
10 KiB
C
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "common.h"
|
|
#include "config.h"
|
|
#include "utils.h"
|
|
#include "c2.h"
|
|
|
|
/**
|
|
* Parse a long number.
|
|
*/
|
|
bool
|
|
parse_long(const char *s, long *dest) {
|
|
const char *endptr = NULL;
|
|
long val = strtol(s, (char **) &endptr, 0);
|
|
if (!endptr || endptr == s) {
|
|
printf_errf("(\"%s\"): Invalid number.", s);
|
|
return false;
|
|
}
|
|
while (isspace(*endptr))
|
|
++endptr;
|
|
if (*endptr) {
|
|
printf_errf("(\"%s\"): Trailing characters.", s);
|
|
return false;
|
|
}
|
|
*dest = val;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Parse a floating-point number in matrix.
|
|
*/
|
|
const char *
|
|
parse_matrix_readnum(const char *src, double *dest) {
|
|
char *pc = NULL;
|
|
double val = strtod(src, &pc);
|
|
if (!pc || pc == src) {
|
|
printf_errf("(\"%s\"): No number found.", src);
|
|
return src;
|
|
}
|
|
|
|
while (*pc && (isspace(*pc) || ',' == *pc))
|
|
++pc;
|
|
|
|
*dest = val;
|
|
|
|
return pc;
|
|
}
|
|
|
|
/**
|
|
* Parse a matrix.
|
|
*/
|
|
xcb_render_fixed_t *
|
|
parse_matrix(session_t *ps, const char *src, const char **endptr) {
|
|
int wid = 0, hei = 0;
|
|
const char *pc = NULL;
|
|
xcb_render_fixed_t *matrix = NULL;
|
|
|
|
// Get matrix width and height
|
|
{
|
|
double val = 0.0;
|
|
if (src == (pc = parse_matrix_readnum(src, &val)))
|
|
goto parse_matrix_err;
|
|
src = pc;
|
|
wid = val;
|
|
if (src == (pc = parse_matrix_readnum(src, &val)))
|
|
goto parse_matrix_err;
|
|
src = pc;
|
|
hei = val;
|
|
}
|
|
|
|
// Validate matrix width and height
|
|
if (wid <= 0 || hei <= 0) {
|
|
printf_errf("(): Invalid matrix width/height.");
|
|
goto parse_matrix_err;
|
|
}
|
|
if (!(wid % 2 && hei % 2)) {
|
|
printf_errf("(): Width/height not odd.");
|
|
goto parse_matrix_err;
|
|
}
|
|
if (wid > 16 || hei > 16)
|
|
printf_errf("(): Matrix width/height too large, may slow down"
|
|
"rendering, and/or consume lots of memory");
|
|
|
|
// Allocate memory
|
|
matrix = calloc(wid * hei + 2, sizeof(xcb_render_fixed_t));
|
|
if (!matrix) {
|
|
printf_errf("(): Failed to allocate memory for matrix.");
|
|
goto parse_matrix_err;
|
|
}
|
|
|
|
// Read elements
|
|
{
|
|
int skip = hei / 2 * wid + wid / 2;
|
|
bool hasneg = false;
|
|
for (int i = 0; i < wid * hei; ++i) {
|
|
// Ignore the center element
|
|
if (i == skip) {
|
|
matrix[2 + i] = DOUBLE_TO_XFIXED(0);
|
|
continue;
|
|
}
|
|
double val = 0;
|
|
if (src == (pc = parse_matrix_readnum(src, &val)))
|
|
goto parse_matrix_err;
|
|
src = pc;
|
|
if (val < 0) hasneg = true;
|
|
matrix[2 + i] = DOUBLE_TO_XFIXED(val);
|
|
}
|
|
if (BKEND_XRENDER == ps->o.backend && hasneg)
|
|
printf_errf("(): A convolution kernel with negative values "
|
|
"may not work properly under X Render backend.");
|
|
}
|
|
|
|
// Detect trailing characters
|
|
for ( ;*pc && ';' != *pc; ++pc)
|
|
if (!isspace(*pc) && ',' != *pc) {
|
|
printf_errf("(): Trailing characters in matrix string.");
|
|
goto parse_matrix_err;
|
|
}
|
|
|
|
// Jump over spaces after ';'
|
|
if (';' == *pc) {
|
|
++pc;
|
|
while (*pc && isspace(*pc))
|
|
++pc;
|
|
}
|
|
|
|
// Require an end of string if endptr is not provided, otherwise
|
|
// copy end pointer to endptr
|
|
if (endptr)
|
|
*endptr = pc;
|
|
else if (*pc) {
|
|
printf_errf("(): Only one matrix expected.");
|
|
goto parse_matrix_err;
|
|
}
|
|
|
|
// Fill in width and height
|
|
matrix[0] = DOUBLE_TO_XFIXED(wid);
|
|
matrix[1] = DOUBLE_TO_XFIXED(hei);
|
|
|
|
return matrix;
|
|
|
|
parse_matrix_err:
|
|
free(matrix);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Parse a convolution kernel.
|
|
*/
|
|
xcb_render_fixed_t *
|
|
parse_conv_kern(session_t *ps, const char *src, const char **endptr) {
|
|
return parse_matrix(ps, src, endptr);
|
|
}
|
|
|
|
/**
|
|
* Parse a list of convolution kernels.
|
|
*/
|
|
bool
|
|
parse_conv_kern_lst(session_t *ps, const char *src, xcb_render_fixed_t **dest, int max) {
|
|
static const struct {
|
|
const char *name;
|
|
const char *kern_str;
|
|
} CONV_KERN_PREDEF[] = {
|
|
{ "3x3box", "3,3,1,1,1,1,1,1,1,1," },
|
|
{ "5x5box", "5,5,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1," },
|
|
{ "7x7box", "7,7,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1," },
|
|
{ "3x3gaussian", "3,3,0.243117,0.493069,0.243117,0.493069,0.493069,0.243117,0.493069,0.243117," },
|
|
{ "5x5gaussian", "5,5,0.003493,0.029143,0.059106,0.029143,0.003493,0.029143,0.243117,0.493069,0.243117,0.029143,0.059106,0.493069,0.493069,0.059106,0.029143,0.243117,0.493069,0.243117,0.029143,0.003493,0.029143,0.059106,0.029143,0.003493," },
|
|
{ "7x7gaussian", "7,7,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003," },
|
|
{ "9x9gaussian", "9,9,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000012,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000012,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000," },
|
|
{ "11x11gaussian", "11,11,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000000,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000000,0.000000,0.000012,0.001723,0.059106,0.493069,0.493069,0.059106,0.001723,0.000012,0.000000,0.000000,0.000006,0.000849,0.029143,0.243117,0.493069,0.243117,0.029143,0.000849,0.000006,0.000000,0.000000,0.000001,0.000102,0.003493,0.029143,0.059106,0.029143,0.003493,0.000102,0.000001,0.000000,0.000000,0.000000,0.000003,0.000102,0.000849,0.001723,0.000849,0.000102,0.000003,0.000000,0.000000,0.000000,0.000000,0.000000,0.000001,0.000006,0.000012,0.000006,0.000001,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000," },
|
|
};
|
|
for (unsigned int i = 0;
|
|
i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i)
|
|
if (!strcmp(CONV_KERN_PREDEF[i].name, src))
|
|
return parse_conv_kern_lst(ps, CONV_KERN_PREDEF[i].kern_str, dest, max);
|
|
|
|
int i = 0;
|
|
const char *pc = src;
|
|
|
|
// Free old kernels
|
|
for (i = 0; i < max; ++i) {
|
|
free(dest[i]);
|
|
dest[i] = NULL;
|
|
}
|
|
|
|
// Continue parsing until the end of source string
|
|
i = 0;
|
|
while (pc && *pc && i < max - 1) {
|
|
if (!(dest[i++] = parse_conv_kern(ps, pc, &pc)))
|
|
return false;
|
|
}
|
|
|
|
if (*pc) {
|
|
printf_errf("(): Too many blur kernels!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Parse a X geometry.
|
|
*
|
|
* ps->root_width and ps->root_height must be valid
|
|
*/
|
|
bool
|
|
parse_geometry(session_t *ps, const char *src, region_t *dest) {
|
|
pixman_region32_clear(dest);
|
|
if (!src)
|
|
return true;
|
|
if (!ps->root_width || !ps->root_height)
|
|
return true;
|
|
|
|
geometry_t geom = { .wid = -1, .hei = -1, .x = -1, .y = -1 };
|
|
long val = 0L;
|
|
char *endptr = NULL;
|
|
|
|
src = skip_space(src);
|
|
if (!*src)
|
|
goto parse_geometry_end;
|
|
|
|
// Parse width
|
|
// Must be base 10, because "0x0..." may appear
|
|
if (!('+' == *src || '-' == *src)) {
|
|
val = strtol(src, &endptr, 10);
|
|
if (endptr && src != endptr) {
|
|
geom.wid = val;
|
|
if (geom.wid < 0) {
|
|
printf_errf("(\"%s\"): Invalid width.", src);
|
|
return false;
|
|
}
|
|
src = endptr;
|
|
}
|
|
src = skip_space(src);
|
|
}
|
|
|
|
// Parse height
|
|
if ('x' == *src) {
|
|
++src;
|
|
val = strtol(src, &endptr, 10);
|
|
if (endptr && src != endptr) {
|
|
geom.hei = val;
|
|
if (geom.hei < 0) {
|
|
printf_errf("(\"%s\"): Invalid height.", src);
|
|
return false;
|
|
}
|
|
src = endptr;
|
|
}
|
|
src = skip_space(src);
|
|
}
|
|
|
|
// Parse x
|
|
if ('+' == *src || '-' == *src) {
|
|
val = strtol(src, &endptr, 10);
|
|
if (endptr && src != endptr) {
|
|
geom.x = val;
|
|
if (*src == '-')
|
|
geom.x += ps->root_width - geom.wid;
|
|
src = endptr;
|
|
}
|
|
src = skip_space(src);
|
|
}
|
|
|
|
// Parse y
|
|
if ('+' == *src || '-' == *src) {
|
|
val = strtol(src, &endptr, 10);
|
|
if (endptr && src != endptr) {
|
|
geom.y = val;
|
|
if (*src == '-')
|
|
geom.y += ps->root_height - geom.hei;
|
|
src = endptr;
|
|
}
|
|
src = skip_space(src);
|
|
}
|
|
|
|
if (*src) {
|
|
printf_errf("(\"%s\"): Trailing characters.", src);
|
|
return false;
|
|
}
|
|
|
|
parse_geometry_end:
|
|
pixman_region32_union_rect(dest, dest, geom.x, geom.y, geom.wid, geom.hei);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Parse a list of opacity rules.
|
|
*/
|
|
bool parse_rule_opacity(session_t *ps, const char *src) {
|
|
// Find opacity value
|
|
char *endptr = NULL;
|
|
long val = strtol(src, &endptr, 0);
|
|
if (!endptr || endptr == src) {
|
|
printf_errf("(\"%s\"): No opacity specified?", src);
|
|
return false;
|
|
}
|
|
if (val > 100 || val < 0) {
|
|
printf_errf("(\"%s\"): Opacity %ld invalid.", src, val);
|
|
return false;
|
|
}
|
|
|
|
// Skip over spaces
|
|
while (*endptr && isspace(*endptr))
|
|
++endptr;
|
|
if (':' != *endptr) {
|
|
printf_errf("(\"%s\"): Opacity terminator not found.", src);
|
|
return false;
|
|
}
|
|
++endptr;
|
|
|
|
// Parse pattern
|
|
// I hope 1-100 is acceptable for (void *)
|
|
return c2_parsed(ps, &ps->o.opacity_rules, endptr, (void *) val);
|
|
}
|
|
|
|
/**
|
|
* Add a pattern to a condition linked list.
|
|
*/
|
|
bool
|
|
condlst_add(session_t *ps, c2_lptr_t **pcondlst, const char *pattern) {
|
|
if (!pattern)
|
|
return false;
|
|
|
|
if (!c2_parse(ps, pcondlst, pattern))
|
|
exit(1);
|
|
|
|
return true;
|
|
}
|