2018-10-04 05:14:51 +08:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Copyright (c) 2011-2013, Christopher Jeffrey
|
|
|
|
// Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
|
|
|
|
|
2019-01-21 05:15:20 +08:00
|
|
|
#include <math.h>
|
2018-08-22 19:58:49 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdbool.h>
|
2019-01-21 05:15:20 +08:00
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
|
2018-08-22 19:58:49 +08:00
|
|
|
|
2018-12-16 02:47:21 +08:00
|
|
|
#include "compiler.h"
|
2018-08-22 19:58:49 +08:00
|
|
|
#include "common.h"
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
#include "utils.h"
|
2018-08-22 20:26:42 +08:00
|
|
|
#include "c2.h"
|
2018-12-16 01:42:37 +08:00
|
|
|
#include "string_utils.h"
|
2018-12-16 01:53:17 +08:00
|
|
|
#include "log.h"
|
2019-01-21 00:53:39 +08:00
|
|
|
#include "region.h"
|
|
|
|
#include "types.h"
|
|
|
|
#include "win.h"
|
2018-12-16 01:42:37 +08:00
|
|
|
|
|
|
|
#include "config.h"
|
2018-08-22 19:58:49 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Invalid number: %s", s);
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while (isspace(*endptr))
|
|
|
|
++endptr;
|
|
|
|
if (*endptr) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Trailing characters: %s", s);
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*dest = val;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a floating-point number in matrix.
|
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
parse_matrix_readnum(const char *src, double *dest) {
|
Parse number locale-independently
Previously we were using glibc's strtod function to parse floating point
numbers. The problem is, strtod is locale dependent. Meaning 7,7 might
be parsed as two numbers (7 and 7) in one locale, and parsed as one
number (7 point 7) in another locale. This is undesirable.
We need to set the locale to a value we know to make number parsing
consistently. We could use setlocale(), but that is not thread-safe. We
can also use uselocale(), which is thread-safe, but doesn't cover strtod
(Yeah, some of the locale-aware functions only acknowledge the global
locale, not the thread local one).
So in frustration, I just wrote a simple floating point number parser
myself. This parser obviously doesn't cover all cases strtod covers, but
is good enough for our needs.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2019-02-02 09:22:41 +08:00
|
|
|
const char *pc = NULL;
|
|
|
|
double val = strtod_simple(src, &pc);
|
2018-08-22 19:58:49 +08:00
|
|
|
if (!pc || pc == src) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("No number found: %s", src);
|
2018-08-22 19:58:49 +08:00
|
|
|
return src;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (*pc && (isspace(*pc) || ',' == *pc))
|
|
|
|
++pc;
|
|
|
|
|
|
|
|
*dest = val;
|
|
|
|
|
|
|
|
return pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a matrix.
|
|
|
|
*/
|
2018-09-28 00:14:44 +08:00
|
|
|
xcb_render_fixed_t *
|
2018-12-22 00:25:28 +08:00
|
|
|
parse_matrix(const char *src, const char **endptr, bool *hasneg) {
|
2018-08-22 19:58:49 +08:00
|
|
|
int wid = 0, hei = 0;
|
2018-12-22 00:25:28 +08:00
|
|
|
*hasneg = false;
|
|
|
|
|
2018-08-22 19:58:49 +08:00
|
|
|
const char *pc = NULL;
|
|
|
|
|
|
|
|
// Get matrix width and height
|
|
|
|
{
|
|
|
|
double val = 0.0;
|
|
|
|
if (src == (pc = parse_matrix_readnum(src, &val)))
|
2018-12-16 02:47:21 +08:00
|
|
|
goto err1;
|
2018-08-22 19:58:49 +08:00
|
|
|
src = pc;
|
|
|
|
wid = val;
|
|
|
|
if (src == (pc = parse_matrix_readnum(src, &val)))
|
2018-12-16 02:47:21 +08:00
|
|
|
goto err1;
|
2018-08-22 19:58:49 +08:00
|
|
|
src = pc;
|
|
|
|
hei = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate matrix width and height
|
|
|
|
if (wid <= 0 || hei <= 0) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Invalid matrix width/height.");
|
2018-12-16 02:47:21 +08:00
|
|
|
goto err1;
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
if (!(wid % 2 && hei % 2)) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Width/height not odd.");
|
2018-12-16 02:47:21 +08:00
|
|
|
goto err1;
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
if (wid > 16 || hei > 16)
|
2018-12-21 01:35:45 +08:00
|
|
|
log_warn("Matrix width/height too large, may slow down"
|
|
|
|
"rendering, and/or consume lots of memory");
|
2018-08-22 19:58:49 +08:00
|
|
|
|
|
|
|
// Allocate memory
|
2018-12-16 02:47:21 +08:00
|
|
|
auto matrix = ccalloc(wid * hei + 2, xcb_render_fixed_t);
|
2018-08-22 19:58:49 +08:00
|
|
|
|
|
|
|
// Read elements
|
|
|
|
{
|
|
|
|
int skip = hei / 2 * wid + wid / 2;
|
|
|
|
for (int i = 0; i < wid * hei; ++i) {
|
|
|
|
// Ignore the center element
|
|
|
|
if (i == skip) {
|
2018-09-28 00:14:44 +08:00
|
|
|
matrix[2 + i] = DOUBLE_TO_XFIXED(0);
|
2018-08-22 19:58:49 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
double val = 0;
|
|
|
|
if (src == (pc = parse_matrix_readnum(src, &val)))
|
2018-12-16 02:47:21 +08:00
|
|
|
goto err2;
|
2018-08-22 19:58:49 +08:00
|
|
|
src = pc;
|
2018-12-22 00:25:28 +08:00
|
|
|
if (val < 0) *hasneg = true;
|
2018-09-28 00:14:44 +08:00
|
|
|
matrix[2 + i] = DOUBLE_TO_XFIXED(val);
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect trailing characters
|
|
|
|
for ( ;*pc && ';' != *pc; ++pc)
|
|
|
|
if (!isspace(*pc) && ',' != *pc) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Trailing characters in matrix string.");
|
2018-12-16 02:47:21 +08:00
|
|
|
goto err2;
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Only one matrix expected.");
|
2018-12-16 02:47:21 +08:00
|
|
|
goto err2;
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Fill in width and height
|
2018-09-28 00:14:44 +08:00
|
|
|
matrix[0] = DOUBLE_TO_XFIXED(wid);
|
|
|
|
matrix[1] = DOUBLE_TO_XFIXED(hei);
|
2018-08-22 19:58:49 +08:00
|
|
|
|
|
|
|
return matrix;
|
|
|
|
|
2018-12-16 02:47:21 +08:00
|
|
|
err2:
|
2018-08-22 19:58:49 +08:00
|
|
|
free(matrix);
|
2018-12-16 02:47:21 +08:00
|
|
|
err1:
|
2018-08-22 19:58:49 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a convolution kernel.
|
2018-12-22 00:25:28 +08:00
|
|
|
*
|
|
|
|
* Output:
|
|
|
|
* hasneg: whether the convolution kernel has negative values
|
2018-08-22 19:58:49 +08:00
|
|
|
*/
|
2018-09-28 00:14:44 +08:00
|
|
|
xcb_render_fixed_t *
|
2018-12-22 00:25:28 +08:00
|
|
|
parse_conv_kern(const char *src, const char **endptr, bool *hasneg) {
|
|
|
|
return parse_matrix(src, endptr, hasneg);
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a list of convolution kernels.
|
2018-12-22 00:25:28 +08:00
|
|
|
*
|
|
|
|
* Output:
|
|
|
|
* hasneg: whether any of the convolution kernel has negative values
|
2018-08-22 19:58:49 +08:00
|
|
|
*/
|
|
|
|
bool
|
2018-12-22 00:25:28 +08:00
|
|
|
parse_conv_kern_lst(const char *src, xcb_render_fixed_t **dest, int max, bool *hasneg) {
|
2018-08-22 19:58:49 +08:00
|
|
|
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," },
|
|
|
|
};
|
2018-12-22 00:25:28 +08:00
|
|
|
|
|
|
|
*hasneg = false;
|
|
|
|
|
2018-08-22 21:25:40 +08:00
|
|
|
for (unsigned int i = 0;
|
2018-08-22 19:58:49 +08:00
|
|
|
i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i)
|
|
|
|
if (!strcmp(CONV_KERN_PREDEF[i].name, src))
|
2018-12-22 00:25:28 +08:00
|
|
|
return parse_conv_kern_lst(CONV_KERN_PREDEF[i].kern_str, dest, max, hasneg);
|
2018-08-22 19:58:49 +08:00
|
|
|
|
|
|
|
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) {
|
2018-12-22 00:25:28 +08:00
|
|
|
bool tmp_hasneg;
|
|
|
|
if (!(dest[i++] = parse_conv_kern(pc, &pc, &tmp_hasneg)))
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
2018-12-22 00:25:28 +08:00
|
|
|
*hasneg |= tmp_hasneg;
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
2018-11-10 22:57:41 +08:00
|
|
|
if (i > 1) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_warn("You are seeing this message because your are using multipassblur. Please "
|
|
|
|
"report an issue to us so we know multipass blur is actually been used. "
|
|
|
|
"Otherwise it might be removed in future releases");
|
2018-11-10 22:57:41 +08:00
|
|
|
}
|
|
|
|
|
2018-08-22 19:58:49 +08:00
|
|
|
if (*pc) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Too many blur kernels!");
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a X geometry.
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
*
|
|
|
|
* ps->root_width and ps->root_height must be valid
|
2018-08-22 19:58:49 +08:00
|
|
|
*/
|
|
|
|
bool
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
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;
|
|
|
|
|
2018-10-04 01:20:35 +08:00
|
|
|
geometry_t geom = { .wid = ps->root_width, .hei = ps->root_height, .x = 0, .y = 0 };
|
2018-08-22 19:58:49 +08:00
|
|
|
long val = 0L;
|
|
|
|
char *endptr = NULL;
|
|
|
|
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
src = skip_space(src);
|
|
|
|
if (!*src)
|
|
|
|
goto parse_geometry_end;
|
2018-08-22 19:58:49 +08:00
|
|
|
|
|
|
|
// Parse width
|
|
|
|
// Must be base 10, because "0x0..." may appear
|
|
|
|
if (!('+' == *src || '-' == *src)) {
|
|
|
|
val = strtol(src, &endptr, 10);
|
2018-10-04 01:20:35 +08:00
|
|
|
assert(endptr);
|
|
|
|
if (src != endptr) {
|
2018-08-22 19:58:49 +08:00
|
|
|
geom.wid = val;
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
if (geom.wid < 0) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Invalid width: %s", src);
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
return false;
|
|
|
|
}
|
2018-08-22 19:58:49 +08:00
|
|
|
src = endptr;
|
|
|
|
}
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
src = skip_space(src);
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse height
|
|
|
|
if ('x' == *src) {
|
|
|
|
++src;
|
|
|
|
val = strtol(src, &endptr, 10);
|
2018-10-04 01:20:35 +08:00
|
|
|
assert(endptr);
|
|
|
|
if (src != endptr) {
|
2018-08-22 19:58:49 +08:00
|
|
|
geom.hei = val;
|
|
|
|
if (geom.hei < 0) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Invalid height: %s", src);
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
src = endptr;
|
|
|
|
}
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
src = skip_space(src);
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse x
|
|
|
|
if ('+' == *src || '-' == *src) {
|
|
|
|
val = strtol(src, &endptr, 10);
|
|
|
|
if (endptr && src != endptr) {
|
|
|
|
geom.x = val;
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
if (*src == '-')
|
|
|
|
geom.x += ps->root_width - geom.wid;
|
2018-08-22 19:58:49 +08:00
|
|
|
src = endptr;
|
|
|
|
}
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
src = skip_space(src);
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse y
|
|
|
|
if ('+' == *src || '-' == *src) {
|
|
|
|
val = strtol(src, &endptr, 10);
|
|
|
|
if (endptr && src != endptr) {
|
|
|
|
geom.y = val;
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
if (*src == '-')
|
|
|
|
geom.y += ps->root_height - geom.hei;
|
2018-08-22 19:58:49 +08:00
|
|
|
src = endptr;
|
|
|
|
}
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
src = skip_space(src);
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*src) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Trailing characters: %s", src);
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
parse_geometry_end:
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 11:56:00 +08:00
|
|
|
pixman_region32_union_rect(dest, dest, geom.x, geom.y, geom.wid, geom.hei);
|
2018-08-22 19:58:49 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse a list of opacity rules.
|
|
|
|
*/
|
2018-12-22 00:25:28 +08:00
|
|
|
bool parse_rule_opacity(c2_lptr_t **res, const char *src) {
|
2018-08-22 19:58:49 +08:00
|
|
|
// Find opacity value
|
|
|
|
char *endptr = NULL;
|
|
|
|
long val = strtol(src, &endptr, 0);
|
|
|
|
if (!endptr || endptr == src) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("No opacity specified: %s", src);
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (val > 100 || val < 0) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Opacity %ld invalid: %s", val, src);
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip over spaces
|
|
|
|
while (*endptr && isspace(*endptr))
|
|
|
|
++endptr;
|
|
|
|
if (':' != *endptr) {
|
2018-12-21 01:35:45 +08:00
|
|
|
log_error("Opacity terminator not found: %s", src);
|
2018-08-22 19:58:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
++endptr;
|
|
|
|
|
|
|
|
// Parse pattern
|
|
|
|
// I hope 1-100 is acceptable for (void *)
|
2018-12-22 00:25:28 +08:00
|
|
|
return c2_parse(res, endptr, (void *) val);
|
2018-08-22 19:58:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a pattern to a condition linked list.
|
|
|
|
*/
|
|
|
|
bool
|
2018-12-22 00:25:28 +08:00
|
|
|
condlst_add(c2_lptr_t **pcondlst, const char *pattern) {
|
2018-08-22 19:58:49 +08:00
|
|
|
if (!pattern)
|
|
|
|
return false;
|
|
|
|
|
2018-12-22 00:25:28 +08:00
|
|
|
if (!c2_parse(pcondlst, pattern, NULL))
|
2018-08-22 19:58:49 +08:00
|
|
|
exit(1);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2018-12-05 00:28:19 +08:00
|
|
|
|
2018-12-25 02:28:00 +08:00
|
|
|
void set_default_winopts(options_t *opt, win_option_mask_t *mask, bool shadow_enable, bool fading_enable) {
|
|
|
|
// Apply default wintype options.
|
|
|
|
if (!mask[WINTYPE_DESKTOP].shadow) {
|
|
|
|
// Desktop windows are always drawn without shadow by default.
|
|
|
|
mask[WINTYPE_DESKTOP].shadow = true;
|
2018-12-22 00:25:28 +08:00
|
|
|
opt->wintype_option[WINTYPE_DESKTOP].shadow = false;
|
2018-12-05 00:28:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Focused/unfocused state only apply to a few window types, all other windows
|
|
|
|
// are always considered focused.
|
|
|
|
const wintype_t nofocus_type[] =
|
|
|
|
{ WINTYPE_UNKNOWN, WINTYPE_NORMAL, WINTYPE_UTILITY };
|
|
|
|
for (unsigned long i = 0; i < ARR_SIZE(nofocus_type); i++) {
|
2018-12-25 02:28:00 +08:00
|
|
|
if (!mask[nofocus_type[i]].focus) {
|
|
|
|
mask[nofocus_type[i]].focus = true;
|
2018-12-22 00:25:28 +08:00
|
|
|
opt->wintype_option[nofocus_type[i]].focus = false;
|
2018-12-05 00:28:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (unsigned long i = 0; i < NUM_WINTYPES; i++) {
|
2018-12-25 02:28:00 +08:00
|
|
|
if (!mask[i].shadow) {
|
|
|
|
mask[i].shadow = true;
|
|
|
|
opt->wintype_option[i].shadow = shadow_enable;
|
|
|
|
}
|
|
|
|
if (!mask[i].fade) {
|
|
|
|
mask[i].fade = true;
|
|
|
|
opt->wintype_option[i].fade = fading_enable;
|
|
|
|
}
|
|
|
|
if (!mask[i].focus) {
|
|
|
|
mask[i].focus = true;
|
2018-12-22 00:25:28 +08:00
|
|
|
opt->wintype_option[i].focus = true;
|
2018-12-05 00:28:19 +08:00
|
|
|
}
|
2018-12-25 02:28:00 +08:00
|
|
|
if (!mask[i].full_shadow) {
|
|
|
|
mask[i].full_shadow = true;
|
2018-12-22 00:25:28 +08:00
|
|
|
opt->wintype_option[i].full_shadow = false;
|
2018-12-05 00:28:19 +08:00
|
|
|
}
|
2018-12-25 02:28:00 +08:00
|
|
|
if (!mask[i].redir_ignore) {
|
|
|
|
mask[i].redir_ignore = true;
|
2018-12-22 00:25:28 +08:00
|
|
|
opt->wintype_option[i].redir_ignore = false;
|
2018-12-04 23:30:07 +08:00
|
|
|
}
|
2018-12-25 02:28:00 +08:00
|
|
|
if (!mask[i].opacity) {
|
|
|
|
mask[i].opacity = true;
|
2018-12-05 00:28:19 +08:00
|
|
|
// Opacity is not set to a concrete number here because the opacity logic
|
|
|
|
// is complicated, and needs an "unset" state
|
2018-12-22 00:25:28 +08:00
|
|
|
opt->wintype_option[i].opacity = NAN;
|
2018-12-05 00:28:19 +08:00
|
|
|
}
|
|
|
|
}
|
2018-12-25 02:28:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
char *parse_config(options_t *opt, const char *config_file,
|
|
|
|
bool *shadow_enable, bool *fading_enable, bool *hasneg,
|
|
|
|
win_option_mask_t *winopt_mask) {
|
|
|
|
char *ret = NULL;
|
|
|
|
#ifdef CONFIG_LIBCONFIG
|
|
|
|
ret = parse_config_libconfig(opt, config_file, shadow_enable, fading_enable,
|
|
|
|
hasneg, winopt_mask);
|
|
|
|
#endif
|
2018-12-22 00:25:28 +08:00
|
|
|
return ret;
|
2018-12-05 00:28:19 +08:00
|
|
|
}
|