Merge branch 'next'

This commit is contained in:
Yuxuan Shui 2019-03-23 14:22:46 +00:00
commit 3bf86b3e7e
No known key found for this signature in database
GPG Key ID: 37C999F617EA1A47
58 changed files with 11475 additions and 11928 deletions

View File

@ -43,57 +43,46 @@ jobs:
<<: *defaults
steps:
- build:
build-config: -Dbuild_docs=true -Dnew_backends=true
build-config: -Dbuild_docs=true
minimal:
<<: *defaults
steps:
- build:
build-config: -Dopengl=false -Ddbus=false -Dregex=false -Dconfig_file=false -Dnew_backends=true
build-config: -Dopengl=false -Ddbus=false -Dregex=false -Dconfig_file=false
nogl:
<<: *defaults
steps:
- build:
build-config: -Dopengl=false -Dnew_backends=true
build-config: -Dopengl=false
noregex:
<<: *defaults
steps:
- build:
build-config: -Dregex=false -Dnew_backends=true
build-config: -Dregex=false
clang_basic:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config: -Dnew_backends=true
build-config:
clang_minimal:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config: -Dopengl=false -Ddbus=false -Dregex=false -Dconfig_file=false -Dnew_backends=true
build-config: -Dopengl=false -Ddbus=false -Dregex=false -Dconfig_file=false
clang_nogl:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config: -Dopengl=false -Dnew_backends=true
build-config: -Dopengl=false
clang_noregex:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config: -Dregex=false -Dnew_backends=true
clang_basic_nonew:
<<: *defaults
steps:
- build:
cc: clang-6.0
build-config:
basic_nonew:
<<: *defaults
steps:
- build:
build-config:
build-config: -Dregex=false
workflows:
all_builds:
@ -104,8 +93,6 @@ workflows:
- clang_minimal
- nogl
- clang_nogl
- clang_basic_nonew
- basic_nonew
# - test-xvfb
# vim: set sw=2 ts=8 et:

View File

@ -22,8 +22,8 @@ PenaltyReturnTypeOnItsOwnLine: 0
PenaltyBreakAssignment: 0
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 1
PenaltyBreakString: 5
PenaltyExcessCharacter: 1
PenaltyBreakString: 36
PenaltyExcessCharacter: 3
PenaltyBreakFirstLessLess: 0
PenaltyBreakTemplateDeclaration: 0
BreakBeforeBinaryOperators: None

View File

@ -1,5 +1,5 @@
root = true
[*.{c,h}]
indent_style = space
indent_size = 2
indent_style = tab
indent_size = 8
max_line_length = 90

View File

@ -1,6 +1,7 @@
Sorted in alphabetical order
Adam Jackson <ajax@nwnk.net>
Antonin Décimoantonin.decimo@gmail.com>
Avi-D-coder <avi.the.coder@gmail.com>
Brottweiler <tibell.christoffer@gmail.com>
Carl Worth <cworth@cworth.org>

View File

@ -1674,7 +1674,7 @@ UML_LOOK = NO
# the class node. If there are many fields or methods and many nodes the
# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
# threshold limits the number of items for each type to make the size more
# managable. Set this to 0 for no limit. Note that the threshold may be
# manageable. Set this to 0 for no limit. Note that the threshold may be
# exceeded by 50% before the limit is enforced.
UML_LIMIT_NUM_FIELDS = 10

View File

@ -44,7 +44,7 @@ Assuming you already have all the usual building tools installed (e.g. gcc, meso
To build the documents, you need `asciidoc`
### How to build
### To build
```bash
$ meson --buildtype=release . build
@ -53,6 +53,14 @@ $ ninja -C build
Built binary can be found in `build/src`
### To install
``` bash
$ ninja -C build install
```
Default install prefix is `/usr/local`, you can change it with `meson configure -Dprefix=<path> build`
## How to Contribute
### Code

View File

@ -50,7 +50,7 @@ for v in "$@"; do
done
# This takes into account the fact that getopts stops on
# any argument it doesn't recongize or errors on. This
# any argument it doesn't recognize or errors on. This
# allows for things like `compton-trans -5` as well
# as `compton-trans -c +5 -s` (contrived example).
while test $# -gt 0; do

View File

@ -56,7 +56,7 @@ mark-ovredir-focused = true;
detect-rounded-corners = true;
detect-client-opacity = true;
refresh-rate = 0;
vsync = "none";
vsync = true;
# sw-opti = true;
# unredir-if-possible = true;
# unredir-if-possible-delay = 5000;
@ -70,10 +70,8 @@ invert-color-include = [ ];
# GLX backend
# glx-no-stencil = true;
# glx-no-rebind-pixmap = true;
glx-swap-method = "undefined";
# glx-use-gpushader4 = true;
# xrender-sync = true;
# xrender-sync-fence = true;
use-damage = true;
# Window type settings
wintypes:

View File

@ -50,7 +50,7 @@ OPTIONS
Enabled client-side shadows on windows. Note desktop windows (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow, unless explicitly requested using the wintypes option.
*-C*, *--no-dock-shadow*::
Avoid drawing shadows on dock/panel windows.
Avoid drawing shadows on dock/panel windows. This option is deprecated, you should use the *wintypes* option in your config file instead.
*-f*, *--fading*::
Fade windows in/out when opening/closing and when opacity changes, unless *--no-fading-openclose* is used.
@ -65,7 +65,7 @@ OPTIONS
Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default)
*-G*, *--no-dnd-shadow*::
Don't draw shadows on drag-and-drop windows.
Don't draw shadows on drag-and-drop windows. This option is deprecated, you should use the *wintypes* option in your config file instead.
*-b*, *--daemon*::
Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers.
@ -74,7 +74,10 @@ OPTIONS
Set the log level. Possible values are "TRACE", "DEBUG", "INFO", "WARN", "ERROR", in increasing level of importance. Case doesn't matter.
*--log-file*::
Set the log file. If *--log-file* is never specified, logs will be written to stderr. Otherwise, logs will to writeen to the given file, though some of the early logs might still be written to the stderr. When setting this option from the config file, it is recommended to use an absolute path.
Set the log file. If *--log-file* is never specified, logs will be written to stderr. Otherwise, logs will to written to the given file, though some of the early logs might still be written to the stderr. When setting this option from the config file, it is recommended to use an absolute path.
*--experimental-backends*::
Use the new, reimplemented version of the backends. The new backends are HIGHLY UNSTABLE at this point, you have been warned. This option is not available in the config file.
*--show-all-xerrors*::
Show all X errors (for debugging).
@ -127,28 +130,8 @@ OPTIONS
*--refresh-rate* 'REFRESH_RATE'::
Specify refresh rate of the screen. If not specified or 0, compton will try detecting this with X RandR extension.
*--vsync* 'VSYNC_METHOD'::
Set VSync method. VSync methods currently available:
+
--
* 'none': No VSync
* 'drm': VSync with 'DRM_IOCTL_WAIT_VBLANK'. May only work on some (DRI-based) drivers.
* 'opengl': Try to VSync with 'SGI_video_sync' OpenGL extension. Only work on some drivers.
* 'opengl-oml': Try to VSync with 'OML_sync_control' OpenGL extension. Only work on some drivers.
* 'opengl-swc': Try to VSync with 'MESA_swap_control' or 'SGI_swap_control' (in order of preference) OpenGL extension. Works only with GLX backend. Known to be most effective on many drivers. Does not guarantee to control paint timing.
* 'opengl-mswc': Deprecated, use 'opengl-swc' instead.
(Note some VSync methods may not be enabled at compile time.)
--
*--vsync-aggressive*::
Attempt to send painting request before VBlank and do XFlush() during VBlank. Reported to work pretty terribly. This switch may be lifted out at any moment.
*--dbe*::
Enable DBE painting mode, intended to use with VSync to (hopefully) eliminate tearing. Reported to have no effect, though.
*--paint-on-overlay*::
Painting on X Composite overlay window instead of on root window.
*--vsync*::
Enable VSync.
*--sw-opti*::
Limit compton to repaint at most once every 1 / 'refresh_rate' second to boost performance. This should not be used with *--vsync* drm/opengl/opengl-oml as they essentially does *--sw-opti*'s job already, unless you wish to specify a lower refresh rate than the actual value.
@ -202,7 +185,11 @@ OPTIONS
WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...
----
+
The element in the center must not be included, it will be forever 1.0 or changing based on opacity, depending on whether you have `--blur-background-fixed`. Yet the automatic adjustment of blur factor may not work well with a custom blur kernel.
In other words, the matrix is formatted as a list of comma separated numbers. The first two numbers must be integers, which specify the width and height of the matrix. They must be odd numbers. Then, the following 'width * height - 1' numbers specifies the numbers in the matrix, row by row, excluding the center element.
+
The elements are finite floating point numbers. The decimal pointer has to be '.' (a period), and scientific notation is not supported.
+
The element in the center will either be 1.0 or changing based on opacity, depending on whether you have `--blur-background-fixed`. Yet the automatic adjustment of blur factor may not work well with a custom blur kernel.
+
A 7x7 Gaussian blur kernel (sigma = 0.84089642) looks like:
+
@ -216,7 +203,7 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box
Exclude conditions for background blur.
*--resize-damage* 'INTEGER'::
Resize damaged region by a specific number of pixels. A positive value enlarges it while a negative one shrinks it. If the value is positive, those additional pixels will not be actually painted to screen, only used in blur calculation, and such. (Due to technical limitations, with *--dbe* or *--glx-swap-method*, those pixels will still be incorrectly painted to screen.) Primarily used to fix the line corruption issues of blur, in which case you should use the blur radius value here (e.g. with a 3x3 kernel, you should use *--resize-damage* 1, with a 5x5 one you use *--resize-damage* 2, and so on). May or may not work with `--glx-no-stencil`. Shrinking doesn't function correctly.
Resize damaged region by a specific number of pixels. A positive value enlarges it while a negative one shrinks it. If the value is positive, those additional pixels will not be actually painted to screen, only used in blur calculation, and such. (Due to technical limitations, with *--use-damage*, those pixels will still be incorrectly painted to screen.) Primarily used to fix the line corruption issues of blur, in which case you should use the blur radius value here (e.g. with a 3x3 kernel, you should use *--resize-damage* 1, with a 5x5 one you use *--resize-damage* 2, and so on). May or may not work with `--glx-no-stencil`. Shrinking doesn't function correctly.
*--invert-color-include* 'CONDITION'::
Specify a list of conditions of windows that should be painted with inverted color. Resource-hogging, and is not well tested.
@ -245,11 +232,8 @@ May also be one of the predefined kernels: `3x3box` (default), `5x5box`, `7x7box
*--glx-no-rebind-pixmap*::
GLX backend: Avoid rebinding pixmap on window damage. Probably could improve performance on rapid window content changes, but is known to break things on some drivers (LLVMpipe, xf86-video-intel, etc.). Recommended if it works.
*--glx-swap-method* undefined/exchange/copy/3/4/5/6/buffer-age::
GLX backend: GLX buffer swap method we assume. Could be `undefined` (0), `copy` (1), `exchange` (2), 3-6, or `buffer-age` (-1). `undefined` is the slowest and the safest, and the default value. `copy` is fastest, but may fail on some drivers, 2-6 are gradually slower but safer (6 is still faster than 0). Usually, double buffer means 2, triple buffer means 3. `buffer-age` means auto-detect using 'GLX_EXT_buffer_age', supported by some drivers. Partially breaks `--resize-damage`. Defaults to `undefined`.
*--glx-use-gpushader4*::
GLX backend: Use 'GL_EXT_gpu_shader4' for some optimization on blur GLSL code. My tests on GTX 670 show no noticeable effect.
*-use-damage*::
Use the damage information to limit rendering to parts of the screen that has actually changed. Potentially improves the performance.
*--xrender-sync-fence*::
Use X Sync fence to sync clients' draw calls, to make sure all draw calls are finished before compton starts drawing. Needed on nvidia-drivers with GLX backend for some users.
@ -357,7 +341,9 @@ CONFIGURATION FILES
-------------------
compton could read from a configuration file if libconfig support is compiled in. If *--config* is not used, compton will seek for a configuration file in `$XDG_CONFIG_HOME/compton.conf` (`~/.config/compton.conf`, usually), then `~/.compton.conf`, then `compton.conf` under `$XDG_CONFIG_DIRS` (often `/etc/xdg/compton.conf`).
compton uses general libconfig configuration file format. A sample configuration file is available as `compton.sample.conf` in the source tree. Most commandline switches each could be replaced with an option in configuration file, thus documented above. Window-type-specific settings are exposed only in configuration file and has the following format:
compton uses general libconfig configuration file format. A sample configuration file is available as `compton.sample.conf` in the source tree. Most of commandline switches can be used as options in configuration file as well. For example, *--vsync* option documented above can be set in the configuration file using `vsync = `. Command line options will always overwrite the settings in the configuration file.
Window-type-specific settings are exposed only in configuration file and has the following format:
------------
wintypes:
@ -430,10 +416,10 @@ $ compton -c --shadow-red 1 --shadow-green 1 --shadow-blue 1
$ compton -c --shadow-exclude 'class_g = "wbar"'
------------
* Enable OpenGL SGI_swap_control VSync with GLX backend:
* Enable VSync with GLX backend:
+
------------
$ compton --backend glx --vsync opengl-swc
$ compton --backend glx --vsync
------------
BUGS

View File

@ -1,4 +1,4 @@
project('compton', 'c', version: '5',
project('compton', 'c', version: '6',
default_options: ['c_std=c11'])
cc = meson.get_compiler('c')
@ -46,7 +46,8 @@ endif
add_global_arguments('-D_GNU_SOURCE', language: 'c')
warns = [ 'all', 'extra', 'no-unused-parameter', 'nonnull', 'shadow', 'implicit-fallthrough' ]
warns = [ 'all', 'extra', 'no-unused-parameter', 'nonnull', 'shadow',
'implicit-fallthrough', 'no-unknown-warning-option', 'no-missing-braces' ]
foreach w : warns
if cc.has_argument('-W'+w)
add_global_arguments('-W'+w, language: 'c')

View File

@ -1,17 +1,14 @@
option('sanitize', type: 'boolean', value: false, description: 'Compile compton with sanitizers')
option('xinerama', type: 'boolean', value: true, description: 'Enable XINERAMA support')
option('config_file', type: 'boolean', value: true, description: 'Enable config file support')
option('regex', type: 'boolean', value: true, description: 'Enable regex support in window conditions')
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('dbus', type: 'boolean', value: true, description: 'Enable suport for D-Bus remote control')
option('dbus', type: 'boolean', value: true, description: 'Enable support 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')
option('modularize', type: 'boolean', value: false, description: 'Build with clang\'s module system')

View File

@ -1,47 +1,95 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <xcb/xcb.h>
#include "backend.h"
#include "config.h"
#include "win.h"
#include "region.h"
#include "backend/backend.h"
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "log.h"
#include "region.h"
#include "win.h"
backend_info_t *backend_list[NUM_BKEND] = {[BKEND_XRENDER] = &xrender_backend};
extern struct backend_operations xrender_ops;
#ifdef CONFIG_OPENGL
extern struct backend_operations glx_ops;
#endif
bool default_is_win_transparent(void *backend_data, win *w, void *win_data) {
return w->mode != WMODE_SOLID;
}
struct backend_operations *backend_list[NUM_BKEND] = {
[BKEND_XRENDER] = &xrender_ops,
#ifdef CONFIG_OPENGL
[BKEND_GLX] = &glx_ops,
#endif
};
bool default_is_frame_transparent(void *backend_data, win *w, void *win_data) {
return w->frame_opacity != 1;
/**
* @param all_damage if true ignore damage and repaint the whole screen
*/
region_t get_damage(session_t *ps, bool all_damage) {
region_t region;
auto buffer_age_fn = ps->backend_data->ops->buffer_age;
int buffer_age = buffer_age_fn ? buffer_age_fn(ps->backend_data) : -1;
if (all_damage) {
buffer_age = -1;
}
pixman_region32_init(&region);
if (buffer_age == -1 || buffer_age > ps->ndamage) {
pixman_region32_copy(&region, &ps->screen_reg);
} else {
for (int i = 0; i < buffer_age; i++) {
const int curr = ((ps->damage - ps->damage_ring) + i) % ps->ndamage;
pixman_region32_union(&region, &region, &ps->damage_ring[curr]);
}
pixman_region32_intersect(&region, &region, &ps->screen_reg);
}
return region;
}
/// paint all windows
void paint_all_new(session_t *ps, region_t *region, win *const t) {
auto bi = backend_list[ps->o.backend];
assert(bi);
void paint_all_new(session_t *ps, win *const t, bool ignore_damage) {
// All painting will be limited to the damage, if _some_ of
// the paints bleed out of the damage region, it will destroy
// part of the image we want to reuse
region_t reg_damage;
if (!ignore_damage) {
reg_damage = get_damage(ps, ps->o.monitor_repaint || !ps->o.use_damage);
} else {
pixman_region32_init(&reg_damage);
pixman_region32_copy(&reg_damage, &ps->screen_reg);
}
if (!pixman_region32_not_empty(&reg_damage)) {
pixman_region32_fini(&reg_damage);
return;
}
#ifdef DEBUG_REPAINT
static struct timespec last_paint = {0};
#endif
// Ignore out-of-screen damages
pixman_region32_intersect(region, region, &ps->screen_reg);
region_t reg_tmp, *reg_paint;
pixman_region32_init(&reg_tmp);
// A hint to backend, the region that will be visible on screen
// backend can optimize based on this info
region_t reg_visible;
pixman_region32_init(&reg_visible);
pixman_region32_copy(&reg_visible, &ps->screen_reg);
if (t) {
// Calculate the region upon which the root window (wallpaper) is to be
// painted based on the ignore region of the lowest window, if available
pixman_region32_subtract(&reg_tmp, region, t->reg_ignore);
reg_paint = &reg_tmp;
} else {
reg_paint = region;
pixman_region32_subtract(&reg_visible, &reg_visible, t->reg_ignore);
}
if (bi->prepare)
bi->prepare(ps->backend_data, ps, reg_paint);
// TODO Bind root pixmap
if (ps->backend_data->ops->prepare) {
ps->backend_data->ops->prepare(ps->backend_data, &reg_damage);
}
if (ps->root_image) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0,
&reg_damage, &reg_visible);
}
// Windows are sorted from bottom to top
// Each window has a reg_ignore, which is the region obscured by all the windows
@ -49,58 +97,180 @@ void paint_all_new(session_t *ps, region_t *region, win *const t) {
//
// Whether this is beneficial is to be determined XXX
for (win *w = t; w; w = w->prev_trans) {
// Calculate the region based on the reg_ignore of the next (higher)
// window and the bounding region
// XXX XXX
pixman_region32_subtract(&reg_tmp, region, w->reg_ignore);
pixman_region32_subtract(&reg_visible, &ps->screen_reg, w->reg_ignore);
if (pixman_region32_not_empty(&reg_tmp)) {
// Render window content
// XXX do this in preprocess?
bi->render_win(ps->backend_data, ps, w, w->win_data, &reg_tmp);
// The bounding shape of the window, in global/target coordinates
// reminder: bounding shape contains the WM frame
auto reg_bound = win_get_bounding_shape_global_by_val(w);
// Blur window background
bool win_transparent =
bi->is_win_transparent(ps->backend_data, w, w->win_data);
bool frame_transparent =
bi->is_frame_transparent(ps->backend_data, w, w->win_data);
if (w->blur_background &&
(win_transparent ||
(ps->o.blur_background_frame && frame_transparent))) {
// Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is.
region_t reg_blur = win_get_bounding_shape_global_by_val(w);
if (win_is_solid(ps, w)) {
region_t reg_noframe;
pixman_region32_init(&reg_noframe);
win_get_region_noframe_local(w, &reg_noframe);
pixman_region32_translate(&reg_noframe, w->g.x,
w->g.y);
pixman_region32_subtract(&reg_blur, &reg_blur,
&reg_noframe);
pixman_region32_fini(&reg_noframe);
}
bi->blur(ps->backend_data, ps,
(double)w->opacity / OPAQUE, &reg_blur);
pixman_region32_fini(&reg_blur);
// Draw shadow on target
if (w->shadow) {
// Clip region for the shadow
// reg_shadow \in reg_damage
auto reg_shadow = win_extents_by_val(w);
pixman_region32_intersect(&reg_shadow, &reg_shadow, &reg_damage);
if (!ps->o.wintype_option[w->window_type].full_shadow) {
pixman_region32_subtract(&reg_shadow, &reg_shadow, &reg_bound);
}
// Draw window on target
bi->compose(ps->backend_data, ps, w, w->win_data, w->g.x, w->g.y,
&reg_tmp);
// Mask out the region we don't want shadow on
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
pixman_region32_subtract(&reg_shadow, &reg_shadow,
&ps->shadow_exclude_reg);
}
if (bi->finish_render_win)
bi->finish_render_win(ps->backend_data, ps, w, w->win_data);
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(
&reg_shadow, &reg_shadow,
&ps->xinerama_scr_regs[w->xinerama_scr]);
}
assert(w->shadow_image);
if (w->opacity == 1) {
ps->backend_data->ops->compose(
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
} else {
auto new_img = ps->backend_data->ops->copy(
ps->backend_data, w->shadow_image, &reg_visible);
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible, (double[]){w->opacity});
ps->backend_data->ops->compose(
ps->backend_data, new_img, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
}
pixman_region32_fini(&reg_shadow);
}
// The clip region for the current window, in global/target coordinates
// reg_paint \in reg_damage
region_t reg_paint;
pixman_region32_init(&reg_paint);
pixman_region32_intersect(&reg_paint, &reg_bound, &reg_damage);
// Blur window background
bool win_transparent = ps->backend_data->ops->is_image_transparent(
ps->backend_data, w->win_image);
bool frame_transparent = w->frame_opacity != 1;
if (w->blur_background &&
(win_transparent || (ps->o.blur_background_frame && frame_transparent))) {
// Minimize the region we try to blur, if the window
// itself is not opaque, only the frame is.
// TODO resize blur region to fix black line artifact
if (!win_is_solid(ps, w)) {
// We need to blur the bounding shape of the window
// (reg_paint = reg_bound \cap reg_damage)
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_paint, &reg_visible);
} else if (frame_transparent && ps->o.blur_background_frame) {
// Window itself is solid, we only need to blur the frame
// region
auto reg_blur = win_get_region_frame_local_by_val(w);
pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
// make sure reg_blur \in reg_damage
pixman_region32_intersect(&reg_blur, &reg_blur, &reg_damage);
ps->backend_data->ops->blur(ps->backend_data, w->opacity,
&reg_blur, &reg_visible);
pixman_region32_fini(&reg_blur);
}
}
// Draw window on target
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image, w->g.x,
w->g.y, &reg_paint, &reg_visible);
} else {
// For window image processing, we don't need to limit the process
// region to damage, since the window image data is independent
// from the target image data, which we want to protect.
// The bounding shape, in window local coordinates
region_t reg_bound_local;
pixman_region32_init(&reg_bound_local);
pixman_region32_copy(&reg_bound_local, &reg_bound);
pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
// The visible region, in window local coordinates
// Although we don't limit process region to damage, we provide
// that info in reg_visible as a hint. Since window image data
// outside of the damage region won't be painted onto target
region_t reg_visible_local;
pixman_region32_init(&reg_visible_local);
pixman_region32_intersect(&reg_visible_local, &reg_visible, &reg_damage);
pixman_region32_translate(&reg_visible_local, -w->g.x, -w->g.y);
// Data outside of the bounding shape won't be visible, but it is
// not necessary to limit the image operations to the bounding
// shape yet. So pass that as the visible region, not the clip
// region.
pixman_region32_intersect(&reg_visible_local, &reg_visible_local,
&reg_bound_local);
// A region covers the entire window
auto new_img = ps->backend_data->ops->copy(
ps->backend_data, w->win_image, &reg_visible_local);
if (w->invert_color) {
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_INVERT_COLOR_ALL, new_img,
NULL, &reg_visible_local, NULL);
}
if (w->dim) {
double dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed) {
dim_opacity *= w->opacity;
}
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_DIM_ALL, new_img, NULL,
&reg_visible_local, (double[]){dim_opacity});
}
if (w->frame_opacity != 1) {
auto reg_frame = win_get_region_frame_local_by_val(w);
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame,
&reg_visible_local, (double[]){w->frame_opacity});
pixman_region32_fini(&reg_frame);
}
if (w->opacity != 1) {
ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible_local, (double[]){w->opacity});
}
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x,
w->g.y, &reg_paint, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
}
pixman_region32_fini(&reg_bound);
pixman_region32_fini(&reg_paint);
}
pixman_region32_fini(&reg_damage);
if (ps->o.monitor_repaint) {
reg_damage = get_damage(ps, false);
ps->backend_data->ops->fill(ps->backend_data, 0.5, 0, 0, 0.5, &reg_damage);
pixman_region32_fini(&reg_damage);
}
// Free up all temporary regions
pixman_region32_fini(&reg_tmp);
// Move the head of the damage ring
ps->damage = ps->damage - 1;
if (ps->damage < ps->damage_ring) {
ps->damage = ps->damage_ring + ps->ndamage - 1;
}
pixman_region32_clear(ps->damage);
if (bi->present) {
if (ps->backend_data->ops->present) {
// Present the rendered scene
// Vsync is done here
bi->present(ps->backend_data, ps);
ps->backend_data->ops->present(ps->backend_data);
}
#ifdef DEBUG_REPAINT

View File

@ -3,12 +3,43 @@
#pragma once
#include "region.h"
#include <stdbool.h>
#include "compiler.h"
#include "kernel.h"
#include "region.h"
#include "x.h"
typedef struct session session_t;
typedef struct win win;
typedef struct backend_info {
struct backend_operations;
typedef struct backend_base {
struct backend_operations *ops;
xcb_connection_t *c;
xcb_window_t root;
// ...
} backend_t;
enum image_operations {
// Invert the color of the entire image, `reg_op` ignored
IMAGE_OP_INVERT_COLOR_ALL,
// Dim the entire image, argument is the percentage. `reg_op` ignored
IMAGE_OP_DIM_ALL,
// Multiply the alpha channel by the argument
IMAGE_OP_APPLY_ALPHA,
// Same as APPLY_ALPHA, but `reg_op` is ignored and the operation applies to the
// full image
IMAGE_OP_APPLY_ALPHA_ALL,
// Change the effective size of the image, without touching the backing image
// itself. When the image is used, the backing image should be tiled to fill its
// effective size. `reg_op` and `reg_visibile` is ignored. `arg` is two integers,
// width and height, in that order.
IMAGE_OP_RESIZE_TILE,
};
struct backend_operations {
// =========== Initialization ===========
@ -16,141 +47,144 @@ typedef struct backend_info {
/// 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)));
/// TODO make the target window a parameter
backend_t *(*init)(session_t *) attr_nonnull(1);
void (*deinit)(backend_t *backend_data) attr_nonnull(1);
/// 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);
///
/// Optional, not yet used
void (*pause)(backend_t *backend_data, session_t *ps);
/// Called before rendering is resumed
void (*resume)(void *backend_data, session_t *ps);
///
/// Optional, not yet used
void (*resume)(backend_t *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
/// the existing image data returned by this backend should
/// remain valid.
///
/// Optional
void *(*root_change)(void *backend_data, session_t *ps);
/// Called when vsync is toggled after initialization. If vsync is enabled when init()
/// is called, these function won't be called
void (*vsync_start)(void *backend_data, session_t *ps);
void (*vsync_stop)(void *backend_data, session_t *ps);
void *(*root_change)(backend_t *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. Otherwise, it should always
/// be non-NULL.
/// Called before when a new frame starts.
///
/// Optional
void (*present)(void *backend_data, session_t *ps) __attribute__((nonnull(1, 2)));
void (*prepare)(backend_t *backend_data, const region_t *reg_damage);
/**
* Render the content of a window into an opaque
* data structure. Dimming, shadow and color inversion is handled
* here.
* Paint the content of an image onto the (possibly buffered)
* target picture.
*
* 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.
* @param backend_data the backend data
* @param image_data the image to paint
* @param dst_x, dst_y the top left corner of the image in the target
* @param reg_paint the clip region, in target coordinates
* @param reg_visibile the visible region, in target coordinates
*/
void (*render_win)(void *backend_data, session_t *ps, win *w, void *win_data,
const region_t *reg_paint);
void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible);
/// Free resource allocated for rendering. After this function is
/// called, compose() won't be called before render_win is called
/// another time.
/// Fill rectangle of target, mostly for debug purposes, optional.
void (*fill)(backend_t *backend_data, double r, double g, double b, double a,
const region_t *clip);
/// Blur a given region of the target.
bool (*blur)(backend_t *backend_data, double opacity, const region_t *reg_blur,
const region_t *reg_visible) attr_nonnull(1, 3, 4);
/// Present the back buffer onto the screen.
///
/// Optional
void (*finish_render_win)(void *backend_data, session_t *ps, win *w, void *win_data);
/// Optional if the screen is not buffered
void (*present)(backend_t *backend_data) attr_nonnull(1);
/**
* Bind a X pixmap to the backend's internal image data structure.
*
* @param backend_data backend data
* @param pixmap X pixmap to bind
* @param fmt information of the pixmap's visual
* @param owned whether the ownership of the pixmap is transfered to the backend
* @return backend internal data structure bound with this pixmap
*/
void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned);
/// Create a shadow image based on the parameters
/// Default implementation: default_backend_render_shadow
void *(*render_shadow)(backend_t *backend_data, int width, int height,
const conv *kernel, double r, double g, double b, double a);
// ============ Resource management ===========
// XXX Thoughts: calling release_win and prepare_win for every config notify
// XXX Thoughts: calling release_image and render_* 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 as for now. We need to reconsider this.
/// 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)));
/// Free resources associated with an image data structure
void (*release_image)(backend_t *backend_data, void *img_data)
attr_nonnull(1, 2);
// =========== Query ===========
/// Return if a window has transparent content. Guaranteed to only
/// be called after render_win is called.
/// Return if image is not completely opaque.
///
/// This function is needed because some backend might change the content of the
/// window (e.g. when using a custom shader with the glx backend), so we only now
/// the transparency after the window is rendered
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.
///
/// Same logic as is_win_transparent applies here.
bool (*is_frame_transparent)(void *backend_data, win *w, void *win_data)
__attribute__((nonnull(1, 2)));
/// window (e.g. when using a custom shader with the glx backend), so only the
/// backend knows if an image is transparent.
bool (*is_image_transparent)(backend_t *backend_data, void *image_data)
attr_nonnull(1, 2);
/// Get the age of the buffer content we are currently rendering ontop
/// of. The buffer that has just been `present`ed has a buffer age of 1.
/// Everytime `present` is called, buffers get older. Return -1 if the
/// buffer is empty.
int (*buffer_age)(void *backend_data, session_t *);
///
/// Optional
int (*buffer_age)(backend_t *backend_data);
/// The maximum number buffer_age might return.
int max_buffer_age;
// =========== Post-processing ============
/**
* Manipulate an image
*
* @param backend_data backend data
* @param op the operation to perform
* @param image_data an image data structure returned by the backend
* @param reg_op the clip region, define the part of the image to be
* operated on.
* @param reg_visible define the part of the image that will eventually
* be visible on screen. this is a hint to the backend
* for optimization purposes.
* @param args extra arguments, operation specific
* @return a new image data structure containing the result
*/
bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
const region_t *reg_op, const region_t *reg_visible, void *args);
/// Create another instance of the `image_data`. All `image_op` calls on the
/// returned image should not affect the original image
void *(*copy)(backend_t *base, const void *image_data, const region_t *reg_visible);
// =========== Hooks ============
/// Let the backend hook into the event handling queue
} backend_info_t;
};
extern backend_info_t xrender_backend;
extern backend_info_t glx_backend;
extern backend_info_t *backend_list[];
typedef backend_t *(*backend_init_fn)(session_t *ps) attr_nonnull(1);
extern struct backend_operations *backend_list[];
bool default_is_win_transparent(void *, win *, void *);
bool default_is_frame_transparent(void *, win *, void *);
void paint_all_new(session_t *ps, region_t *region, win *const t) attr_nonnull(1);
void paint_all_new(session_t *ps, win *const t, bool ignore_damage) attr_nonnull(1);
// vim: set noet sw=8 ts=8 :

View File

@ -1,37 +1,40 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <string.h>
#include <xcb/xcb_image.h>
#include <xcb/render.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_renderutil.h>
#include "backend.h"
#include "backend_common.h"
#include "kernel.h"
#include "backend/backend.h"
#include "backend/backend_common.h"
#include "common.h"
#include "kernel.h"
#include "log.h"
#include "win.h"
#include "x.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_render_picture_t solid_picture(xcb_connection_t *c, xcb_drawable_t d, 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);
pixmap = x_create_pixmap(c, argb ? 32 : 8, d, 1, 1);
if (!pixmap)
return XCB_NONE;
pa.repeat = 1;
picture = x_create_picture_with_standard_and_pixmap(
ps, argb ? XCB_PICT_STANDARD_ARGB_32 : XCB_PICT_STANDARD_A_8, pixmap,
c, 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);
xcb_free_pixmap(c, pixmap);
return XCB_NONE;
}
@ -45,14 +48,14 @@ solid_picture(session_t *ps, bool argb, double a, double r, double g, double b)
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);
xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_SRC, picture, col, 1, &rect);
xcb_free_pixmap(c, pixmap);
return picture;
}
xcb_image_t *make_shadow(xcb_connection_t *c, const conv *kernel,
double opacity, int width, int height) {
xcb_image_t *
make_shadow(xcb_connection_t *c, const conv *kernel, double opacity, int width, int height) {
/*
* We classify shadows into 4 kinds of regions
* r = shadow radius
@ -68,7 +71,10 @@ xcb_image_t *make_shadow(xcb_connection_t *c, const conv *kernel,
*/
xcb_image_t *ximage;
const double *shadow_sum = kernel->rsum;
int d = kernel->size, r = d / 2;
assert(shadow_sum);
// We only support square kernels for shadow
assert(kernel->w == kernel->h);
int d = kernel->w, r = d / 2;
int swidth = width + r * 2, sheight = height + r * 2;
assert(d % 2 == 1);
@ -142,7 +148,7 @@ xcb_image_t *make_shadow(xcb_connection_t *c, const conv *kernel,
// Fill part 3
for (int y = r; y < height + r; y++) {
memset(data + sstride * y + r, 255, width);
memset(data + sstride * y + r, 255 * opacity, width);
}
// Part 1
@ -178,25 +184,23 @@ xcb_image_t *make_shadow(xcb_connection_t *c, const conv *kernel,
/**
* 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) {
bool build_shadow(xcb_connection_t *c, xcb_drawable_t d, double opacity, const int width,
const int height, const conv *kernel, 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 = XCB_NONE, shadow_pixmap_argb = XCB_NONE;
xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE;
xcb_gcontext_t gc = XCB_NONE;
shadow_image =
make_shadow(ps->c, ps->gaussian_map, opacity, width, height);
shadow_image = make_shadow(c, kernel, 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 = x_create_pixmap(c, 8, d, shadow_image->width, shadow_image->height);
shadow_pixmap_argb =
x_create_pixmap(ps, 32, ps->root, shadow_image->width, shadow_image->height);
x_create_pixmap(c, 32, d, shadow_image->width, shadow_image->height);
if (!shadow_pixmap || !shadow_pixmap_argb) {
log_error("Failed to create shadow pixmaps");
@ -204,45 +208,75 @@ bool build_shadow(session_t *ps, double opacity, const int width, const int heig
}
shadow_picture = x_create_picture_with_standard_and_pixmap(
ps, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
c, 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)
c, 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);
gc = xcb_generate_id(c);
xcb_create_gc(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,
xcb_image_put(c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
xcb_render_composite(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_free_gc(c, gc);
xcb_image_destroy(shadow_image);
xcb_free_pixmap(ps->c, shadow_pixmap);
xcb_render_free_picture(ps->c, shadow_picture);
xcb_free_pixmap(c, shadow_pixmap);
xcb_render_free_picture(c, shadow_picture);
return true;
shadow_picture_err:
if (shadow_image)
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);
}
if (shadow_pixmap) {
xcb_free_pixmap(c, shadow_pixmap);
}
if (shadow_pixmap_argb) {
xcb_free_pixmap(c, shadow_pixmap_argb);
}
if (shadow_picture) {
xcb_render_free_picture(c, shadow_picture);
}
if (shadow_picture_argb) {
xcb_render_free_picture(c, shadow_picture_argb);
}
if (gc) {
xcb_free_gc(c, gc);
}
return false;
}
// vim: set noet sw=8 ts=8 :
void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
const conv *kernel, double r, double g, double b, double a) {
xcb_pixmap_t shadow_pixel = solid_picture(backend_data->c, backend_data->root,
true, 1, r, g, b),
shadow = XCB_NONE;
xcb_render_picture_t pict = XCB_NONE;
build_shadow(backend_data->c, backend_data->root, a, width, height, kernel,
shadow_pixel, &shadow, &pict);
auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32);
void *ret = backend_data->ops->bind_pixmap(
backend_data, shadow, x_get_visual_info(backend_data->c, visual), true);
xcb_render_free_picture(backend_data->c, pict);
return ret;
}
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;
}

View File

@ -1,4 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <xcb/render.h>
#include <xcb/xcb_image.h>
@ -10,12 +13,23 @@ typedef struct session session_t;
typedef struct win win;
typedef struct conv conv;
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);
bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, const int width,
const int height, const conv *kernel, 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);
xcb_render_picture_t solid_picture(xcb_connection_t *, xcb_drawable_t, bool argb,
double a, double r, double g, double b);
xcb_image_t *
make_shadow(xcb_connection_t *c, const conv *kernel, double opacity, int width, int height);
/// The default implementation of `is_win_transparent`, it simply looks at win::mode. So
/// this is not suitable for backends that alter the content of windows
bool default_is_win_transparent(void *, win *, void *);
/// The default implementation of `is_frame_transparent`, it uses win::frame_opacity. Same
/// caveat as `default_is_win_transparent` applies.
bool default_is_frame_transparent(void *, win *, void *);
void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
const conv *kernel, double r, double g, double b, double a);

View File

@ -1,14 +1,17 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <GL/gl.h>
#include <GL/glext.h>
#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "kernel.h"
#include "log.h"
#include "region.h"
#include "string_utils.h"
@ -16,7 +19,8 @@
#include "backend/gl/gl_common.h"
struct gl_data {};
#define GLSL(version, ...) "#version " #version "\n" #__VA_ARGS__
#define QUOTE(...) #__VA_ARGS__
GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
log_trace("===\n%s\n===", shader_str);
@ -133,124 +137,117 @@ GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_
return prog;
}
void gl_free_prog_main(session_t *ps, gl_win_shader_t *pprogram) {
static void gl_free_prog_main(gl_win_shader_t *pprogram) {
if (!pprogram)
return;
if (pprogram->prog) {
glDeleteProgram(pprogram->prog);
pprogram->prog = 0;
}
pprogram->unifm_opacity = -1;
pprogram->unifm_invert_color = -1;
pprogram->unifm_tex = -1;
}
/**
* @brief Get tightly packed RGB888 data from GL front buffer.
* Render a region with texture data.
*
* Don't expect any sort of decent performance.
*
* @returns tightly packed RGB888 data of the size of the screen,
* to be freed with `free()`
* @param ptex the texture
* @param dst_x,dst_y the top left corner of region where this texture
* should go. In Xorg coordinate system (important!).
* @param reg_tgt the clip region, also in Xorg coordinate system
* @param reg_visible ignored
*/
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;
}
void gl_compose(backend_t *base, void *image_data, int dst_x, int dst_y,
const region_t *reg_tgt, const region_t *reg_visible) {
/**
* @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) {
gl_texture_t *ptex = image_data;
struct gl_data *gd = (void *)base;
// Until we start to use glClipControl, reg_tgt, dst_x and dst_y and
// in a different coordinate system than the one OpenGL uses.
// OpenGL window coordinate (or NDC) has the origin at the lower left of the
// screen, with y axis pointing up; Xorg has the origin at the upper left of the
// screen, with y axis pointing down. We have to do some coordinate conversion in
// this function
if (!ptex || !ptex->texture) {
log_error("Missing texture.");
return false;
return;
}
// argb = argb || (GLX_TEXTURE_FORMAT_RGBA_EXT ==
// ps->psglx->fbconfigs[ptex->depth]->texture_fmt);
// dst_y is the top coordinate, in OpenGL, it is the upper bound of the y
// coordinate.
dst_y = gd->height - dst_y;
auto dst_y2 = dst_y - ptex->height;
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);
glEnable(GL_TEXTURE_2D);
if (gd->win_shader.prog) {
glUseProgram(gd->win_shader.prog);
if (gd->win_shader.unifm_opacity >= 0) {
glUniform1f(gd->win_shader.unifm_opacity, ptex->opacity);
}
if (gd->win_shader.unifm_invert_color >= 0) {
glUniform1i(gd->win_shader.unifm_invert_color, ptex->color_inverted);
}
if (gd->win_shader.unifm_tex >= 0) {
glUniform1i(gd->win_shader.unifm_tex, 0);
}
if (gd->win_shader.unifm_dim >= 0) {
glUniform1f(gd->win_shader.unifm_dim, ptex->dim);
}
}
// 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);
glBindTexture(GL_TEXTURE_2D, ptex->texture);
if (dual_texture) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(ptex->target, ptex->texture);
glBindTexture(GL_TEXTURE_2D, ptex->texture);
glActiveTexture(GL_TEXTURE0);
}
// Painting
P_PAINTREG_START(crect) {
int nrects;
const rect_t *rects;
rects = pixman_region32_rectangles((region_t *)reg_tgt, &nrects);
glBegin(GL_QUADS);
for (int ri = 0; ri < nrects; ++ri) {
// Y-flip. Note after this, crect.y1 > crect.y2
rect_t crect = rects[ri];
crect.y1 = gd->height - crect.y1;
crect.y2 = gd->height - crect.y2;
// 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;
// (texture_x1, texture_y1), texture coord for the _bottom left_ corner
GLfloat texture_x1 = crect.x1 - dst_x;
GLfloat texture_y1 = crect.y2 - dst_y2;
GLfloat texture_x2 = texture_x1 + crect.x2 - crect.x1;
GLfloat texture_y2 = texture_y1 + crect.y1 - 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;
texture_y1 = ptex->height - texture_y1;
texture_y2 = ptex->height - texture_y2;
}
// GL_TEXTURE_2D coordinates are normalized
// TODO use texelFetch
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.y2;
GLint vx2 = crect.x2;
GLint vy2 = crect.y1;
// log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d",
// ri, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
@ -261,23 +258,22 @@ bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int widt
for (int i = 0; i < 4; i++) {
glTexCoord2f(texture_x[i], texture_y[i]);
glVertex3i(vx[i], vy[i], z);
glVertex3i(vx[i], vy[i], 0);
}
}
P_PAINTREG_END();
glEnd();
// Cleanup
glBindTexture(ptex->target, 0);
glBindTexture(GL_TEXTURE_2D, 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);
glDisable(GL_TEXTURE_2D);
if (dual_texture) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(ptex->target, 0);
glDisable(ptex->target);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
}
@ -285,266 +281,137 @@ bool gl_compose(const gl_texture_t *ptex, int x, int y, int dx, int dy, int widt
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;
return;
}
/**
* 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;
bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur,
const region_t *reg_visible) {
// Remainder: regions are in Xorg coordinates
struct gl_data *gd = (void *)base;
const rect_t *extent = pixman_region32_extents((region_t *)reg_blur);
int width = extent->x2 - extent->x1, height = extent->y2 - extent->y1;
int dst_y = gd->height - extent->y2;
if (width == 0 || height == 0) {
return true;
}
// 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);
int curr = 0;
glReadBuffer(GL_BACK);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]);
// Copy the area to be blurred into tmp buffer
glCopyTexSubImage2D(tex_tgt, 0, 0, 0, dx, dy, width, height);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extent->x1, dst_y, 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) {
for (int i = 0; i < gd->npasses; ++i) {
assert(i < MAX_BLUR_PASS - 1);
const gl_blur_shader_t *curr = &pass[i];
assert(curr->prog);
const gl_blur_shader_t *p = &gd->blur_shader[i];
assert(p->prog);
assert(tex_scr);
glBindTexture(tex_tgt, tex_scr);
assert(gd->blur_texture[curr]);
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[curr]);
if (i < npasses - 1) {
glUseProgram(p->prog);
if (i < gd->npasses - 1) {
// not last pass, draw into framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gd->blur_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});
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, gd->blur_texture[!curr], 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
log_error("Framebuffer attachment failed.");
goto end;
}
glUniform1f(p->unifm_opacity, 1.0);
} 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);
glDrawBuffer(GL_BACK);
glUniform1f(p->unifm_opacity, opacity);
}
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);
glUniform1f(p->unifm_offset_x, 1.0 / gd->width);
glUniform1f(p->unifm_offset_y, 1.0 / gd->height);
// XXX use multiple draw calls is probably going to be slow than
// just simply blur the whole area.
P_PAINTREG_START(crect) {
int nrects;
const rect_t *rect =
pixman_region32_rectangles((region_t *)reg_blur, &nrects);
glBegin(GL_QUADS);
for (int j = 0; j < nrects; j++) {
rect_t crect = rect[j];
// flip y axis, because the regions are in Xorg's coordinates,
// which is y-flipped from OpenGL's.
crect.y1 = gd->height - crect.y1;
crect.y2 = gd->height - crect.y2;
// 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;
GLfloat texture_x1 = (crect.x1 - extent->x1);
GLfloat texture_y1 = (crect.y2 - dst_y);
GLfloat texture_x2 = texture_x1 + (crect.x2 - crect.x1);
GLfloat texture_y2 = texture_y1 + (crect.y1 - crect.y2);
texture_x1 /= gd->width;
texture_x2 /= gd->width;
texture_y1 /= gd->height;
texture_y2 /= gd->height;
// 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) {
GLfloat vx1 = crect.x1 - extent->x1;
GLfloat vy1 = crect.y2 - dst_y;
if (i == gd->npasses - 1) {
// For last pass, we are drawing back to source, so we
// don't need to map
vx1 = crect.x1;
vy1 = crect.y1;
vy1 = crect.y2;
}
GLfloat vx2 = vx1 + (crect.x2 - crect.x1);
GLfloat vy2 = vy1 + (crect.y2 - crect.y1);
GLfloat vy2 = vy1 + (crect.y1 - crect.y2);
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);
for (int k = 0; k < 4; k++) {
glTexCoord2f(texture_x[k], texture_y[k]);
glVertex3i(vx[k], vy[k], 0);
}
}
P_PAINTREG_END();
glEnd();
glUseProgram(0);
// Swap tex_scr and tex_scr2
GLuint tmp = tex_scr2;
tex_scr2 = tex_scr;
tex_scr = tmp;
curr = !curr;
}
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);
}
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
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();
}
GLuint glGetUniformLocationChecked(GLuint p, const char *name) {
static GLuint glGetUniformLocationChecked(GLuint p, const char *name) {
auto ret = glGetUniformLocation(p, name);
if (ret < 0) {
log_error("Failed to get location of uniform '%s'. compton might not "
"work correctly.",
name);
return 0;
}
return ret;
}
@ -552,8 +419,8 @@ GLuint glGetUniformLocationChecked(GLuint p, const char *name) {
/**
* 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) {
static int gl_win_shader_from_string(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) {
@ -565,6 +432,7 @@ int gl_win_shader_from_string(session_t *ps, const char *vshader_str,
ret->unifm_opacity = glGetUniformLocationChecked(ret->prog, "opacity");
ret->unifm_invert_color = glGetUniformLocationChecked(ret->prog, "invert_color");
ret->unifm_tex = glGetUniformLocationChecked(ret->prog, "tex");
ret->unifm_dim = glGetUniformLocationChecked(ret->prog, "dim");
gl_check_err();
@ -574,7 +442,7 @@ int gl_win_shader_from_string(session_t *ps, const char *vshader_str,
/**
* Callback to run on root window size change.
*/
void gl_resize(int width, int height) {
void gl_resize(struct gl_data *gd, int width, int height) {
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
@ -582,38 +450,43 @@ void gl_resize(int width, int height) {
glOrtho(0, width, 0, height, -1000.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gd->height = height;
gd->width = width;
if (gd->npasses > 0) {
// Resize the temporary textures used for blur
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0,
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
if (gd->npasses > 1) {
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, gd->width, gd->height, 0,
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
}
}
}
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;
void gl_fill(backend_t *base, double r, double g, double b, double a, const region_t *clip) {
int nrects;
const rect_t *rect = pixman_region32_rectangles((region_t *)clip, &nrects);
struct gl_data *gd = (void *)base;
glColor4f(r, g, b, a);
glBegin(GL_QUADS);
for (int i = 0; i < nrects; i++) {
glVertex2f(rect[i].x1, gd->height - rect[i].y2);
glVertex2f(rect[i].x2, gd->height - rect[i].y2);
glVertex2f(rect[i].x2, gd->height - rect[i].y1);
glVertex2f(rect[i].x1, gd->height - rect[i].y1);
}
glEnd();
}
/**
* Initialize GL blur filters.
*
* Fill `passes` with blur filters, won't create more than MAX_BLUR_FILTER number of
* filters
*/
bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_cap_t *cap) {
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 GL backends.");
return false;
}
glDeleteFramebuffers(1, &fbo);
static bool gl_init_blur(struct gl_data *gd, conv *const *const kernels) {
if (!kernels[0]) {
return true;
}
char *lc_numeric_old = strdup(setlocale(LC_NUMERIC, NULL));
@ -621,104 +494,105 @@ bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_ca
// 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";
// clang-format off
static const char *FRAG_SHADER_BLUR = GLSL(130,
%s\n // other extension pragmas
uniform float offset_x;
uniform float offset_y;
uniform sampler2D tex_scr;
uniform float opacity;
out vec4 out_color;
void main() {
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
%s //body of the convolution
out_color = sum / float(%.7g) * opacity;
}
);
static const char *FRAG_SHADER_BLUR_ADD = QUOTE(
sum += float(%.7g) *
texture2D(tex_scr, vec2(gl_TexCoord[0].x + offset_x * float(%d),
gl_TexCoord[0].y + offset_y * float(%d)));
);
// clang-format on
const bool use_texture_rect = !cap->non_power_of_two_texture;
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;
gl_blur_shader_t *passes = gd->blur_shader;
for (int i = 0; i < MAX_BLUR_PASS && kernels[i]; gd->npasses = ++i) {
auto kern = kernels[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);
int width = kern->w, height = kern->h;
int nele = width * height - 1;
size_t body_len = (strlen(shader_add) + 42) * nele;
char *shader_body = ccalloc(body_len, char);
char *pc = shader_body;
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)
for (int j = 0; j < height; ++j) {
for (int k = 0; k < width; ++k) {
double val;
if (height / 2 == j && width / 2 == k) {
val = 1;
} else {
val = kern->data[j * width + k];
}
if (val == 0) {
continue;
}
sum += val;
sprintf(pc, shader_add, val, texture_func, k - wid / 2, j - hei / 2);
pc += strlen(pc);
assert(strlen(shader_str) < len);
pc += snprintf(pc, body_len - (pc - shader_body),
FRAG_SHADER_BLUR_ADD, val, k - width / 2,
j - height / 2);
assert(pc < shader_body + body_len);
}
}
auto pass = passes + i;
sprintf(pc, FRAG_SHADER_BLUR_SUFFIX, texture_func, sum);
assert(strlen(shader_str) < len);
pass->frag_shader = gl_create_shader(GL_FRAGMENT_SHADER, shader_str);
free(shader_str);
if (!pass->frag_shader) {
log_error("Failed to create fragment shader %d.", i);
goto err;
}
size_t shader_len = strlen(FRAG_SHADER_BLUR) + strlen(extension) +
strlen(shader_body) + 10 /* sum */ +
1 /* null terminator */;
char *shader_str = ccalloc(shader_len, char);
size_t real_shader_len = snprintf(
shader_str, shader_len, FRAG_SHADER_BLUR, extension, shader_body, sum);
assert(real_shader_len < shader_len);
free(shader_body);
// Build program
pass->prog = gl_create_program(&pass->frag_shader, 1);
pass->prog = gl_create_program_from_str(NULL, shader_str);
free(shader_str);
if (!pass->prog) {
log_error("Failed to create GLSL program.");
goto err;
}
glBindFragDataLocation(pass->prog, 0, "out_color");
// Get uniform addresses
pass->unifm_factor_center =
glGetUniformLocationChecked(pass->prog, "factor_center");
if (!ps->o.glx_use_gpushader4) {
pass->unifm_offset_x =
glGetUniformLocationChecked(pass->prog, "offset_x");
pass->unifm_offset_y =
glGetUniformLocationChecked(pass->prog, "offset_y");
}
pass->unifm_offset_x =
glGetUniformLocationChecked(pass->prog, "offset_x");
pass->unifm_offset_y =
glGetUniformLocationChecked(pass->prog, "offset_y");
pass->unifm_opacity = glGetUniformLocationChecked(pass->prog, "opacity");
}
free(extension);
// Texture size will be defined by gl_resize
glGenTextures(gd->npasses > 1 ? 2 : 1, gd->blur_texture);
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
if (gd->npasses > 1) {
glBindTexture(GL_TEXTURE_2D, gd->blur_texture[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Generate FBO and textures when needed
glGenFramebuffers(1, &gd->blur_fbo);
if (!gd->blur_fbo) {
log_error("Failed to generate framebuffer object for blur");
return false;
}
}
// Restore LC_NUMERIC
setlocale(LC_NUMERIC, lc_numeric_old);
free(lc_numeric_old);
@ -732,3 +606,130 @@ err:
free(lc_numeric_old);
return false;
}
// clang-format off
const char *win_shader_glsl = GLSL(110,
uniform float opacity;
uniform float dim;
uniform bool invert_color;
uniform sampler2D tex;
void main() {
vec4 c = texture2D(tex, gl_TexCoord[0].xy);
if (invert_color) {
c = vec4(c.aaa - c.rgb, c.a);
}
c = vec4(c.rgb * (1.0 - dim), c.a) * opacity;
gl_FragColor = c;
}
);
// clang-format on
bool gl_init(struct gl_data *gd, session_t *ps) {
// Initialize GLX data structure
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
gd->blur_shader[i] = (gl_blur_shader_t){.prog = 0};
}
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
// X pixmap is in premultiplied alpha, so we might just as well use it too.
// Thanks to derhass for help.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// Initialize stencil buffer
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_STENCIL_BUFFER_BIT);
gd->npasses = 0;
gl_win_shader_from_string(NULL, win_shader_glsl, &gd->win_shader);
if (!gl_init_blur(gd, ps->o.blur_kerns)) {
return false;
}
// Set up the size of the viewport. We do this last because it expects the blur
// textures are already set up.
gl_resize(gd, ps->root_width, ps->root_height);
return true;
}
static inline void gl_free_blur_shader(gl_blur_shader_t *shader) {
if (shader->prog) {
glDeleteProgram(shader->prog);
}
shader->prog = 0;
}
void gl_deinit(struct gl_data *gd) {
// Free GLSL shaders/programs
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
gl_free_blur_shader(&gd->blur_shader[i]);
}
gl_free_prog_main(&gd->win_shader);
glDeleteTextures(gd->npasses > 1 ? 2 : 1, gd->blur_texture);
if (gd->npasses > 1) {
glDeleteFramebuffers(1, &gd->blur_fbo);
}
gl_check_err();
}
GLuint gl_new_texture(GLenum target) {
GLuint texture;
glGenTextures(1, &texture);
if (!texture) {
log_error("Failed to generate texture");
return 0;
}
glEnable(target);
glBindTexture(target, texture);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBindTexture(target, 0);
return texture;
}
/// stub for backend_operations::image_op
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
const region_t *reg_op, const region_t *reg_visible, void *arg) {
struct gl_texture *tex = image_data;
int *iargs = arg;
switch (op) {
case IMAGE_OP_INVERT_COLOR_ALL: tex->color_inverted = true; break;
case IMAGE_OP_DIM_ALL:
tex->dim = 1.0 - (1.0 - tex->dim) * (1.0 - *(double *)arg);
break;
case IMAGE_OP_APPLY_ALPHA_ALL: tex->opacity *= *(double *)arg; break;
case IMAGE_OP_APPLY_ALPHA:
// TODO
log_warn("IMAGE_OP_APPLY_ALPHA not implemented yet");
break;
case IMAGE_OP_RESIZE_TILE:
// texture is already set to repeat, so nothing else we need to do
tex->ewidth = iargs[0];
tex->eheight = iargs[1];
break;
}
return true;
}
bool gl_is_image_transparent(backend_t *base, void *image_data) {
gl_texture_t *img = image_data;
return img->has_alpha;
}

View File

@ -1,51 +1,67 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <string.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <stdbool.h>
#include <string.h>
#include "common.h"
#include "backend/backend.h"
#include "config.h"
#include "log.h"
#include "region.h"
#define CASESTRRET(s) \
case s: return #s
// 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;
GLint unifm_dim;
} 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;
GLint unifm_opacity;
} gl_blur_shader_t;
/// @brief Wrapper of a binded GLX texture.
typedef struct gl_texture {
double opacity;
double dim;
int *refcount;
GLuint texture;
GLenum target;
unsigned width;
unsigned height;
// The size of the backing texture
int width, height;
// The effective size of the texture
int ewidth, eheight;
unsigned depth;
bool y_inverted;
bool has_alpha;
bool color_inverted;
} gl_texture_t;
// OpenGL capabilities
typedef struct gl_cap {
bool non_power_of_two_texture;
} gl_cap_t;
struct gl_data {
backend_t base;
// Height and width of the viewport
int height, width;
int npasses;
gl_win_shader_t win_shader;
gl_blur_shader_t blur_shader[MAX_BLUR_PASS];
// Temporary textures used for blurring. They are always the same size as the
// target, so they are always big enough without resizing.
// Turns out calling glTexImage to resize is expensive, so we avoid that.
GLuint blur_texture[2];
// Temporary fbo used for blurring
GLuint blur_fbo;
};
typedef struct {
/// Framebuffer used for blurring.
@ -58,23 +74,40 @@ typedef struct {
int height;
} gl_blur_cache_t;
typedef struct session session_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);
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);
void gl_free_prog_main(session_t *ps, gl_win_shader_t *prog);
/**
* @brief Render a region with texture data.
*/
void gl_compose(backend_t *, void *ptex, int dst_x, int dst_y, const region_t *reg_tgt,
const region_t *reg_visible);
unsigned char *gl_take_screenshot(session_t *ps, int *out_length);
void gl_resize(int width, int height);
bool gl_create_blur_filters(session_t *ps, gl_blur_shader_t *passes, const gl_cap_t *cap);
void gl_resize(struct gl_data *, int width, int height);
GLuint glGetUniformLocationChecked(GLuint p, const char *name);
bool gl_init(struct gl_data *gd, session_t *);
void gl_deinit(struct gl_data *gd);
GLuint gl_new_texture(GLenum target);
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
const region_t *reg_op, const region_t *reg_visible, void *arg);
bool gl_blur(backend_t *base, double opacity, const region_t *reg_blur,
const region_t *reg_visible);
bool gl_is_image_transparent(backend_t *base, void *image_data);
void gl_fill(backend_t *base, double r, double g, double b, double a, const region_t *clip);
static inline void gl_delete_texture(GLuint texture) {
glDeleteTextures(1, &texture);
}
/**
* Get a textual representation of an OpenGL error.
@ -118,11 +151,11 @@ static inline void gl_check_err_(const char *func, int line) {
/**
* Check if a GLX extension exists.
*/
static inline bool gl_has_extension(session_t *ps, const char *ext) {
static inline bool gl_has_extension(const char *ext) {
GLint nexts = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &nexts);
if (!nexts) {
log_error("Failed get GL extension list.");
log_error("Failed to get GL extension list.");
return false;
}
@ -134,33 +167,3 @@ static inline bool gl_has_extension(session_t *ps, const char *ext) {
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(&reg_new, dx, dy, width, height); \
pixman_region32_intersect(&reg_new, &reg_new, (region_t *)reg_tgt); \
rects = pixman_region32_rectangles(&reg_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(&reg_new); \
} \
while (0)

File diff suppressed because it is too large Load Diff

72
src/backend/gl/glx.h Normal file
View File

@ -0,0 +1,72 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stdbool.h>
// Older version of glx.h defines function prototypes for these extensions...
// Rename them to avoid conflicts
#define glXSwapIntervalMESA glXSwapIntervalMESA_
#define glXBindTexImageEXT glXBindTexImageEXT_
#define glXReleaseTexImageEXT glXReleaseTexImageEXT
#include <GL/glx.h>
#undef glXSwapIntervalMESA
#undef glXBindTexImageEXT
#undef glXReleaseTexImageEXT
#include <X11/Xlib.h>
#include <xcb/xcb.h>
#include <xcb/render.h>
#include "log.h"
#include "compiler.h"
#include "utils.h"
#include "x.h"
struct glx_fbconfig_info {
GLXFBConfig cfg;
int texture_tgts;
int texture_fmt;
int y_inverted;
};
/// The search criteria for glx_find_fbconfig
struct glx_fbconfig_criteria {
/// Bit width of the red component
int red_size;
/// Bit width of the green component
int green_size;
/// Bit width of the blue component
int blue_size;
/// Bit width of the alpha component
int alpha_size;
/// The depth of X visual
int visual_depth;
};
struct glx_fbconfig_info *glx_find_fbconfig(Display *, int screen, struct xvisual_info);
struct glxext_info {
bool initialized;
bool has_GLX_SGI_video_sync;
bool has_GLX_SGI_swap_control;
bool has_GLX_OML_sync_control;
bool has_GLX_MESA_swap_control;
bool has_GLX_EXT_swap_control;
bool has_GLX_EXT_texture_from_pixmap;
bool has_GLX_ARB_create_context;
bool has_GLX_EXT_buffer_age;
};
extern struct glxext_info glxext;
extern PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
extern PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
extern PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
extern PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
extern PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
extern PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
extern PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
extern PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
void glxext_init(Display *, int screen);

View File

@ -1,12 +1,7 @@
# enable xrender
srcs += [ files('backend_common.c') ]
if get_option('new_backends')
srcs += [ files('xrender.c', 'backend.c') ]
srcs += [ files('backend_common.c', 'xrender.c', 'backend.c') ]
# enable opengl
if get_option('opengl')
srcs += [ files('gl/gl_common.c', 'gl/glx.c') ]
deps += [ dependency('gl', required: true) ]
cflags += [ '-DGL_GLEXT_PROTOTYPES' ]
endif
# enable opengl
if get_option('opengl')
srcs += [ files('gl/gl_common.c', 'gl/glx.c') ]
endif

View File

@ -1,18 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/present.h>
#include <xcb/sync.h>
#include <xcb/render.h>
#include <xcb/composite.h>
#include <xcb/present.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xcb.h>
#include "backend/backend.h"
#include "backend_common.h"
#include "backend/backend_common.h"
#include "common.h"
#include "config.h"
#include "kernel.h"
#include "log.h"
#include "region.h"
#include "utils.h"
@ -22,6 +25,10 @@
#define auto __auto_type
typedef struct _xrender_data {
backend_t base;
/// If vsync is enabled and supported by the current system
bool vsync;
xcb_visualid_t default_visual;
/// The idle fence for the present extension
xcb_sync_fence_t idle_fence;
/// The target window
@ -29,9 +36,13 @@ typedef struct _xrender_data {
/// The painting target, it is either the root or the overlay
xcb_render_picture_t target;
/// A back buffer
xcb_render_picture_t back;
xcb_render_picture_t back[2];
/// Age of each back buffer
int buffer_age[2];
/// The back buffer we should be painting into
int curr_back;
/// The corresponding pixmap to the back buffer
xcb_pixmap_t back_pixmap;
xcb_pixmap_t back_pixmap[2];
/// The original root window content, usually the wallpaper.
/// We save it so we don't loss the wallpaper when we paint over
/// it.
@ -47,152 +58,117 @@ typedef struct _xrender_data {
/// 1x1 black picture
xcb_render_picture_t black_pixel;
/// 1x1 picture of the shadow color
xcb_render_picture_t shadow_pixel;
/// Width and height of the target pixmap
int target_width, target_height;
/// Blur kernels converted to X format
xcb_render_fixed_t *x_blur_kern[MAX_BLUR_PASS];
/// Number of elements in each blur kernel
size_t x_blur_kern_size[MAX_BLUR_PASS];
xcb_special_event_t *present_event;
} 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 {
struct _xrender_image_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;
long width, height;
// The effective size of the image
long ewidth, eheight;
bool has_alpha;
double opacity;
xcb_visualid_t visual;
uint8_t depth;
bool owned;
};
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(&reg_tmp);
// Shadow doesn't need to be painted underneath the body of the window
// Because no one can see it
pixman_region32_subtract(&reg_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(&reg_tmp, &reg_tmp, &ps->shadow_exclude_reg);
// Might be worth while to crop the region to shadow border
pixman_region32_intersect_rect(&reg_tmp, &reg_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(&reg_tmp, &reg_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(
&reg_tmp, &reg_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(&reg_tmp, &reg_tmp, &bshape);
pixman_region32_fini(&bshape);
// Detect if the region is empty before painting
if (pixman_region32_not_empty(&reg_tmp)) {
x_set_picture_clip_region(ps, xd->back, 0, 0, &reg_tmp);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER,
wd->shadow_pict, alpha_pict, xd->back, 0, 0, 0,
0, dst_x + w->shadow_dx, dst_y + w->shadow_dy,
w->shadow_width, w->shadow_height);
}
pixman_region32_fini(&reg_tmp);
pixman_region32_fini(&shadow_reg);
}
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible) {
struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = img_data;
int op = (img->has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
auto alpha_pict = xd->alpha_pict[(int)(img->opacity * 255.0)];
region_t reg;
pixman_region32_init(&reg);
pixman_region32_intersect(&reg, (region_t *)reg_paint, (region_t *)reg_visible);
// 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_clear_picture_clip_region(base->c, img->pict);
x_set_picture_clip_region(ps, xd->back, 0, 0, reg_paint);
xcb_render_composite(ps->c, op, wd->rendered_pict, alpha_pict, xd->back, 0, 0, 0,
0, dst_x, dst_y, w->widthb, w->heightb);
x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[xd->curr_back],
0, 0, 0, 0, dst_x, dst_y, img->ewidth, img->eheight);
pixman_region32_fini(&reg);
}
/**
* 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 void
fill(backend_t *base, double r, double g, double b, double a, const region_t *clip) {
struct _xrender_data *xd = (void *)base;
const rect_t *extent = pixman_region32_extents((region_t *)clip);
x_set_picture_clip_region(base->c, xd->back[xd->curr_back], 0, 0, clip);
// color is in X fixed point representation
xcb_render_fill_rectangles(
base->c, XCB_RENDER_PICT_OP_OVER, xd->back[xd->curr_back],
(xcb_render_color_t){
.red = r * 0xffff, .green = g * 0xffff, .blue = b * 0xffff, .alpha = a * 0xffff},
1,
(xcb_rectangle_t[]){{.x = extent->x1,
.y = extent->y1,
.width = extent->x2 - extent->x1,
.height = extent->y2 - extent->y1}});
}
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;
static bool blur(backend_t *backend_data, double opacity, const region_t *reg_blur,
const region_t *reg_visible) {
struct _xrender_data *xd = (void *)backend_data;
xcb_connection_t *c = xd->base.c;
region_t reg_op;
pixman_region32_init(&reg_op);
pixman_region32_intersect(&reg_op, (region_t *)reg_blur, (region_t *)reg_visible);
if (!pixman_region32_not_empty(&reg_op)) {
pixman_region32_fini(&reg_op);
return true;
}
const pixman_box32_t *extent = pixman_region32_extents(&reg_op);
const int height = extent->y2 - extent->y1;
const int width = extent->x2 - extent->x1;
int src_x = extent->x1, src_y = extent->y1;
static const char *filter0 = "Nearest"; // The "null" filter
static const char *filter = "convolution";
// 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);
x_create_picture_with_visual(xd->base.c, xd->base.root, width, height,
xd->default_visual, 0, NULL),
x_create_picture_with_visual(xd->base.c, xd->base.root, width, height,
xd->default_visual, 0, NULL)};
if (!tmp_picture[0] || !tmp_picture[1]) {
log_error("Failed to build intermediate Picture.");
pixman_region32_fini(&reg_op);
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);
region_t clip;
pixman_region32_init(&clip);
pixman_region32_copy(&clip, &reg_op);
pixman_region32_translate(&clip, -src_x, -src_y);
x_set_picture_clip_region(c, tmp_picture[0], 0, 0, &clip);
x_set_picture_clip_region(c, tmp_picture[1], 0, 0, &clip);
pixman_region32_fini(&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->back, dst_pict = tmp_picture[0];
xcb_render_picture_t src_pict = xd->back[xd->curr_back], 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;
x_set_picture_clip_region(c, src_pict, 0, 0, &reg_op);
// For more than 1 pass, we do:
// back -(pass 1)-> tmp0 -(pass 2)-> tmp1 ...
@ -200,30 +176,30 @@ static bool blur(void *backend_data, session_t *ps, double opacity, const region
// For 1 pass, we do
// back -(pass 1)-> tmp0 -(copy)-> target_buffer
int i;
for (i = 0; ps->o.blur_kerns[i]; i++) {
for (i = 0; xd->x_blur_kern[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);
// TODO cache converted blur_kerns
xcb_render_set_picture_filter(c, src_pict, strlen(filter), filter,
xd->x_blur_kern_size[i], xd->x_blur_kern[i]);
if (ps->o.blur_kerns[i + 1] || i == 0) {
if (xd->x_blur_kern[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);
xcb_render_composite(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->back, 0, 0, 0, 0, reg->x1,
reg->y1, width, height);
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict,
alpha_pict, xd->back[xd->curr_back], 0, 0, 0,
0, src_x, src_y, width, height);
}
xrfilter_reset(ps, src_pict);
// reset filter
xcb_render_set_picture_filter(c, src_pict, strlen(filter0), filter0, 0, NULL);
src_pict = tmp_picture[current];
dst_pict = tmp_picture[!current];
@ -234,275 +210,369 @@ static bool blur(void *backend_data, session_t *ps, double opacity, const region
// There is only 1 pass
if (i == 1) {
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
xd->back, 0, 0, 0, 0, reg->x1, reg->y1, width, height);
xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, src_pict, alpha_pict,
xd->back[xd->curr_back], 0, 0, 0, 0, extent->x1,
extent->y1, width, height);
}
xcb_render_free_picture(ps->c, tmp_picture[0]);
xcb_render_free_picture(ps->c, tmp_picture[1]);
xcb_render_free_picture(c, tmp_picture[0]);
xcb_render_free_picture(c, tmp_picture[1]);
pixman_region32_fini(&reg_op);
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;
w->pixmap_damaged = false;
if (!w->invert_color && w->frame_opacity == 1 && !w->dim) {
// No extra processing needed
wd->rendered_pict = wd->pict;
return;
static void *
bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
xcb_generic_error_t *e;
auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), &e);
if (!r) {
log_error("Invalid pixmap: %#010x", pixmap);
x_print_error(e->full_sequence, e->major_code, e->minor_code, e->error_code);
return NULL;
}
region_t reg_paint_local;
pixman_region32_init(&reg_paint_local);
pixman_region32_copy(&reg_paint_local, (region_t *)reg_paint);
pixman_region32_translate(&reg_paint_local, -w->g.x, -w->g.y);
auto img = ccalloc(1, struct _xrender_image_data);
img->depth = fmt.visual_depth;
img->width = img->ewidth = r->width;
img->height = img->eheight = r->height;
img->pixmap = pixmap;
img->opacity = 1;
img->has_alpha = fmt.alpha_size != 0;
img->pict =
x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL);
img->owned = owned;
img->visual = fmt.visual;
if (img->pict == XCB_NONE) {
free(img);
return NULL;
}
return img;
}
// 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);
static void release_image(backend_t *base, void *image) {
struct _xrender_image_data *img = image;
xcb_render_free_picture(base->c, img->pict);
if (img->owned) {
xcb_free_pixmap(base->c, img->pixmap);
}
}
static void deinit(backend_t *backend_data) {
struct _xrender_data *xd = (void *)backend_data;
for (int i = 0; i < 256; i++) {
xcb_render_free_picture(xd->base.c, xd->alpha_pict[i]);
}
xcb_render_free_picture(xd->base.c, xd->target);
xcb_render_free_picture(xd->base.c, xd->root_pict);
for (int i = 0; i < 2; i++) {
xcb_render_free_picture(xd->base.c, xd->back[i]);
xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]);
}
for (int i = 0; xd->x_blur_kern[i]; i++) {
free(xd->x_blur_kern[i]);
}
if (xd->present_event) {
xcb_unregister_for_special_event(xd->base.c, xd->present_event);
}
xcb_render_free_picture(xd->base.c, xd->white_pixel);
xcb_render_free_picture(xd->base.c, xd->black_pixel);
free(xd);
}
static void present(backend_t *base) {
struct _xrender_data *xd = (void *)base;
if (xd->vsync) {
// Make sure we got reply from PresentPixmap before waiting for events,
// to avoid deadlock
auto e = xcb_request_check(
base->c, xcb_present_pixmap_checked(
xd->base.c, xd->target_win,
xd->back_pixmap[xd->curr_back], 0, XCB_NONE, XCB_NONE, 0,
0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
if (e) {
log_error("Failed to present pixmap");
free(e);
return;
}
// TODO don't block wait for present completion
xcb_present_generic_event_t *pev =
(void *)xcb_wait_for_special_event(base->c, xd->present_event);
if (!pev) {
// We don't know what happened, maybe X died
// But reset buffer age, so in case we do recover, we will
// render correctly.
xd->buffer_age[0] = xd->buffer_age[1] = -1;
return;
}
assert(pev->evtype == XCB_PRESENT_COMPLETE_NOTIFY);
xcb_present_complete_notify_event_t *pcev = (void *)pev;
// log_trace("Present complete: %d %ld", pcev->mode, pcev->msc);
xd->buffer_age[xd->curr_back] = 1;
// buffer_age < 0 means that back buffer is empty
if (xd->buffer_age[1 - xd->curr_back] > 0) {
xd->buffer_age[1 - xd->curr_back]++;
}
if (pcev->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
// We cannot use the pixmap we used anymore
xd->curr_back = 1 - xd->curr_back;
}
free(pev);
} else {
// compose() sets clip region, so clear it first to make
// sure we update the whole screen.
x_clear_picture_clip_region(xd->base.c, xd->back[xd->curr_back]);
// TODO buffer-age-like optimization might be possible here.
// but that will require a different backend API
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC,
xd->back[xd->curr_back], XCB_NONE, xd->target, 0, 0,
0, 0, 0, 0, xd->target_width, xd->target_height);
xd->buffer_age[xd->curr_back] = 1;
}
}
static int buffer_age(backend_t *backend_data) {
struct _xrender_data *xd = (void *)backend_data;
return xd->buffer_age[xd->curr_back];
}
static bool is_image_transparent(backend_t *bd, void *image) {
struct _xrender_image_data *img = image;
return img->has_alpha;
}
static bool image_op(backend_t *base, enum image_operations op, void *image,
const region_t *reg_op, const region_t *reg_visible, void *arg) {
struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = image;
region_t reg;
double *dargs = arg;
int *iargs = arg;
if (op == IMAGE_OP_APPLY_ALPHA_ALL) {
img->opacity *= dargs[0];
img->has_alpha = true;
return true;
}
// 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, XCB_NONE,
wd->rendered_pict, 0, 0, 0, 0, 0, 0, w->widthb, w->heightb);
pixman_region32_init(&reg);
if (w->invert_color) {
// Handle invert color
x_set_picture_clip_region(ps, wd->rendered_pict, 0, 0, &reg_paint_local);
switch (op) {
case IMAGE_OP_INVERT_COLOR_ALL:
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible);
if (img->has_alpha) {
auto tmp_pict =
x_create_picture_with_visual(base->c, base->root, img->width,
img->height, img->visual, 0, NULL);
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict,
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0,
img->width, img->height);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE, xd->white_pixel, XCB_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, XCB_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_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE,
xd->white_pixel, XCB_NONE, tmp_pict, 0, 0, 0,
0, 0, 0, img->width, img->height);
// We use an extra PictOpInReverse operation to get correct pixel
// alpha. There could be a better solution.
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN_REVERSE,
tmp_pict, XCB_NONE, img->pict, 0, 0, 0, 0, 0,
0, img->width, img->height);
xcb_render_free_picture(base->c, tmp_pict);
} else {
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_DIFFERENCE,
xd->white_pixel, XCB_NONE, img->pict, 0, 0,
0, 0, 0, 0, img->width, img->height);
}
break;
case IMAGE_OP_DIM_ALL:
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg_visible);
xcb_render_color_t color = {
.red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dim_opacity};
.red = 0, .green = 0, .blue = 0, .alpha = 0xffff * dargs[0]};
// Dim the actually content of window
xcb_rectangle_t rect = {
.x = 0,
.y = 0,
.width = w->widthb,
.height = w->heightb,
.width = img->width,
.height = img->height,
};
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);
}
xcb_render_fill_rectangles(base->c, XCB_RENDER_PICT_OP_OVER, img->pict,
color, 1, &rect);
break;
case IMAGE_OP_APPLY_ALPHA:
assert(reg_op);
pixman_region32_intersect(&reg, (region_t *)reg_op, (region_t *)reg_visible);
if (!pixman_region32_not_empty(&reg)) {
break;
}
pixman_region32_fini(&reg_paint_local);
if (dargs[0] == 1) {
break;
}
auto alpha_pict = xd->alpha_pict[(int)(dargs[0] * 255)];
x_set_picture_clip_region(base->c, img->pict, 0, 0, &reg);
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_IN, img->pict, XCB_NONE,
alpha_pict, 0, 0, 0, 0, 0, 0, img->width, img->height);
img->has_alpha = true;
break;
case IMAGE_OP_RESIZE_TILE:
img->ewidth = iargs[0];
img->eheight = iargs[1];
break;
case IMAGE_OP_APPLY_ALPHA_ALL: assert(false);
}
pixman_region32_fini(&reg);
return true;
}
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);
static void *copy(backend_t *base, const void *image, const region_t *reg) {
const struct _xrender_image_data *img = image;
struct _xrender_data *xd = (void *)base;
auto new_img = ccalloc(1, struct _xrender_image_data);
assert(img->visual != XCB_NONE);
log_trace("xrender: copying %#010x visual %#x", img->pixmap, img->visual);
*new_img = *img;
x_set_picture_clip_region(base->c, img->pict, 0, 0, reg);
new_img->pixmap =
x_create_pixmap(base->c, img->depth, base->root, img->width, img->height);
new_img->opacity = 1;
new_img->owned = true;
if (new_img->pixmap == XCB_NONE) {
log_error("Failed to create pixmap for copy");
free(new_img);
return NULL;
}
new_img->pict = x_create_picture_with_visual_and_pixmap(base->c, img->visual,
new_img->pixmap, 0, NULL);
if (new_img->pict == XCB_NONE) {
log_error("Failed to create picture for copy");
xcb_free_pixmap(base->c, new_img->pixmap);
free(new_img);
return NULL;
}
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;
auto alpha_pict =
img->opacity == 1 ? XCB_NONE : xd->alpha_pict[(int)(img->opacity * 255)];
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, img->pict, alpha_pict,
new_img->pict, 0, 0, 0, 0, 0, 0, img->width, img->height);
return new_img;
}
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) {
backend_t *backend_xrender_init(session_t *ps) {
auto xd = ccalloc(1, struct _xrender_data);
xd->base.c = ps->c;
xd->base.root = ps->root;
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);
xd->alpha_pict[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0);
assert(xd->alpha_pict[i] != XCB_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);
xd->target_width = ps->root_width;
xd->target_height = ps->root_height;
xd->default_visual = ps->vis;
xd->black_pixel = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0);
xd->white_pixel = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1);
if (ps->overlay != XCB_NONE) {
xd->target =
x_create_picture_with_visual_and_pixmap(ps, ps->vis, ps->overlay, 0, NULL);
xd->target = x_create_picture_with_visual_and_pixmap(
ps->c, ps->vis, ps->overlay, 0, NULL);
xd->target_win = ps->overlay;
} 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);
ps->c, ps->vis, ps->root, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
xd->target_win = ps->root;
}
auto pictfmt = x_get_pictform_for_visual(ps, ps->vis);
auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis);
if (!pictfmt) {
log_fatal("Default visual is invalid");
abort();
}
xd->back_pixmap =
x_create_pixmap(ps, pictfmt->depth, ps->root, ps->root_width, ps->root_height);
xd->back = x_create_picture_with_pictfmt_and_pixmap(ps, pictfmt, xd->back_pixmap, 0, NULL);
xd->vsync = ps->o.vsync;
if (ps->present_exists) {
auto eid = xcb_generate_id(ps->c);
auto e =
xcb_request_check(ps->c, xcb_present_select_input_checked(
ps->c, eid, xd->target_win,
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY));
if (e) {
log_error("Cannot select present input, vsync will be disabled");
xd->vsync = false;
free(e);
}
xd->present_event =
xcb_register_for_special_xge(ps->c, &xcb_present_id, eid, NULL);
if (!xd->present_event) {
log_error("Cannot register for special XGE, vsync will be "
"disabled");
xd->vsync = false;
}
} else {
xd->vsync = false;
}
// We might need to do double buffering for vsync
int pixmap_needed = xd->vsync ? 2 : 1;
for (int i = 0; i < pixmap_needed; i++) {
xd->back_pixmap[i] = x_create_pixmap(ps->c, pictfmt->depth, ps->root,
ps->root_width, ps->root_height);
xd->back[i] = x_create_picture_with_pictfmt_and_pixmap(
ps->c, pictfmt, xd->back_pixmap[i], 0, NULL);
xd->buffer_age[i] = -1;
if (xd->back_pixmap[i] == XCB_NONE || xd->back[i] == XCB_NONE) {
log_error("Cannot create pixmap for rendering");
goto err;
}
}
xd->curr_back = 0;
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);
xd->root_pict = solid_picture(ps->c, ps->root, 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);
xd->root_pict = x_create_picture_with_visual_and_pixmap(
ps->c, ps->vis, root_pixmap, 0, NULL);
}
if (ps->present_exists) {
xd->idle_fence = xcb_generate_id(ps->c);
// To make sure we won't get stuck waiting for the idle_fence, we maintain
// this invariant: the idle_fence is either triggered, or is in the
// process of being triggered (e.g. by xcb_present_pixmap)
auto e = xcb_request_check(
ps->c, xcb_sync_create_fence(ps->c, ps->root, xd->idle_fence, 1));
if (e) {
log_error("Cannot create a fence, vsync might not work");
xd->idle_fence = XCB_NONE;
free(e);
}
for (int i = 0; ps->o.blur_kerns[i]; i++) {
assert(i < MAX_BLUR_PASS - 1);
xd->x_blur_kern_size[i] = x_picture_filter_from_conv(
ps->o.blur_kerns[i], 1, &xd->x_blur_kern[i], (size_t[]){0});
}
return xd;
return &xd->base;
err:
deinit(&xd->base);
return NULL;
}
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 prepare(void *backend_data, session_t *ps, const region_t *reg_paint) {
struct _xrender_data *xd = backend_data;
if (ps->o.vsync != VSYNC_NONE && ps->present_exists) {
xcb_sync_await_fence(ps->c, 1, &xd->idle_fence);
}
// Paint the root pixmap (i.e. wallpaper)
// Limit the paint area
x_set_picture_clip_region(ps, xd->back, 0, 0, reg_paint);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, xd->root_pict, XCB_NONE,
xd->back, 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;
if (ps->o.vsync != VSYNC_NONE && ps->present_exists) {
// Only reset the fence when we are sure we will trigger it again.
// To make sure rendering won't get stuck if user toggles vsync on the
// fly.
xcb_sync_reset_fence(ps->c, xd->idle_fence);
xcb_present_pixmap(ps->c, xd->target_win, xd->back_pixmap, 0, XCB_NONE, XCB_NONE,
0, 0, XCB_NONE, XCB_NONE, xd->idle_fence, 0, 0, 1, 0, 0, NULL);
} else {
// compose() sets clip region, so clear it first to make
// sure we update the whole screen.
x_clear_picture_clip_region(ps, xd->back);
// 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->back, XCB_NONE, xd->target,
0, 0, 0, 0, 0, 0, ps->root_width, ps->root_height);
}
}
struct backend_info xrender_backend = {
.init = init,
struct backend_operations xrender_ops = {
.init = backend_xrender_init,
.deinit = deinit,
.blur = blur,
.present = present,
.prepare = prepare,
.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,
.fill = fill,
.bind_pixmap = bind_pixmap,
.release_image = release_image,
.render_shadow = default_backend_render_shadow,
//.prepare_win = prepare_win,
//.release_win = release_win,
.is_image_transparent = is_image_transparent,
.buffer_age = buffer_age,
.max_buffer_age = 2,
.image_op = image_op,
.copy = copy,
};
// vim: set noet sw=8 ts=8:

2411
src/c2.c

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,6 @@ c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data);
c2_lptr_t *c2_free_lptr(c2_lptr_t *lp);
bool c2_match(session_t *ps, win *w, const c2_lptr_t *condlst, const c2_lptr_t **cache,
void **pdata);
bool c2_match(session_t *ps, const win *w, const c2_lptr_t *condlst, void **pdata);
bool c2_list_postprocess(session_t *ps, c2_lptr_t *list);

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,11 @@
#include <stdc-predef.h>
#define auto __auto_type
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define auto __auto_type
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define likely_if(x) if (likely(x))
#define unlikely_if(x) if (unlikely(x))
#ifndef __has_attribute
# if __GNUC__ >= 4
@ -45,6 +47,8 @@
#else
# define attr_warn_unused_result
#endif
// An alias for conveninence
#define must_use attr_warn_unused_result
#if __has_attribute(nonnull)
# define attr_nonnull(...) __attribute__((nonnull(__VA_ARGS__)))

File diff suppressed because it is too large Load Diff

View File

@ -6,23 +6,24 @@
// === Includes ===
#include <stdlib.h>
#include <stdbool.h>
#include <locale.h>
#include <stdbool.h>
#include <stdlib.h>
#include <xcb/xproto.h>
#include <X11/Xutil.h>
#include "common.h"
#include "win.h"
#include "x.h"
#include "backend/backend.h"
#include "c2.h"
#include "log.h" // XXX clean up
#include "region.h"
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "log.h" // XXX clean up
#include "region.h"
#include "render.h"
#include "types.h"
#include "utils.h"
#include "render.h"
#include "config.h"
#include "win.h"
#include "x.h"
// == Functions ==
// TODO move static inline functions that are only used in compton.c, into
@ -35,33 +36,19 @@ void add_damage(session_t *ps, const region_t *damage);
long determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode);
xcb_window_t
find_client_win(session_t *ps, xcb_window_t w);
xcb_window_t find_client_win(session_t *ps, xcb_window_t w);
win *find_toplevel2(session_t *ps, xcb_window_t wid);
void map_win(session_t *ps, xcb_window_t id);
/**
* Subtract two unsigned long values.
*
* Truncate to 0 if the result is negative.
*/
static inline unsigned long attr_const
sub_unslong(unsigned long a, unsigned long b) {
return (a > b) ? a - b : 0;
}
/**
* Set a <code>switch_t</code> array of all unset wintypes to true.
*/
static inline void
wintype_arr_enable_unset(switch_t arr[]) {
wintype_t i;
static inline void wintype_arr_enable_unset(switch_t arr[]) {
wintype_t i;
for (i = 0; i < NUM_WINTYPES; ++i)
if (UNSET == arr[i])
arr[i] = ON;
for (i = 0; i < NUM_WINTYPES; ++i)
if (UNSET == arr[i])
arr[i] = ON;
}
/**
@ -71,91 +58,76 @@ wintype_arr_enable_unset(switch_t arr[]) {
* @param count amount of elements in the array
* @param wid window ID to search for
*/
static inline bool
array_wid_exists(const xcb_window_t *arr, int count, xcb_window_t wid) {
while (count--) {
if (arr[count] == wid) {
return true;
}
}
static inline bool array_wid_exists(const xcb_window_t *arr, int count, xcb_window_t wid) {
while (count--) {
if (arr[count] == wid) {
return true;
}
}
return false;
return false;
}
/**
* Destroy a condition list.
*/
static inline void
free_wincondlst(c2_lptr_t **pcondlst) {
while ((*pcondlst = c2_free_lptr(*pcondlst)))
continue;
static inline void free_wincondlst(c2_lptr_t **pcondlst) {
while ((*pcondlst = c2_free_lptr(*pcondlst)))
continue;
}
#ifndef CONFIG_OPENGL
static inline void
free_paint_glx(session_t *ps, paint_t *p) {}
static inline void
free_win_res_glx(session_t *ps, win *w) {}
static inline void free_paint_glx(session_t *ps, paint_t *p) {
}
static inline void free_win_res_glx(session_t *ps, win *w) {
}
#endif
/**
* Create a XTextProperty of a single string.
*/
static inline XTextProperty *
make_text_prop(session_t *ps, char *str) {
XTextProperty *pprop = ccalloc(1, XTextProperty);
static inline XTextProperty *make_text_prop(session_t *ps, char *str) {
XTextProperty *pprop = ccalloc(1, XTextProperty);
if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) {
cxfree(pprop->value);
free(pprop);
pprop = NULL;
}
if (XmbTextListToTextProperty(ps->dpy, &str, 1, XStringStyle, pprop)) {
cxfree(pprop->value);
free(pprop);
pprop = NULL;
}
return pprop;
return pprop;
}
/**
* Set a single-string text property on a window.
*/
static inline bool
wid_set_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop_atom, char *str) {
XTextProperty *pprop = make_text_prop(ps, str);
if (!pprop) {
log_error("Failed to make text property: %s.", str);
return false;
}
XTextProperty *pprop = make_text_prop(ps, str);
if (!pprop) {
log_error("Failed to make text property: %s.", str);
return false;
}
XSetTextProperty(ps->dpy, wid, pprop, prop_atom);
cxfree(pprop->value);
cxfree(pprop);
XSetTextProperty(ps->dpy, wid, pprop, prop_atom);
cxfree(pprop->value);
cxfree(pprop);
return true;
return true;
}
/**
* Dump an drawable's info.
*/
static inline void
dump_drawable(session_t *ps, xcb_drawable_t drawable) {
auto r = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, drawable), NULL);
if (!r) {
log_trace("Drawable %#010x: Failed", drawable);
return;
}
log_trace("Drawable %#010x: x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u",
drawable, r->x, r->y, r->width, r->height, r->border_width, r->depth);
free(r);
}
/**
* Validate pixmap of a window, and destroy pixmap and picture if invalid.
*/
static inline void
win_validate_pixmap(session_t *ps, win *w) {
// Destroy pixmap and picture, if invalid
if (!x_validate_pixmap(ps, w->paint.pixmap))
free_paint(ps, &w->paint);
static inline void dump_drawable(session_t *ps, xcb_drawable_t drawable) {
auto r = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, drawable), NULL);
if (!r) {
log_trace("Drawable %#010x: Failed", drawable);
return;
}
log_trace("Drawable %#010x: x = %u, y = %u, wid = %u, hei = %d, b = %u, d = %u",
drawable, r->x, r->y, r->width, r->height, r->border_width, r->depth);
free(r);
}
// vim: set et sw=2 :

View File

@ -1,5 +1,4 @@
// modulemap
// Generated by: modularize -module-map-path=modulemap list -I. -I/usr/include/pixman-1 -x c -I/usr/include/dbus-1.0 -DCONFIG_XINERAMA -DCONFIG_LIBCONFIG -DCONFIG_REGEX_PCRE -DCONFIG_REGEX_PCRE_JIT -DCONFIG_OPENGL -DCONFIG_DBUS -DGL_GLEXT_PROTOTYPES -I/usr/lib/dbus-1.0/include
module compiler {
header "compiler.h"
@ -47,6 +46,7 @@ module win {
}
module log {
header "log.h"
export compiler
}
module x {
header "x.h"
@ -68,6 +68,10 @@ module backend {
module gl_common {
header "backend/gl/gl_common.h"
}
module glx {
header "backend/gl/glx.h"
export GL.glx
}
}
module backend {
header "backend/backend.h"

View File

@ -2,21 +2,22 @@
// Copyright (c) 2011-2013, Christopher Jeffrey
// Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <ctype.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include "compiler.h"
#include "common.h"
#include "utils.h"
#include "c2.h"
#include "string_utils.h"
#include "common.h"
#include "compiler.h"
#include "kernel.h"
#include "log.h"
#include "region.h"
#include "string_utils.h"
#include "types.h"
#include "utils.h"
#include "win.h"
#include "config.h"
@ -24,206 +25,248 @@
/**
* 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) {
log_error("Invalid number: %s", s);
return false;
}
while (isspace(*endptr))
++endptr;
if (*endptr) {
log_error("Trailing characters: %s", s);
return false;
}
*dest = val;
return true;
bool parse_long(const char *s, long *dest) {
const char *endptr = NULL;
long val = strtol(s, (char **)&endptr, 0);
if (!endptr || endptr == s) {
log_error("Invalid number: %s", s);
return false;
}
while (isspace(*endptr))
++endptr;
if (*endptr) {
log_error("Trailing characters: %s", s);
return false;
}
*dest = val;
return true;
}
/**
* Parse a floating-point number in matrix.
* Parse a floating-point number in from a string,
* also strips the trailing space and comma after the number.
*
* @param[in] src string to parse
* @param[out] dest return the number parsed from the string
* @return pointer to the last character parsed
*/
const char *
parse_matrix_readnum(const char *src, double *dest) {
char *pc = NULL;
double val = strtod(src, &pc);
if (!pc || pc == src) {
log_error("No number found: %s", src);
return src;
}
while (*pc && (isspace(*pc) || ',' == *pc))
++pc;
*dest = val;
return pc;
const char *parse_readnum(const char *src, double *dest) {
const char *pc = NULL;
double val = strtod_simple(src, &pc);
if (!pc || pc == src) {
log_error("No number found: %s", src);
return src;
}
while (*pc && (isspace(*pc) || *pc == ',')) {
++pc;
}
*dest = val;
return pc;
}
/**
* Parse a matrix.
*
* @param[in] src the blur kernel string
* @param[out] endptr return where the end of kernel is in the string
* @param[out] hasneg whether the kernel has negative values
*/
xcb_render_fixed_t *
parse_matrix(const char *src, const char **endptr, bool *hasneg) {
int wid = 0, hei = 0;
*hasneg = false;
conv *parse_blur_kern(const char *src, const char **endptr, bool *hasneg) {
int width = 0, height = 0;
*hasneg = false;
const char *pc = NULL;
const char *pc = NULL;
// Get matrix width and height
{
double val = 0.0;
if (src == (pc = parse_matrix_readnum(src, &val)))
goto err1;
src = pc;
wid = val;
if (src == (pc = parse_matrix_readnum(src, &val)))
goto err1;
src = pc;
hei = val;
}
// Get matrix width and height
double val = 0.0;
if (src == (pc = parse_readnum(src, &val)))
goto err1;
src = pc;
width = val;
if (src == (pc = parse_readnum(src, &val)))
goto err1;
src = pc;
height = val;
// Validate matrix width and height
if (wid <= 0 || hei <= 0) {
log_error("Invalid matrix width/height.");
goto err1;
}
if (!(wid % 2 && hei % 2)) {
log_error("Width/height not odd.");
goto err1;
}
if (wid > 16 || hei > 16)
log_warn("Matrix width/height too large, may slow down"
"rendering, and/or consume lots of memory");
// Validate matrix width and height
if (width <= 0 || height <= 0) {
log_error("Blue kernel width/height can't be negative.");
goto err1;
}
if (!(width % 2 && height % 2)) {
log_error("Blur kernel width/height must be odd.");
goto err1;
}
if (width > 16 || height > 16)
log_warn("Blur kernel width/height too large, may slow down"
"rendering, and/or consume lots of memory");
// Allocate memory
auto matrix = ccalloc(wid * hei + 2, xcb_render_fixed_t);
// Allocate memory
conv *matrix = cvalloc(sizeof(conv) + width * height * sizeof(double));
// Read elements
{
int skip = hei / 2 * wid + wid / 2;
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 err2;
src = pc;
if (val < 0) *hasneg = true;
matrix[2 + i] = DOUBLE_TO_XFIXED(val);
}
}
// Read elements
int skip = height / 2 * width + width / 2;
for (int i = 0; i < width * height; ++i) {
// Ignore the center element
if (i == skip) {
matrix->data[i] = 0;
continue;
}
if (src == (pc = parse_readnum(src, &val))) {
goto err2;
}
src = pc;
if (val < 0) {
*hasneg = true;
}
matrix->data[i] = val;
}
// Detect trailing characters
for ( ;*pc && ';' != *pc; ++pc)
if (!isspace(*pc) && ',' != *pc) {
log_error("Trailing characters in matrix string.");
goto err2;
}
// Detect trailing characters
for (; *pc && *pc != ';'; pc++) {
if (!isspace(*pc) && *pc != ',') {
// TODO isspace is locale aware, be careful
log_error("Trailing characters in blur kernel string.");
goto err2;
}
}
// Jump over spaces after ';'
if (';' == *pc) {
++pc;
while (*pc && isspace(*pc))
++pc;
}
// 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) {
log_error("Only one matrix expected.");
goto err2;
}
// Require an end of string if endptr is not provided, otherwise
// copy end pointer to endptr
if (endptr) {
*endptr = pc;
} else if (*pc) {
log_error("Only one blur kernel expected.");
goto err2;
}
// Fill in width and height
matrix[0] = DOUBLE_TO_XFIXED(wid);
matrix[1] = DOUBLE_TO_XFIXED(hei);
return matrix;
// Fill in width and height
matrix->w = width;
matrix->h = height;
return matrix;
err2:
free(matrix);
free(matrix);
err1:
return NULL;
}
/**
* Parse a convolution kernel.
*
* Output:
* hasneg: whether the convolution kernel has negative values
*/
xcb_render_fixed_t *
parse_conv_kern(const char *src, const char **endptr, bool *hasneg) {
return parse_matrix(src, endptr, hasneg);
return NULL;
}
/**
* Parse a list of convolution kernels.
*
* Output:
* hasneg: whether any of the convolution kernel has negative values
* @param[in] src string to parse
* @param[out] dest pointer to an array of kernels, must points to an array
* of `max` elements.
* @param[in] max maximum number of kernels supported
* @param[out] hasneg whether any of the kernels have negative values
* @return if the `src` string is a valid kernel list string
*/
bool
parse_conv_kern_lst(const char *src, xcb_render_fixed_t **dest, int max, bool *hasneg) {
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," },
};
bool parse_blur_kern_lst(const char *src, conv **dest, int max, bool *hasneg) {
// TODO just return a predefined kernels, not parse predefined strings...
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,"},
};
*hasneg = false;
*hasneg = false;
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_blur_kern_lst(CONV_KERN_PREDEF[i].kern_str, dest,
max, hasneg);
}
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(CONV_KERN_PREDEF[i].kern_str, dest, max, hasneg);
int i = 0;
const char *pc = src;
int i = 0;
const char *pc = src;
// Free old kernels
for (i = 0; i < max; ++i) {
free(dest[i]);
dest[i] = NULL;
}
// 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) {
bool tmp_hasneg;
dest[i] = parse_blur_kern(pc, &pc, &tmp_hasneg);
if (!dest[i]) {
return false;
}
i++;
*hasneg |= tmp_hasneg;
}
// Continue parsing until the end of source string
i = 0;
while (pc && *pc && i < max - 1) {
bool tmp_hasneg;
if (!(dest[i++] = parse_conv_kern(pc, &pc, &tmp_hasneg)))
return false;
*hasneg |= tmp_hasneg;
}
if (i > 1) {
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");
}
if (i > 1) {
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");
}
if (*pc) {
log_error("Too many blur kernels!");
return false;
}
if (*pc) {
log_error("Too many blur kernels!");
return false;
}
return true;
return true;
}
/**
@ -231,187 +274,184 @@ parse_conv_kern_lst(const char *src, xcb_render_fixed_t **dest, int max, bool *h
*
* 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;
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 = ps->root_width, .hei = ps->root_height, .x = 0, .y = 0 };
long val = 0L;
char *endptr = NULL;
geometry_t geom = {.wid = ps->root_width, .hei = ps->root_height, .x = 0, .y = 0};
long val = 0L;
char *endptr = NULL;
src = skip_space(src);
if (!*src)
goto parse_geometry_end;
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);
assert(endptr);
if (src != endptr) {
geom.wid = val;
if (geom.wid < 0) {
log_error("Invalid width: %s", src);
return false;
}
src = endptr;
}
src = skip_space(src);
}
// Parse width
// Must be base 10, because "0x0..." may appear
if (!('+' == *src || '-' == *src)) {
val = strtol(src, &endptr, 10);
assert(endptr);
if (src != endptr) {
geom.wid = val;
if (geom.wid < 0) {
log_error("Invalid width: %s", src);
return false;
}
src = endptr;
}
src = skip_space(src);
}
// Parse height
if ('x' == *src) {
++src;
val = strtol(src, &endptr, 10);
assert(endptr);
if (src != endptr) {
geom.hei = val;
if (geom.hei < 0) {
log_error("Invalid height: %s", src);
return false;
}
src = endptr;
}
src = skip_space(src);
}
// Parse height
if ('x' == *src) {
++src;
val = strtol(src, &endptr, 10);
assert(endptr);
if (src != endptr) {
geom.hei = val;
if (geom.hei < 0) {
log_error("Invalid height: %s", 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 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);
}
// 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) {
log_error("Trailing characters: %s", src);
return false;
}
if (*src) {
log_error("Trailing characters: %s", src);
return false;
}
parse_geometry_end:
pixman_region32_union_rect(dest, dest, geom.x, geom.y, geom.wid, geom.hei);
return true;
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(c2_lptr_t **res, const char *src) {
// Find opacity value
char *endptr = NULL;
long val = strtol(src, &endptr, 0);
if (!endptr || endptr == src) {
log_error("No opacity specified: %s", src);
return false;
}
if (val > 100 || val < 0) {
log_error("Opacity %ld invalid: %s", val, src);
return false;
}
// Find opacity value
char *endptr = NULL;
long val = strtol(src, &endptr, 0);
if (!endptr || endptr == src) {
log_error("No opacity specified: %s", src);
return false;
}
if (val > 100 || val < 0) {
log_error("Opacity %ld invalid: %s", val, src);
return false;
}
// Skip over spaces
while (*endptr && isspace(*endptr))
++endptr;
if (':' != *endptr) {
log_error("Opacity terminator not found: %s", src);
return false;
}
++endptr;
// Skip over spaces
while (*endptr && isspace(*endptr))
++endptr;
if (':' != *endptr) {
log_error("Opacity terminator not found: %s", src);
return false;
}
++endptr;
// Parse pattern
// I hope 1-100 is acceptable for (void *)
return c2_parse(res, endptr, (void *) val);
// Parse pattern
// I hope 1-100 is acceptable for (void *)
return c2_parse(res, endptr, (void *)val);
}
/**
* Add a pattern to a condition linked list.
*/
bool
condlst_add(c2_lptr_t **pcondlst, const char *pattern) {
if (!pattern)
return false;
bool condlst_add(c2_lptr_t **pcondlst, const char *pattern) {
if (!pattern)
return false;
if (!c2_parse(pcondlst, pattern, NULL))
exit(1);
if (!c2_parse(pcondlst, pattern, NULL))
exit(1);
return true;
return true;
}
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;
opt->wintype_option[WINTYPE_DESKTOP].shadow = false;
}
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;
opt->wintype_option[WINTYPE_DESKTOP].shadow = false;
}
// 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++) {
if (!mask[nofocus_type[i]].focus) {
mask[nofocus_type[i]].focus = true;
opt->wintype_option[nofocus_type[i]].focus = false;
}
}
for (unsigned long i = 0; i < NUM_WINTYPES; i++) {
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;
opt->wintype_option[i].focus = true;
}
if (!mask[i].full_shadow) {
mask[i].full_shadow = true;
opt->wintype_option[i].full_shadow = false;
}
if (!mask[i].redir_ignore) {
mask[i].redir_ignore = true;
opt->wintype_option[i].redir_ignore = false;
}
if (!mask[i].opacity) {
mask[i].opacity = true;
// Opacity is not set to a concrete number here because the opacity logic
// is complicated, and needs an "unset" state
opt->wintype_option[i].opacity = NAN;
}
}
// 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++) {
if (!mask[nofocus_type[i]].focus) {
mask[nofocus_type[i]].focus = true;
opt->wintype_option[nofocus_type[i]].focus = false;
}
}
for (unsigned long i = 0; i < NUM_WINTYPES; i++) {
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;
opt->wintype_option[i].focus = true;
}
if (!mask[i].full_shadow) {
mask[i].full_shadow = true;
opt->wintype_option[i].full_shadow = false;
}
if (!mask[i].redir_ignore) {
mask[i].redir_ignore = true;
opt->wintype_option[i].redir_ignore = false;
}
if (!mask[i].opacity) {
mask[i].opacity = true;
// Opacity is not set to a concrete number here because the
// opacity logic is complicated, and needs an "unset" state
opt->wintype_option[i].opacity = NAN;
}
}
}
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;
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);
ret = parse_config_libconfig(opt, config_file, shadow_enable, fading_enable,
hasneg, winopt_mask);
#endif
return ret;
return ret;
}

View File

@ -7,37 +7,27 @@
/// Common functions and definitions for configuration parsing
/// Used for command line arguments and config files
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include <xcb/xcb.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include <xcb/xfixes.h>
#ifdef CONFIG_LIBCONFIG
#include <libconfig.h>
#endif
#include "region.h"
#include "log.h"
#include "compiler.h"
#include "win.h"
#include "kernel.h"
#include "log.h"
#include "region.h"
#include "types.h"
#include "win.h"
typedef struct session session_t;
/// VSync modes.
typedef enum {
VSYNC_NONE,
VSYNC_DRM,
VSYNC_OPENGL,
VSYNC_OPENGL_OML,
VSYNC_OPENGL_SWC,
VSYNC_OPENGL_MSWC,
NUM_VSYNC,
} vsync_t;
/// @brief Possible backends of compton.
enum backend {
BKEND_XRENDER,
@ -78,8 +68,8 @@ typedef struct options_t {
bool monitor_repaint;
bool print_diagnostics;
// === General ===
/// The configuration file we used.
char *config_file;
/// Use the experimental new backends?
bool experimental_backends;
/// Path to write PID to.
char *write_pid_path;
/// The backend in use.
@ -92,14 +82,8 @@ typedef struct options_t {
bool glx_no_stencil;
/// Whether to avoid rebinding pixmap on window damage.
bool glx_no_rebind_pixmap;
/// GLX swap method we assume OpenGL uses.
int glx_swap_method;
/// Whether to use GL_EXT_gpu_shader4 to (hopefully) accelerates blurring.
bool glx_use_gpushader4;
/// Custom fragment shader for painting windows, as a string.
char *glx_fshader_win_str;
/// Whether to fork to background.
bool fork_after_register;
/// Whether to detect rounded corners.
bool detect_rounded_corners;
/// Force painting of window content with blending.
@ -118,10 +102,6 @@ typedef struct options_t {
switch_t redirected_force;
/// Whether to stop painting. Controlled through D-Bus.
switch_t stoppaint_force;
/// Whether to re-redirect screen on root size change.
bool reredir_on_root_change;
/// Whether to reinitialize GLX on root size change.
bool glx_reinit_on_root_change;
/// Whether to enable D-Bus support.
bool dbus;
/// Path to log file.
@ -145,12 +125,12 @@ typedef struct options_t {
/// Whether to enable refresh-rate-based software optimization.
bool sw_opti;
/// VSync method to use;
vsync_t vsync;
/// Whether to do VSync aggressively.
bool vsync_aggressive;
bool vsync;
/// Whether to use glFinish() instead of glFlush() for (possibly) better
/// VSync yet probably higher CPU usage.
bool vsync_use_glfinish;
/// Whether use damage information to help limit the area to paint
bool use_damage;
// === Shadow ===
/// Red, green and blue tone of the shadow.
@ -171,9 +151,9 @@ typedef struct options_t {
// === Fading ===
/// How much to fade in in a single fading step.
opacity_t fade_in_step;
double fade_in_step;
/// How much to fade out in a single fading step.
opacity_t fade_out_step;
double fade_out_step;
/// Fading time delta. In milliseconds.
unsigned long fade_delta;
/// Whether to disable fading on window open/close.
@ -185,11 +165,10 @@ typedef struct options_t {
// === Opacity ===
/// Default opacity for inactive windows.
/// 32-bit integer with the format of _NET_WM_OPACITY. 0 stands for
/// not enabled, default.
opacity_t inactive_opacity;
/// 32-bit integer with the format of _NET_WM_OPACITY.
double inactive_opacity;
/// Default opacity for inactive windows.
opacity_t active_opacity;
double active_opacity;
/// Whether inactive_opacity overrides the opacity set by window
/// attributes.
bool inactive_opacity_override;
@ -212,7 +191,7 @@ typedef struct options_t {
/// Background blur blacklist. A linked list of conditions.
c2_lptr_t *blur_background_blacklist;
/// Blur convolution kernel.
xcb_render_fixed_t *blur_kerns[MAX_BLUR_PASS];
conv *blur_kerns[MAX_BLUR_PASS];
/// How much to dim an inactive window. 0.0 - 1.0, 0 to disable.
double inactive_dim;
/// Whether to use fixed inactive dim opacity, instead of deciding
@ -246,17 +225,10 @@ typedef struct options_t {
bool track_leader;
} options_t;
extern const char *const VSYNC_STRS[NUM_VSYNC + 1];
extern const char *const BACKEND_STRS[NUM_BKEND + 1];
attr_warn_unused_result bool parse_long(const char *, long *);
attr_warn_unused_result const char *parse_matrix_readnum(const char *, double *);
attr_warn_unused_result xcb_render_fixed_t *
parse_matrix(const char *, const char **, bool *hasneg);
attr_warn_unused_result xcb_render_fixed_t *
parse_conv_kern(const char *, const char **, bool *hasneg);
attr_warn_unused_result bool
parse_conv_kern_lst(const char *, xcb_render_fixed_t **, int, bool *hasneg);
attr_warn_unused_result bool parse_blur_kern_lst(const char *, conv **, int, bool *hasneg);
attr_warn_unused_result bool parse_geometry(session_t *, const char *, region_t *);
attr_warn_unused_result bool parse_rule_opacity(c2_lptr_t **, const char *);
@ -279,18 +251,19 @@ parse_config_libconfig(options_t *, const char *config_file, bool *shadow_enable
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask);
#endif
void set_default_winopts(options_t *, win_option_mask_t *, bool shadow_enable, bool fading_enable);
void set_default_winopts(options_t *, win_option_mask_t *, bool shadow_enable,
bool fading_enable);
/// Parse a configuration file is that is enabled, also initialize the winopt_mask with
/// default values
/// Outputs and returns:
/// same as parse_config_libconfig
char *parse_config(options_t *, const char *config_file, bool *shadow_enable,
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask);
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask);
/**
* Parse a backend option argument.
*/
static inline attr_const enum backend parse_backend(const char *str) {
static inline attr_pure enum backend parse_backend(const char *str) {
for (enum backend i = 0; BACKEND_STRS[i]; ++i) {
if (!strcasecmp(str, BACKEND_STRS[i])) {
return i;
@ -298,13 +271,13 @@ static inline attr_const enum backend parse_backend(const char *str) {
}
// Keep compatibility with an old revision containing a spelling mistake...
if (!strcasecmp(str, "xr_glx_hybird")) {
log_warn("backend xr_glx_hybird should be xr_glx_hybrid, the misspelt"
log_warn("backend xr_glx_hybird should be xr_glx_hybrid, the misspelt "
"version will be removed soon.");
return BKEND_XR_GLX_HYBRID;
}
// cju wants to use dashes
if (!strcasecmp(str, "xr-glx-hybrid")) {
log_warn("backend xr-glx-hybrid should be xr_glx_hybrid, the alternative"
log_warn("backend xr-glx-hybrid should be xr_glx_hybrid, the alternative "
"version will be removed soon.");
return BKEND_XR_GLX_HYBRID;
}
@ -312,62 +285,15 @@ static inline attr_const enum backend parse_backend(const char *str) {
return NUM_BKEND;
}
/**
* Parse a glx_swap_method option argument.
*
* Returns -2 on failure
*/
static inline attr_const int parse_glx_swap_method(const char *str) {
// Parse alias
if (!strcmp("undefined", str)) {
return 0;
}
if (!strcmp("copy", str)) {
return 1;
}
if (!strcmp("exchange", str)) {
return 2;
}
if (!strcmp("buffer-age", str)) {
return -1;
}
// Parse number
char *pc = NULL;
int age = strtol(str, &pc, 0);
if (!pc || str == pc) {
log_error("glx-swap-method is an invalid number: %s", str);
return -2;
}
for (; *pc; ++pc)
if (!isspace(*pc)) {
log_error("Trailing characters in glx-swap-method option: %s", str);
return -2;
}
if (age < -1) {
log_error("Number for glx-swap-method is too small: %s", str);
return -2;
}
return age;
}
/**
* Parse a VSync option argument.
*/
static inline vsync_t parse_vsync(const char *str) {
for (vsync_t i = 0; VSYNC_STRS[i]; ++i)
if (!strcasecmp(str, VSYNC_STRS[i])) {
return i;
}
log_error("Invalid vsync argument: %s", str);
return NUM_VSYNC;
static inline bool parse_vsync(const char *str) {
if (strcmp(str, "no") == 0 || strcmp(str, "none") == 0 ||
strcmp(str, "false") == 0 || strcmp(str, "nah") == 0) {
return false;
}
return true;
}
// vim: set noet sw=8 ts=8 :

View File

@ -1,21 +1,22 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2012-2014 Richard Grenville <pyxlcy@gmail.com>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <libconfig.h>
#include <basedir_fs.h>
#include <libconfig.h>
#include <libgen.h>
#include "common.h"
#include "compiler.h"
#include "config.h"
#include "string_utils.h"
#include "options.h"
#include "err.h"
#include "log.h"
#include "options.h"
#include "string_utils.h"
#include "utils.h"
#include "win.h"
@ -26,15 +27,32 @@
*
* So it takes a pointer to bool.
*/
static inline int
lcfg_lookup_bool(const config_t *config, const char *path, bool *value) {
int ival;
static inline int lcfg_lookup_bool(const config_t *config, const char *path, bool *value) {
int ival;
int ret = config_lookup_bool(config, path, &ival);
if (ret)
*value = ival;
int ret = config_lookup_bool(config, path, &ival);
if (ret)
*value = ival;
return ret;
return ret;
}
/// Search for config file under a base directory
FILE *open_config_file_at(const char *base, char **out_path) {
static const char *config_paths[] = {"/compton.conf", "/compton/compton.conf"};
for (size_t i = 0; i < ARR_SIZE(config_paths); i++) {
char *path = mstrjoin(base, config_paths[i]);
FILE *ret = fopen(path, "r");
if (ret && out_path) {
*out_path = path;
} else {
free(path);
}
if (ret) {
return ret;
}
}
return NULL;
}
/**
@ -42,68 +60,71 @@ lcfg_lookup_bool(const config_t *config, const char *path, bool *value) {
*
* Follows the XDG specification to search for the configuration file.
*/
FILE *
open_config_file(const char *cpath, char **ppath) {
static const char *config_paths[] = {
"/compton.conf",
"/compton/compton.conf"
};
static const char config_filename_legacy[] = "/.compton.conf";
FILE *open_config_file(const char *cpath, char **ppath) {
static const char config_filename_legacy[] = "/.compton.conf";
if (cpath) {
FILE *ret = fopen(cpath, "r");
if (ret && ppath)
*ppath = strdup(cpath);
return ret;
}
if (cpath) {
FILE *ret = fopen(cpath, "r");
if (ret && ppath)
*ppath = strdup(cpath);
return ret;
}
for (size_t i = 0; i < ARR_SIZE(config_paths); i++) {
char *path = xdgConfigFind(config_paths[i], NULL);
FILE *ret = fopen(path, "r");
if (ret && ppath) {
*ppath = strdup(path);
}
free(path);
if (ret) {
return ret;
}
}
// First search for config file in user config directory
auto config_home = xdgConfigHome(NULL);
auto ret = open_config_file_at(config_home, ppath);
free((void *)config_home);
if (ret) {
return ret;
}
// Fall back to legacy config file names
const char *home = getenv("HOME");
if (home && strlen(home)) {
auto path = ccalloc(strlen(home)+strlen(config_filename_legacy)+1, char);
strcpy(path, home);
strcpy(path+strlen(home), config_filename_legacy);
FILE *ret = fopen(path, "r");
if (ret && ppath)
*ppath = path;
else
free(path);
return ret;
}
return NULL;
// Fall back to legacy config file in user home directory
const char *home = getenv("HOME");
if (home && strlen(home)) {
auto path = mstrjoin(home, config_filename_legacy);
ret = fopen(path, "r");
if (ret && ppath) {
*ppath = path;
} else {
free(path);
}
if (ret) {
return ret;
}
}
// Fall back to config file in system config directory
auto config_dirs = xdgConfigDirectories(NULL);
for (int i = 0; config_dirs[i]; i++) {
ret = open_config_file_at(config_dirs[i], ppath);
if (ret) {
free((void *)config_dirs);
return ret;
}
}
free((void *)config_dirs);
return NULL;
}
/**
* Parse a condition list in configuration file.
*/
void
parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst,
const char *name) {
config_setting_t *setting = config_lookup(pcfg, name);
if (setting) {
// Parse an array of options
if (config_setting_is_array(setting)) {
int i = config_setting_length(setting);
while (i--)
condlst_add(pcondlst, config_setting_get_string_elem(setting, i));
}
// Treat it as a single pattern if it's a string
else if (CONFIG_TYPE_STRING == config_setting_type(setting)) {
condlst_add(pcondlst, config_setting_get_string(setting));
}
}
void parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst, const char *name) {
config_setting_t *setting = config_lookup(pcfg, name);
if (setting) {
// Parse an array of options
if (config_setting_is_array(setting)) {
int i = config_setting_length(setting);
while (i--)
condlst_add(pcondlst,
config_setting_get_string_elem(setting, i));
}
// Treat it as a single pattern if it's a string
else if (CONFIG_TYPE_STRING == config_setting_type(setting)) {
condlst_add(pcondlst, config_setting_get_string(setting));
}
}
}
/**
@ -111,22 +132,24 @@ parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst,
*/
static inline void
parse_cfg_condlst_opct(options_t *opt, const config_t *pcfg, const char *name) {
config_setting_t *setting = config_lookup(pcfg, name);
if (setting) {
// Parse an array of options
if (config_setting_is_array(setting)) {
int i = config_setting_length(setting);
while (i--)
if (!parse_rule_opacity(&opt->opacity_rules,
config_setting_get_string_elem(setting, i)))
exit(1);
}
// Treat it as a single pattern if it's a string
else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
if (!parse_rule_opacity(&opt->opacity_rules, config_setting_get_string(setting)))
exit(1);
}
}
config_setting_t *setting = config_lookup(pcfg, name);
if (setting) {
// Parse an array of options
if (config_setting_is_array(setting)) {
int i = config_setting_length(setting);
while (i--)
if (!parse_rule_opacity(
&opt->opacity_rules,
config_setting_get_string_elem(setting, i)))
exit(1);
}
// Treat it as a single pattern if it's a string
else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
if (!parse_rule_opacity(&opt->opacity_rules,
config_setting_get_string(setting)))
exit(1);
}
}
}
/**
@ -135,310 +158,328 @@ parse_cfg_condlst_opct(options_t *opt, const config_t *pcfg, const char *name) {
* Returns the actually config_file name
*/
char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shadow_enable,
bool *fading_enable, bool *conv_kern_hasneg, win_option_mask_t *winopt_mask)
{
char *path = NULL;
FILE *f;
config_t cfg;
int ival = 0;
bool bval;
double dval = 0.0;
// libconfig manages string memory itself, so no need to manually free
// anything
const char *sval = NULL;
bool *fading_enable, bool *conv_kern_hasneg,
win_option_mask_t *winopt_mask) {
char *path = NULL;
FILE *f;
config_t cfg;
int ival = 0;
bool bval;
double dval = 0.0;
// libconfig manages string memory itself, so no need to manually free
// anything
const char *sval = NULL;
f = open_config_file(config_file, &path);
if (!f) {
if (config_file) {
log_fatal("Failed to read configuration file \"%s\".", config_file);
abort();
}
free(path);
return NULL;
}
f = open_config_file(config_file, &path);
if (!f) {
free(path);
if (config_file) {
log_fatal("Failed to read configuration file \"%s\".", config_file);
return ERR_PTR(-1);
}
return NULL;
}
config_init(&cfg);
{
// dirname() could modify the original string, thus we must pass a
// copy
char *path2 = strdup(path);
char *parent = dirname(path2);
config_init(&cfg);
{
// dirname() could modify the original string, thus we must pass a
// copy
char *path2 = strdup(path);
char *parent = dirname(path2);
if (parent)
config_set_include_dir(&cfg, parent);
if (parent)
config_set_include_dir(&cfg, parent);
free(path2);
}
free(path2);
}
{
int read_result = config_read(&cfg, f);
fclose(f);
f = NULL;
if (read_result == CONFIG_FALSE) {
log_error("Error when reading configuration file \"%s\", line %d: %s",
path, config_error_line(&cfg), config_error_text(&cfg));
config_destroy(&cfg);
free(path);
return NULL;
}
}
config_set_auto_convert(&cfg, 1);
{
int read_result = config_read(&cfg, f);
fclose(f);
f = NULL;
if (read_result == CONFIG_FALSE) {
log_fatal("Error when reading configuration file \"%s\", line "
"%d: %s",
path, config_error_line(&cfg), config_error_text(&cfg));
goto err;
}
}
config_set_auto_convert(&cfg, 1);
// Get options from the configuration file. We don't do range checking
// right now. It will be done later
// Get options from the configuration file. We don't do range checking
// right now. It will be done later
// -D (fade_delta)
if (config_lookup_int(&cfg, "fade-delta", &ival))
opt->fade_delta = ival;
// -I (fade_in_step)
if (config_lookup_float(&cfg, "fade-in-step", &dval))
opt->fade_in_step = normalize_d(dval) * OPAQUE;
// -O (fade_out_step)
if (config_lookup_float(&cfg, "fade-out-step", &dval))
opt->fade_out_step = normalize_d(dval) * OPAQUE;
// -r (shadow_radius)
config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
// -o (shadow_opacity)
config_lookup_float(&cfg, "shadow-opacity", &opt->shadow_opacity);
// -l (shadow_offset_x)
config_lookup_int(&cfg, "shadow-offset-x", &opt->shadow_offset_x);
// -t (shadow_offset_y)
config_lookup_int(&cfg, "shadow-offset-y", &opt->shadow_offset_y);
// -i (inactive_opacity)
if (config_lookup_float(&cfg, "inactive-opacity", &dval))
opt->inactive_opacity = normalize_d(dval) * OPAQUE;
// --active_opacity
if (config_lookup_float(&cfg, "active-opacity", &dval))
opt->active_opacity = normalize_d(dval) * OPAQUE;
// -e (frame_opacity)
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
// -c (shadow_enable)
if (config_lookup_bool(&cfg, "shadow", &ival))
*shadow_enable = ival;
// -C (no_dock_shadow)
if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) {
log_warn("Option `no-dock-shadow` is deprecated, and will be removed."
" Please use the wintype option `shadow` of `dock` instead.");
opt->wintype_option[WINTYPE_DOCK].shadow = false;
winopt_mask[WINTYPE_DOCK].shadow = true;
}
// -G (no_dnd_shadow)
if (config_lookup_bool(&cfg, "no-dnd-shadow", &ival)) {
log_warn("Option `no-dnd-shadow` is deprecated, and will be removed."
" Please use the wintype option `shadow` of `dnd` instead.");
opt->wintype_option[WINTYPE_DND].shadow = false;
winopt_mask[WINTYPE_DND].shadow = true;
};
// -m (menu_opacity)
if (config_lookup_float(&cfg, "menu-opacity", &dval)) {
log_warn("Option `menu-opacity` is deprecated, and will be removed.Please use the "
"wintype option `opacity` of `popup_menu` and `dropdown_menu` instead.");
opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = dval;
opt->wintype_option[WINTYPE_POPUP_MENU].opacity = dval;
winopt_mask[WINTYPE_DROPDOWN_MENU].opacity = true;
winopt_mask[WINTYPE_POPUP_MENU].opacity = true;
}
// -f (fading_enable)
if (config_lookup_bool(&cfg, "fading", &ival))
*fading_enable = ival;
// --no-fading-open-close
lcfg_lookup_bool(&cfg, "no-fading-openclose", &opt->no_fading_openclose);
// --no-fading-destroyed-argb
lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb",
&opt->no_fading_destroyed_argb);
// --shadow-red
config_lookup_float(&cfg, "shadow-red", &opt->shadow_red);
// --shadow-green
config_lookup_float(&cfg, "shadow-green", &opt->shadow_green);
// --shadow-blue
config_lookup_float(&cfg, "shadow-blue", &opt->shadow_blue);
// --shadow-exclude-reg
if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval))
opt->shadow_exclude_reg_str = strdup(sval);
// --inactive-opacity-override
lcfg_lookup_bool(&cfg, "inactive-opacity-override",
&opt->inactive_opacity_override);
// --inactive-dim
config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim);
// --mark-wmwin-focused
lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opt->mark_wmwin_focused);
// --mark-ovredir-focused
lcfg_lookup_bool(&cfg, "mark-ovredir-focused",
&opt->mark_ovredir_focused);
// --shadow-ignore-shaped
lcfg_lookup_bool(&cfg, "shadow-ignore-shaped",
&opt->shadow_ignore_shaped);
// --detect-rounded-corners
lcfg_lookup_bool(&cfg, "detect-rounded-corners",
&opt->detect_rounded_corners);
// --xinerama-shadow-crop
lcfg_lookup_bool(&cfg, "xinerama-shadow-crop",
&opt->xinerama_shadow_crop);
// --detect-client-opacity
lcfg_lookup_bool(&cfg, "detect-client-opacity",
&opt->detect_client_opacity);
// --refresh-rate
config_lookup_int(&cfg, "refresh-rate", &opt->refresh_rate);
// --vsync
if (config_lookup_string(&cfg, "vsync", &sval)) {
opt->vsync = parse_vsync(sval);
if (opt->vsync >= NUM_VSYNC) {
log_fatal("Cannot parse vsync");
exit(1);
}
}
// --backend
if (config_lookup_string(&cfg, "backend", &sval)) {
opt->backend = parse_backend(sval);
if (opt->backend >= NUM_BKEND) {
log_fatal("Cannot parse backend");
exit(1);
}
}
// --log-level
if (config_lookup_string(&cfg, "log-level", &sval)) {
auto level = string_to_log_level(sval);
if (level == LOG_LEVEL_INVALID) {
log_warn("Invalid log level, defaults to WARN");
} else {
log_set_level_tls(level);
}
}
// --log-file
if (config_lookup_string(&cfg, "log-file", &sval)) {
if (*sval != '/') {
log_warn("The log-file in your configuration file is not an absolute path");
}
opt->logpath = strdup(sval);
}
// --sw-opti
lcfg_lookup_bool(&cfg, "sw-opti", &opt->sw_opti);
// --use-ewmh-active-win
lcfg_lookup_bool(&cfg, "use-ewmh-active-win",
&opt->use_ewmh_active_win);
// --unredir-if-possible
lcfg_lookup_bool(&cfg, "unredir-if-possible",
&opt->unredir_if_possible);
// --unredir-if-possible-delay
if (config_lookup_int(&cfg, "unredir-if-possible-delay", &ival))
opt->unredir_if_possible_delay = ival;
// --inactive-dim-fixed
lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &opt->inactive_dim_fixed);
// --detect-transient
lcfg_lookup_bool(&cfg, "detect-transient", &opt->detect_transient);
// --detect-client-leader
lcfg_lookup_bool(&cfg, "detect-client-leader",
&opt->detect_client_leader);
// --shadow-exclude
parse_cfg_condlst(&cfg, &opt->shadow_blacklist, "shadow-exclude");
// --fade-exclude
parse_cfg_condlst(&cfg, &opt->fade_blacklist, "fade-exclude");
// --focus-exclude
parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude");
// --invert-color-include
parse_cfg_condlst(&cfg, &opt->invert_color_list, "invert-color-include");
// --blur-background-exclude
parse_cfg_condlst(&cfg, &opt->blur_background_blacklist, "blur-background-exclude");
// --opacity-rule
parse_cfg_condlst_opct(opt, &cfg, "opacity-rule");
// --unredir-if-possible-exclude
parse_cfg_condlst(&cfg, &opt->unredir_if_possible_blacklist, "unredir-if-possible-exclude");
// --blur-background
lcfg_lookup_bool(&cfg, "blur-background", &opt->blur_background);
// --blur-background-frame
lcfg_lookup_bool(&cfg, "blur-background-frame",
&opt->blur_background_frame);
// --blur-background-fixed
lcfg_lookup_bool(&cfg, "blur-background-fixed",
&opt->blur_background_fixed);
// --blur-kern
if (config_lookup_string(&cfg, "blur-kern", &sval) &&
!parse_conv_kern_lst(sval, opt->blur_kerns, MAX_BLUR_PASS, conv_kern_hasneg)) {
log_fatal("Cannot parse \"blur-kern\"");
exit(1);
}
// --resize-damage
config_lookup_int(&cfg, "resize-damage", &opt->resize_damage);
// --glx-no-stencil
lcfg_lookup_bool(&cfg, "glx-no-stencil", &opt->glx_no_stencil);
// --glx-no-rebind-pixmap
lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &opt->glx_no_rebind_pixmap);
// --glx-swap-method
if (config_lookup_string(&cfg, "glx-swap-method", &sval)) {
opt->glx_swap_method = parse_glx_swap_method(sval);
if (opt->glx_swap_method == -2) {
log_fatal("Cannot parse \"glx-swap-method\"");
exit(1);
}
}
// --glx-use-gpushader4
lcfg_lookup_bool(&cfg, "glx-use-gpushader4", &opt->glx_use_gpushader4);
// --xrender-sync
if (config_lookup_bool(&cfg, "xrender-sync", &ival) && ival) {
log_warn("Please use xrender-sync-fence instead of xrender-sync.");
opt->xrender_sync_fence = true;
}
// --xrender-sync-fence
lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence);
// -D (fade_delta)
if (config_lookup_int(&cfg, "fade-delta", &ival))
opt->fade_delta = ival;
// -I (fade_in_step)
if (config_lookup_float(&cfg, "fade-in-step", &dval))
opt->fade_in_step = normalize_d(dval);
// -O (fade_out_step)
if (config_lookup_float(&cfg, "fade-out-step", &dval))
opt->fade_out_step = normalize_d(dval);
// -r (shadow_radius)
config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
// -o (shadow_opacity)
config_lookup_float(&cfg, "shadow-opacity", &opt->shadow_opacity);
// -l (shadow_offset_x)
config_lookup_int(&cfg, "shadow-offset-x", &opt->shadow_offset_x);
// -t (shadow_offset_y)
config_lookup_int(&cfg, "shadow-offset-y", &opt->shadow_offset_y);
// -i (inactive_opacity)
if (config_lookup_float(&cfg, "inactive-opacity", &dval))
opt->inactive_opacity = normalize_d(dval);
// --active_opacity
if (config_lookup_float(&cfg, "active-opacity", &dval))
opt->active_opacity = normalize_d(dval);
// -e (frame_opacity)
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
// -c (shadow_enable)
if (config_lookup_bool(&cfg, "shadow", &ival))
*shadow_enable = ival;
// -C (no_dock_shadow)
if (config_lookup_bool(&cfg, "no-dock-shadow", &ival)) {
log_warn("Option `no-dock-shadow` is deprecated, and will be removed."
" Please use the wintype option `shadow` of `dock` instead.");
opt->wintype_option[WINTYPE_DOCK].shadow = false;
winopt_mask[WINTYPE_DOCK].shadow = true;
}
// -G (no_dnd_shadow)
if (config_lookup_bool(&cfg, "no-dnd-shadow", &ival)) {
log_warn("Option `no-dnd-shadow` is deprecated, and will be removed."
" Please use the wintype option `shadow` of `dnd` instead.");
opt->wintype_option[WINTYPE_DND].shadow = false;
winopt_mask[WINTYPE_DND].shadow = true;
};
// -m (menu_opacity)
if (config_lookup_float(&cfg, "menu-opacity", &dval)) {
log_warn("Option `menu-opacity` is deprecated, and will be "
"removed.Please use the "
"wintype option `opacity` of `popup_menu` and `dropdown_menu` "
"instead.");
opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = dval;
opt->wintype_option[WINTYPE_POPUP_MENU].opacity = dval;
winopt_mask[WINTYPE_DROPDOWN_MENU].opacity = true;
winopt_mask[WINTYPE_POPUP_MENU].opacity = true;
}
// -f (fading_enable)
if (config_lookup_bool(&cfg, "fading", &ival))
*fading_enable = ival;
// --no-fading-open-close
lcfg_lookup_bool(&cfg, "no-fading-openclose", &opt->no_fading_openclose);
// --no-fading-destroyed-argb
lcfg_lookup_bool(&cfg, "no-fading-destroyed-argb", &opt->no_fading_destroyed_argb);
// --shadow-red
config_lookup_float(&cfg, "shadow-red", &opt->shadow_red);
// --shadow-green
config_lookup_float(&cfg, "shadow-green", &opt->shadow_green);
// --shadow-blue
config_lookup_float(&cfg, "shadow-blue", &opt->shadow_blue);
// --shadow-exclude-reg
if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval))
opt->shadow_exclude_reg_str = strdup(sval);
// --inactive-opacity-override
lcfg_lookup_bool(&cfg, "inactive-opacity-override", &opt->inactive_opacity_override);
// --inactive-dim
config_lookup_float(&cfg, "inactive-dim", &opt->inactive_dim);
// --mark-wmwin-focused
lcfg_lookup_bool(&cfg, "mark-wmwin-focused", &opt->mark_wmwin_focused);
// --mark-ovredir-focused
lcfg_lookup_bool(&cfg, "mark-ovredir-focused", &opt->mark_ovredir_focused);
// --shadow-ignore-shaped
lcfg_lookup_bool(&cfg, "shadow-ignore-shaped", &opt->shadow_ignore_shaped);
// --detect-rounded-corners
lcfg_lookup_bool(&cfg, "detect-rounded-corners", &opt->detect_rounded_corners);
// --xinerama-shadow-crop
lcfg_lookup_bool(&cfg, "xinerama-shadow-crop", &opt->xinerama_shadow_crop);
// --detect-client-opacity
lcfg_lookup_bool(&cfg, "detect-client-opacity", &opt->detect_client_opacity);
// --refresh-rate
config_lookup_int(&cfg, "refresh-rate", &opt->refresh_rate);
// --vsync
if (config_lookup_string(&cfg, "vsync", &sval)) {
opt->vsync = parse_vsync(sval);
log_warn("vsync option will take a boolean from now on. \"%s\" is "
"interpreted as \"%s\" for compatibility, but this will stop "
"working soon",
sval, opt->vsync ? "true" : "false");
}
lcfg_lookup_bool(&cfg, "vsync", &opt->vsync);
// --backend
if (config_lookup_string(&cfg, "backend", &sval)) {
opt->backend = parse_backend(sval);
if (opt->backend >= NUM_BKEND) {
log_fatal("Cannot parse backend");
goto err;
}
}
// --log-level
if (config_lookup_string(&cfg, "log-level", &sval)) {
auto level = string_to_log_level(sval);
if (level == LOG_LEVEL_INVALID) {
log_warn("Invalid log level, defaults to WARN");
} else {
log_set_level_tls(level);
}
}
// --log-file
if (config_lookup_string(&cfg, "log-file", &sval)) {
if (*sval != '/') {
log_warn("The log-file in your configuration file is not an "
"absolute path");
}
opt->logpath = strdup(sval);
}
// --sw-opti
lcfg_lookup_bool(&cfg, "sw-opti", &opt->sw_opti);
// --use-ewmh-active-win
lcfg_lookup_bool(&cfg, "use-ewmh-active-win", &opt->use_ewmh_active_win);
// --unredir-if-possible
lcfg_lookup_bool(&cfg, "unredir-if-possible", &opt->unredir_if_possible);
// --unredir-if-possible-delay
if (config_lookup_int(&cfg, "unredir-if-possible-delay", &ival))
opt->unredir_if_possible_delay = ival;
// --inactive-dim-fixed
lcfg_lookup_bool(&cfg, "inactive-dim-fixed", &opt->inactive_dim_fixed);
// --detect-transient
lcfg_lookup_bool(&cfg, "detect-transient", &opt->detect_transient);
// --detect-client-leader
lcfg_lookup_bool(&cfg, "detect-client-leader", &opt->detect_client_leader);
// --shadow-exclude
parse_cfg_condlst(&cfg, &opt->shadow_blacklist, "shadow-exclude");
// --fade-exclude
parse_cfg_condlst(&cfg, &opt->fade_blacklist, "fade-exclude");
// --focus-exclude
parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude");
// --invert-color-include
parse_cfg_condlst(&cfg, &opt->invert_color_list, "invert-color-include");
// --blur-background-exclude
parse_cfg_condlst(&cfg, &opt->blur_background_blacklist, "blur-background-exclude");
// --opacity-rule
parse_cfg_condlst_opct(opt, &cfg, "opacity-rule");
// --unredir-if-possible-exclude
parse_cfg_condlst(&cfg, &opt->unredir_if_possible_blacklist,
"unredir-if-possible-exclude");
// --blur-background
lcfg_lookup_bool(&cfg, "blur-background", &opt->blur_background);
// --blur-background-frame
lcfg_lookup_bool(&cfg, "blur-background-frame", &opt->blur_background_frame);
// --blur-background-fixed
lcfg_lookup_bool(&cfg, "blur-background-fixed", &opt->blur_background_fixed);
// --blur-kern
if (config_lookup_string(&cfg, "blur-kern", &sval) &&
!parse_blur_kern_lst(sval, opt->blur_kerns, MAX_BLUR_PASS, conv_kern_hasneg)) {
log_fatal("Cannot parse \"blur-kern\"");
goto err;
}
// --resize-damage
config_lookup_int(&cfg, "resize-damage", &opt->resize_damage);
// --glx-no-stencil
lcfg_lookup_bool(&cfg, "glx-no-stencil", &opt->glx_no_stencil);
// --glx-no-rebind-pixmap
lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &opt->glx_no_rebind_pixmap);
// --glx-swap-method
if (config_lookup_string(&cfg, "glx-swap-method", &sval)) {
char *endptr;
long val = strtol(sval, &endptr, 10);
if (*endptr || !(*sval)) {
// sval is not a number, or an empty string
val = -1;
}
if (strcmp(sval, "undefined") != 0 && val != 0) {
// If not undefined, we will use damage and buffer-age to limit
// the rendering area.
opt->use_damage = true;
}
log_warn("glx-swap-method has been deprecated since v6, your setting "
"\"%s\" should be %s.",
sval,
opt->use_damage ? "replaced by `use-damage = true`" : "removed");
}
// --use-damage
lcfg_lookup_bool(&cfg, "use-damage", &opt->use_damage);
// --glx-use-gpushader4
if (config_lookup_bool(&cfg, "glx-use-gpushader4", &ival) && ival) {
log_warn("glx-use-gpushader4 is deprecated since v6, please remove it "
"from"
"your config file");
}
// --xrender-sync
if (config_lookup_bool(&cfg, "xrender-sync", &ival) && ival) {
log_warn("Please use xrender-sync-fence instead of xrender-sync.");
opt->xrender_sync_fence = true;
}
// --xrender-sync-fence
lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence);
if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval))
log_warn("\"clear-shadow\" is removed as an option, and is always"
" enabled now. Consider removing it from your config file");
if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval))
log_warn("\"paint-on-overlay\" has been removed as an option, and "
"is enabled whenever possible");
if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval))
log_warn("\"clear-shadow\" is removed as an option, and is always"
" enabled now. Consider removing it from your config file");
if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval))
log_warn("\"paint-on-overlay\" has been removed as an option, and "
"is enabled whenever possible");
if (config_lookup_float(&cfg, "alpha-step", &dval))
log_warn("\"alpha-step\" has been removed, compton now tries to make use"
" of all alpha values");
if (config_lookup_float(&cfg, "alpha-step", &dval))
log_warn("\"alpha-step\" has been removed, compton now tries to make use"
" of all alpha values");
const char *deprecation_message = "has been removed. If you encounter problems "
"without this feature, please feel free to open a bug report";
if (lcfg_lookup_bool(&cfg, "glx-use-copysubbuffermesa", &bval) && bval)
log_warn("\"glx-use-copysubbuffermesa\" %s", deprecation_message);
if (lcfg_lookup_bool(&cfg, "glx-copy-from-front", &bval) && bval)
log_warn("\"glx-copy-from-front\" %s", deprecation_message);
const char *deprecation_message =
"has been removed. If you encounter problems "
"without this feature, please feel free to open a bug report";
if (lcfg_lookup_bool(&cfg, "glx-use-copysubbuffermesa", &bval) && bval) {
log_error("\"glx-use-copysubbuffermesa\" %s", deprecation_message);
return ERR_PTR(-1);
}
if (lcfg_lookup_bool(&cfg, "glx-copy-from-front", &bval) && bval) {
log_error("\"glx-copy-from-front\" %s", deprecation_message);
return ERR_PTR(-1);
}
// Wintype settings
// Wintype settings
// XXX ! Refactor all the wintype_* arrays into a struct
for (wintype_t i = 0; i < NUM_WINTYPES; ++i) {
char *str = mstrjoin("wintypes.", WINTYPES[i]);
config_setting_t *setting = config_lookup(&cfg, str);
free(str);
// XXX ! Refactor all the wintype_* arrays into a struct
for (wintype_t i = 0; i < NUM_WINTYPES; ++i) {
char *str = mstrjoin("wintypes.", WINTYPES[i]);
config_setting_t *setting = config_lookup(&cfg, str);
free(str);
win_option_t *o = &opt->wintype_option[i];
win_option_mask_t *mask = &winopt_mask[i];
if (setting) {
if (config_setting_lookup_bool(setting, "shadow", &ival)) {
o->shadow = ival;
mask->shadow = true;
}
if (config_setting_lookup_bool(setting, "fade", &ival)) {
o->fade = ival;
mask->fade = true;
}
if (config_setting_lookup_bool(setting, "focus", &ival)) {
o->focus = ival;
mask->focus = true;
}
if (config_setting_lookup_bool(setting, "full-shadow", &ival)) {
o->full_shadow = ival;
mask->full_shadow = true;
}
if (config_setting_lookup_bool(setting, "redir-ignore", &ival)) {
o->redir_ignore = ival;
mask->redir_ignore = true;
}
win_option_t *o = &opt->wintype_option[i];
win_option_mask_t *mask = &winopt_mask[i];
if (setting) {
if (config_setting_lookup_bool(setting, "shadow", &ival)) {
o->shadow = ival;
mask->shadow = true;
}
if (config_setting_lookup_bool(setting, "fade", &ival)) {
o->fade = ival;
mask->fade = true;
}
if (config_setting_lookup_bool(setting, "focus", &ival)) {
o->focus = ival;
mask->focus = true;
}
if (config_setting_lookup_bool(setting, "full-shadow", &ival)) {
o->full_shadow = ival;
mask->full_shadow = true;
}
if (config_setting_lookup_bool(setting, "redir-ignore", &ival)) {
o->redir_ignore = ival;
mask->redir_ignore = true;
}
double fval;
if (config_setting_lookup_float(setting, "opacity", &fval)) {
o->opacity = normalize_d(fval);
mask->opacity = true;
}
}
}
double fval;
if (config_setting_lookup_float(setting, "opacity", &fval)) {
o->opacity = normalize_d(fval);
mask->opacity = true;
}
}
}
config_destroy(&cfg);
return path;
config_destroy(&cfg);
return path;
err:
config_destroy(&cfg);
free(path);
return ERR_PTR(-1);
}

1891
src/dbus.c

File diff suppressed because it is too large Load Diff

View File

@ -8,11 +8,10 @@
#include "config.h"
#include "common.h"
void print_diagnostics(session_t *ps) {
void print_diagnostics(session_t *ps, const char *config_file) {
printf("**Version:** " COMPTON_VERSION "\n");
//printf("**CFLAGS:** %s\n", "??");
printf("\n### Extensions:\n\n");
printf("* Name Pixmap: %s\n", ps->has_name_pixmap ? "Yes" : "No");
printf("* Shape: %s\n", ps->shape_exists ? "Yes" : "No");
printf("* XRandR: %s\n", ps->randr_exists ? "Yes" : "No");
printf("* Present: %s\n", ps->present_exists ? "Present" : "Not Present");
@ -21,7 +20,7 @@ void print_diagnostics(session_t *ps) {
#ifdef __FAST_MATH__
printf("* Fast Math: Yes\n");
#endif
printf("* Config file used: %s\n", ps->o.config_file ?: "None");
printf("* Config file used: %s\n", config_file ?: "None");
}
// vim: set noet sw=8 ts=8 :

View File

@ -5,4 +5,4 @@
typedef struct session session_t;
void print_diagnostics(session_t *);
void print_diagnostics(session_t *, const char *config_file);

37
src/err.h Normal file
View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2019 Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "compiler.h"
// Functions for error reporting, adopted from Linux
// INFO in user space we can probably be more liberal about what pointer we consider
// error. e.g. In x86_64 Linux, all addresses with the highest bit set is invalid in user
// space.
#define MAX_ERRNO 4095
static inline void *must_use ERR_PTR(intptr_t err) {
return (void *)err;
}
static inline intptr_t must_use PTR_ERR(void *ptr) {
return (intptr_t)ptr;
}
static inline bool must_use IS_ERR(void *ptr) {
return unlikely((uintptr_t)ptr > (uintptr_t)-MAX_ERRNO);
}
static inline bool must_use IS_ERR_OR_NULL(void *ptr) {
return unlikely(!ptr) || IS_ERR(ptr);
}
static inline intptr_t must_use PTR_ERR_OR_ZERO(void *ptr) {
if (IS_ERR(ptr)) {
return PTR_ERR(ptr);
}
return 0;
}

View File

@ -12,32 +12,30 @@
/// top left corner is at (x, y)
double sum_kernel(const conv *map, int x, int y, int width, int height) {
double ret = 0;
/*
* Compute set of filter values which are "in range"
*/
int xstart = x;
if (xstart < 0)
// Compute sum of values which are "in range"
int xstart = x, xend = width + x;
if (xstart < 0) {
xstart = 0;
int xend = width + x;
if (xend > map->size)
xend = map->size;
int ystart = y;
if (ystart < 0)
}
if (xend > map->w) {
xend = map->w;
}
int ystart = y, yend = height + y;
if (ystart < 0) {
ystart = 0;
int yend = height + y;
if (yend > map->size)
yend = map->size;
}
if (yend > map->h) {
yend = map->h;
}
assert(yend >= ystart && xend >= xstart);
assert(yend > 0 && xend > 0);
int d = map->size;
int d = map->w;
if (map->rsum) {
// See sum_kernel_preprocess
double v1 = xstart ? map->rsum[(yend - 1) * d + xstart - 1] : 0;
double v2 = ystart ? map->rsum[(ystart - 1) * d + xend - 1] : 0;
double v3 =
(xstart && ystart) ? map->rsum[(ystart - 1) * d + xstart - 1] : 0;
double v3 = (xstart && ystart) ? map->rsum[(ystart - 1) * d + xstart - 1] : 0;
return map->rsum[(yend - 1) * d + xend - 1] - v1 - v2 + v3;
}
@ -77,7 +75,7 @@ conv *gaussian_kernel(double r) {
double t;
c = cvalloc(sizeof(conv) + size * size * sizeof(double));
c->size = size;
c->w = c->h = size;
c->rsum = NULL;
t = 0.0;
@ -100,22 +98,22 @@ conv *gaussian_kernel(double r) {
/// preprocess kernels to make shadow generation faster
/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
void shadow_preprocess(conv *map) {
const int d = map->size;
if (map->rsum)
void sum_kernel_preprocess(conv *map) {
if (map->rsum) {
free(map->rsum);
}
auto sum = map->rsum = ccalloc(d * d, double);
auto sum = map->rsum = ccalloc(map->w * map->h, double);
sum[0] = map->data[0];
for (int x = 1; x < d; x++) {
for (int x = 1; x < map->w; x++) {
sum[x] = sum[x - 1] + map->data[x];
}
for (int y = 1; y < d; y++) {
const int d = map->w;
for (int y = 1; y < map->h; y++) {
sum[y * d] = sum[(y - 1) * d] + map->data[y * d];
for (int x = 1; x < d; x++) {
for (int x = 1; x < map->w; x++) {
double tmp = sum[(y - 1) * d + x] + sum[y * d + x - 1] -
sum[(y - 1) * d + x - 1];
sum[y * d + x] = tmp + map->data[y * d + x];

View File

@ -8,22 +8,22 @@
/// Code for generating convolution kernels
typedef struct conv {
int size;
int w, h;
double *rsum;
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 attr_const sum_kernel(const conv *map, int x, int y, int width, int height);
double attr_const sum_kernel_normalized(const conv *map, int x, int y, int width, int height);
double attr_pure sum_kernel(const conv *map, int x, int y, int width, int height);
double attr_pure sum_kernel_normalized(const conv *map, int x, int y, int width, int height);
/// Create a kernel with gaussian distribution of radius r
conv *gaussian_kernel(double r);
/// preprocess kernels to make shadow generation faster
/// shadow_sum[x*d+y] is the sum of the kernel from (0, 0) to (x, y), inclusive
void shadow_preprocess(conv *map);
void sum_kernel_preprocess(conv *map);
static inline void free_conv(conv *k) {
free(k->rsum);

View File

@ -9,8 +9,9 @@
#include <unistd.h>
#ifdef CONFIG_OPENGL
#include <GL/glx.h>
#include "opengl.h"
#include <GL/gl.h>
#include "backend/gl/glx.h"
#include "backend/gl/gl_common.h"
#endif
#include "compiler.h"
@ -72,7 +73,7 @@ static attr_const const char *log_level_to_string(enum log_level level) {
case LOG_LEVEL_WARN: return "WARN";
case LOG_LEVEL_ERROR: return "ERROR";
case LOG_LEVEL_FATAL: return "FATAL ERROR";
default: assert(false);
default: return "????";
}
}
@ -103,7 +104,22 @@ void log_add_target(struct log *l, struct log_target *tgt) {
l->head = tgt;
}
/// Destroy a log struct
/// Remove a previously added log target for a log struct, and destroy it. If the log
/// target was never added, nothing happens.
void log_remove_target(struct log *l, struct log_target *tgt) {
struct log_target *now = l->head, **prev = &l->head;
while (now) {
if (now == tgt) {
*prev = now->next;
tgt->ops->destroy(tgt);
break;
}
prev = &now->next;
now = now->next;
}
}
/// Destroy a log struct and every log target added to it
void log_destroy(struct log *l) {
// free all tgt
struct log_target *head = l->head;
@ -254,7 +270,7 @@ const char *terminal_colorize_begin(enum log_level level) {
case LOG_LEVEL_WARN: return ANSI("33");
case LOG_LEVEL_ERROR: return ANSI("31;1");
case LOG_LEVEL_FATAL: return ANSI("30;103;1");
default: assert(false);
default: return "";
}
}
@ -328,7 +344,7 @@ static const struct log_ops glx_string_marker_logger_ops = {
};
struct log_target *glx_string_marker_logger_new(void) {
if (!glx_hasglext("GL_GREMEDY_string_marker")) {
if (!gl_has_extension("GL_GREMEDY_string_marker")) {
return NULL;
}
@ -344,7 +360,7 @@ struct log_target *glx_string_marker_logger_new(void) {
#else
struct log_target *glx_string_marker_logger_new(void) {
return null_logger_new();
return NULL;
}
#endif

View File

@ -17,15 +17,21 @@ enum log_level {
LOG_LEVEL_FATAL,
};
#define LOG(level, x, ...) \
do { \
if (LOG_LEVEL_##level >= log_get_level_tls()) { \
log_printf(tls_logger, LOG_LEVEL_##level, __func__, x, \
##__VA_ARGS__); \
} \
#define LOG_UNLIKELY(level, x, ...) \
do { \
if (unlikely(LOG_LEVEL_##level >= log_get_level_tls())) { \
log_printf(tls_logger, LOG_LEVEL_##level, __func__, x, ##__VA_ARGS__); \
} \
} while (0)
#define log_trace(x, ...) LOG(TRACE, x, ##__VA_ARGS__)
#define log_debug(x, ...) LOG(DEBUG, x, ##__VA_ARGS__)
#define LOG(level, x, ...) \
do { \
if (LOG_LEVEL_##level >= log_get_level_tls()) { \
log_printf(tls_logger, LOG_LEVEL_##level, __func__, x, ##__VA_ARGS__); \
} \
} while (0)
#define log_trace(x, ...) LOG_UNLIKELY(TRACE, x, ##__VA_ARGS__)
#define log_debug(x, ...) LOG_UNLIKELY(DEBUG, x, ##__VA_ARGS__)
#define log_info(x, ...) LOG(INFO, x, ##__VA_ARGS__)
#define log_warn(x, ...) LOG(WARN, x, ##__VA_ARGS__)
#define log_error(x, ...) LOG(ERROR, x, ##__VA_ARGS__)
@ -38,11 +44,15 @@ attr_printf(4, 5) void log_printf(struct log *, int level, const char *func,
const char *fmt, ...);
attr_malloc struct log *log_new(void);
/// Destroy a log struct and every log target added to it
attr_nonnull_all void log_destroy(struct log *);
attr_nonnull(1) void log_set_level(struct log *l, int level);
attr_pure enum log_level log_get_level(const struct log *l);
attr_nonnull_all void log_add_target(struct log *, struct log_target *);
attr_const enum log_level string_to_log_level(const char *);
attr_pure enum log_level string_to_log_level(const char *);
/// Remove a previously added log target for a log struct, and destroy it. If the log
/// target was never added, nothing happens.
void log_remove_target(struct log *l, struct log_target *tgt);
extern thread_local struct log *tls_logger;
@ -60,6 +70,11 @@ static inline attr_nonnull_all void log_add_target_tls(struct log_target *tgt) {
log_add_target(tls_logger, tgt);
}
static inline attr_nonnull_all void log_remove_target_tls(struct log_target *tgt) {
assert(tls_logger);
log_remove_target(tls_logger, tgt);
}
static inline attr_pure enum log_level log_get_level_tls(void) {
assert(tls_logger);
return log_get_level(tls_logger);

View File

@ -15,7 +15,7 @@ cflags = []
required_package = [
'x11', 'x11-xcb', 'xcb-renderutil',
'xcb-render', 'xcb-damage', 'xcb-randr', 'xcb-sync',
'xcb-composite', 'xcb-shape', 'xcb-image',
'xcb-composite', 'xcb-shape', 'xcb-image', 'xcb-xinerama',
'xcb-xfixes', 'xcb-present', 'xext', 'pixman-1'
]
@ -25,11 +25,6 @@ endforeach
deps = []
if get_option('xinerama')
deps += [dependency('xcb-xinerama', required: true)]
cflags += ['-DCONFIG_XINERAMA']
endif
if get_option('config_file')
deps += [dependency('libconfig', version: '>=1.4', required: true),
dependency('libxdg-basedir', required: true)]

File diff suppressed because it is too large Load Diff

View File

@ -12,310 +12,160 @@
#pragma once
#include "common.h"
#include "compiler.h"
#include "log.h"
#include "region.h"
#include "render.h"
#include "compiler.h"
#include "win.h"
#include "log.h"
#include <xcb/xcb.h>
#include <stdlib.h>
#include <string.h>
#include <GL/gl.h>
#include <ctype.h>
#include <locale.h>
#include <GL/glx.h>
#include <stdlib.h>
#include <string.h>
#include <xcb/render.h>
#include <xcb/xcb.h>
/// @brief Wrapper of a GLX FBConfig.
typedef struct glx_fbconfig {
GLXFBConfig cfg;
GLint texture_fmt;
GLint texture_tgts;
bool y_inverted;
} glx_fbconfig_t;
bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor, const region_t *reg_tgt);
#ifdef DEBUG_GLX_ERR
bool glx_render(session_t *ps, const glx_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 glx_prog_main_t *pprogram);
/**
* Get a textual representation of an OpenGL error.
*/
static inline const char *
glx_dump_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);
}
bool glx_init(session_t *ps, bool need_render);
return NULL;
}
void glx_destroy(session_t *ps);
/**
* Check for GLX error.
*
* http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/
*/
static inline void
glx_check_err_(session_t *ps, const char *func, int line) {
if (!ps->psglx->context) return;
void glx_on_root_change(session_t *ps);
GLenum err = GL_NO_ERROR;
while (GL_NO_ERROR != (err = glGetError())) {
const char *errtext = glx_dump_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 glx_check_err(ps) glx_check_err_(ps, __func__, __LINE__)
#else
#define glx_check_err(ps) ((void) 0)
#endif
/**
* Check if a word is in string.
*/
static inline bool
wd_is_in_str(const char *haystick, const char *needle) {
if (!haystick)
return false;
assert(*needle);
const char *pos = haystick - 1;
while ((pos = strstr(pos + 1, needle))) {
// Continue if it isn't a word boundary
if (((pos - haystick) && !isspace(*(pos - 1)))
|| (strlen(pos) > strlen(needle) && !isspace(pos[strlen(needle)])))
continue;
return true;
}
return false;
}
/**
* Check if a GLX extension exists.
*/
static inline bool
glx_hasglxext(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;
}
bool found = wd_is_in_str(glx_exts, ext);
if (!found)
log_info("Missing GLX extension %s.", ext);
return found;
}
/**
* Check if a GLX extension exists.
*/
static inline bool
glx_hasglext(const char *ext) {
const char *gl_exts = (const char *) glGetString(GL_EXTENSIONS);
if (!gl_exts) {
log_error("Failed get GL extension list.");
return false;
}
bool found = wd_is_in_str(gl_exts, ext);
if (!found)
log_info("Missing GL extension %s.", ext);
return found;
}
bool
glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor, const region_t *reg_tgt);
bool
glx_render(session_t *ps, const glx_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 glx_prog_main_t *pprogram);
bool
glx_init(session_t *ps, bool need_render);
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_init_blur(session_t *ps);
bool glx_init_blur(session_t *ps);
#ifdef CONFIG_OPENGL
bool
glx_load_prog_main(session_t *ps,
const char *vshader_str, const char *fshader_str,
glx_prog_main_t *pprogram);
bool glx_load_prog_main(session_t *ps, const char *vshader_str, const char *fshader_str,
glx_prog_main_t *pprogram);
#endif
bool
glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
unsigned width, unsigned height, unsigned depth);
bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, unsigned width,
unsigned height, bool repeat, const struct glx_fbconfig_info *);
void
glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
void glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
void glx_paint_pre(session_t *ps, region_t *preg)
attr_nonnull(1, 2);
void glx_paint_pre(session_t *ps, region_t *preg) attr_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);
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);
}
void
glx_set_clip(session_t *ps, const region_t *reg);
void glx_set_clip(session_t *ps, const region_t *reg);
bool
glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor_center,
const region_t *reg_tgt,
glx_blur_cache_t *pbc);
bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc);
GLuint
glx_create_shader(GLenum shader_type, const char *shader_str);
GLuint glx_create_shader(GLenum shader_type, const char *shader_str);
GLuint
glx_create_program(const GLuint * const shaders, int nshaders);
GLuint glx_create_program(const GLuint *const shaders, int nshaders);
GLuint
glx_create_program_from_str(const char *vert_shader_str,
const char *frag_shader_str);
GLuint glx_create_program_from_str(const char *vert_shader_str, const char *frag_shader_str);
unsigned char *
glx_take_screenshot(session_t *ps, int *out_length);
unsigned char *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;
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);
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;
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;
}
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);
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;
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);
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.
*/
static inline void
free_texture(session_t *ps, glx_texture_t **pptex) {
glx_texture_t *ptex = *pptex;
static inline void free_texture(session_t *ps, glx_texture_t **pptex) {
glx_texture_t *ptex = *pptex;
// Quit if there's nothing
if (!ptex)
return;
// Quit if there's nothing
if (!ptex)
return;
glx_release_pixmap(ps, ptex);
glx_release_pixmap(ps, ptex);
free_texture_r(ps, &ptex->texture);
free_texture_r(ps, &ptex->texture);
// Free structure itself
free(ptex);
*pptex = NULL;
assert(!*pptex);
// Free structure itself
free(ptex);
*pptex = NULL;
assert(!*pptex);
}
/**
* Free GLX part of paint_t.
*/
static inline void
free_paint_glx(session_t *ps, paint_t *ppaint) {
free_texture(ps, &ppaint->ptex);
static inline void free_paint_glx(session_t *ps, paint_t *ppaint) {
free_texture(ps, &ppaint->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);
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);
#ifdef CONFIG_OPENGL
free_glx_bc(ps, &w->glx_blur_cache);
free_glx_bc(ps, &w->glx_blur_cache);
free(w->paint.fbcfg);
#endif
}

View File

@ -8,7 +8,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
#include "common.h"
#include "config.h"
@ -20,11 +20,10 @@
#pragma GCC diagnostic error "-Wunused-parameter"
/**
* Print usage text and exit.
* Print usage text.
*/
static void usage(int ret) {
#define WARNING_DISABLED " (DISABLED AT COMPILE TIME)"
#define WARNING
static const char *usage_text =
"compton (" COMPTON_VERSION ")\n"
"This is the maintenance fork of compton, please report\n"
@ -87,16 +86,13 @@ static void usage(int ret) {
"--show-all-xerrors\n"
" Show all X errors (for debugging).\n"
"\n"
#undef WARNING
#ifndef CONFIG_LIBCONFIG
#define WARNING WARNING_DISABLED
#else
#define WARNING
#endif
"--config path\n"
" Look for configuration file at the path. Use /dev/null to avoid\n"
" loading configuration file." WARNING "\n"
"\n"
" loading configuration file."
#ifndef CONFIG_LIBCONFIG
WARNING_DISABLED
#endif
"\n\n"
"--write-pid-path path\n"
" Write process ID to a file.\n"
"\n"
@ -158,35 +154,8 @@ static void usage(int ret) {
" Specify refresh rate of the screen. If not specified or 0, compton\n"
" will try detecting this with X RandR extension.\n"
"\n"
"--vsync vsync-method\n"
" Set VSync method. There are (up to) 5 VSync methods currently\n"
" available:\n"
" none = No VSync\n"
#undef WARNING
#ifndef CONFIG_VSYNC_DRM
#define WARNING WARNING_DISABLED
#else
#define WARNING
#endif
" drm = VSync with DRM_IOCTL_WAIT_VBLANK. May only work on some\n"
" (DRI-based) drivers." WARNING "\n"
#undef WARNING
#ifndef CONFIG_OPENGL
#define WARNING WARNING_DISABLED
#else
#define WARNING
#endif
" opengl = Try to VSync with SGI_video_sync OpenGL extension. Only\n"
" work on some drivers." WARNING "\n"
" opengl-oml = Try to VSync with OML_sync_control OpenGL extension.\n"
" Only work on some drivers." WARNING "\n"
" opengl-swc = Enable driver-level VSync. Works only with GLX "
"backend." WARNING "\n"
" opengl-mswc = Deprecated, use opengl-swc instead." WARNING "\n"
"\n"
"--vsync-aggressive\n"
" Attempt to send painting request before VBlank and do XFlush()\n"
" during VBlank. This switch may be lifted out at any moment.\n"
"--vsync\n"
" Enable VSync\n"
"\n"
"--paint-on-overlay\n"
" Painting on X Composite overlay window.\n"
@ -291,27 +260,18 @@ static void usage(int ret) {
" should not be painted in, such as a dock window region.\n"
" Use --shadow-exclude-reg \'x10+0-0\', for example, if the 10 pixels\n"
" on the bottom of the screen should not have shadows painted on.\n"
#undef WARNING
#ifndef CONFIG_XINERAMA
#define WARNING WARNING_DISABLED
#else
#define WARNING
#endif
"\n"
"--xinerama-shadow-crop\n"
" Crop shadow of a window fully on a particular Xinerama screen to the\n"
" screen." WARNING "\n"
" screen.\n"
"\n"
#undef WARNING
#ifndef CONFIG_OPENGL
#define WARNING "(GLX BACKENDS DISABLED AT COMPILE TIME)"
#else
#define WARNING
#endif
"--backend backend\n"
" Choose backend. Possible choices are xrender, glx, and\n"
" xr_glx_hybrid" WARNING ".\n"
"\n"
" xr_glx_hybrid."
#ifndef CONFIG_OPENGL
" (GLX BACKENDS DISABLED AT COMPILE TIME)"
#endif
"\n\n"
"--glx-no-stencil\n"
" GLX backend: Avoid using stencil buffer. Might cause issues\n"
" when rendering transparent content. My tests show a 15% performance\n"
@ -323,42 +283,25 @@ static void usage(int ret) {
" known to break things on some drivers (LLVMpipe, xf86-video-intel,\n"
" etc.).\n"
"\n"
"--glx-swap-method undefined/copy/exchange/3/4/5/6/buffer-age\n"
" GLX backend: GLX buffer swap method we assume. Could be\n"
" undefined (0), copy (1), exchange (2), 3-6, or buffer-age (-1).\n"
" \"undefined\" is the slowest and the safest, and the default value.\n"
" 1 is fastest, but may fail on some drivers, 2-6 are gradually slower\n"
" but safer (6 is still faster than 0). -1 means auto-detect using\n"
" GLX_EXT_buffer_age, supported by some drivers. \n"
"\n"
"--glx-use-gpushader4\n"
" GLX backend: Use GL_EXT_gpu_shader4 for some optimization on blur\n"
" GLSL code. My tests on GTX 670 show no noticeable effect.\n"
"\n"
"--xrender-sync\n"
" Attempt to synchronize client applications' draw calls with XSync(),\n"
" used on GLX backend to ensure up-to-date window content is painted.\n"
#undef WARNING
#define WARNING
"--use-damage\n"
" Use the damage information to limit rendering to parts of the screen\n"
" that has actually changed. Potentially improves the performance.\n"
"\n"
"--xrender-sync-fence\n"
" Additionally use X Sync fence to sync clients' draw calls. Needed\n"
" on nvidia-drivers with GLX backend for some users." WARNING "\n"
" on nvidia-drivers with GLX backend for some users.\n"
"\n"
"--force-win-blend\n"
" Force all windows to be painted with blending. Useful if you have a\n"
" --glx-fshader-win that could turn opaque pixels transparent.\n"
"\n"
#undef WARNING
#ifndef CONFIG_DBUS
#define WARNING WARNING_DISABLED
#else
#define WARNING
#endif
"--dbus\n"
" Enable remote control via D-Bus. See the D-BUS API section in the\n"
" man page for more details." WARNING "\n"
"\n"
" man page for more details."
#ifndef CONFIG_DBUS
WARNING_DISABLED
#endif
"\n\n"
"--benchmark cycles\n"
" Benchmark mode. Repeatedly paint until reaching the specified cycles.\n"
"\n"
@ -370,7 +313,6 @@ static void usage(int ret) {
" backend only.\n";
FILE *f = (ret ? stderr : stdout);
fputs(usage_text, f);
#undef WARNING
#undef WARNING_DISABLED
}
@ -407,12 +349,11 @@ static const struct option longopts[] = {
{"detect-rounded-corners", no_argument, NULL, 267},
{"detect-client-opacity", no_argument, NULL, 268},
{"refresh-rate", required_argument, NULL, 269},
{"vsync", required_argument, NULL, 270},
{"vsync", optional_argument, NULL, 270},
{"alpha-step", required_argument, NULL, 271},
{"dbe", no_argument, NULL, 272},
{"paint-on-overlay", no_argument, NULL, 273},
{"sw-opti", no_argument, NULL, 274},
{"vsync-aggressive", no_argument, NULL, 275},
{"use-ewmh-active-win", no_argument, NULL, 276},
{"respect-prop-shadow", no_argument, NULL, 277},
{"unredir-if-possible", no_argument, NULL, 278},
@ -460,8 +401,8 @@ static const struct option longopts[] = {
{"no-name-pixmap", no_argument, NULL, 320},
{"log-level", required_argument, NULL, 321},
{"log-file", required_argument, NULL, 322},
{"reredir-on-root-change", no_argument, NULL, 731},
{"glx-reinit-on-root-change", no_argument, NULL, 732},
{"use-damage", no_argument, NULL, 323},
{"experimental-backends", no_argument, NULL, 733},
{"monitor-repaint", no_argument, NULL, 800},
{"diagnostics", no_argument, NULL, 801},
// Must terminate with a NULL entry
@ -470,7 +411,8 @@ static const struct option longopts[] = {
/// Get config options that are needed to parse the rest of the options
/// Return true if we should quit
bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors, int *exit_code) {
bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors,
bool *fork, int *exit_code) {
int o = 0, longopt_idx = -1;
// Pre-parse the commandline arguments to check for --config and invalid
@ -479,9 +421,16 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
// arguments
optind = 1;
*config_file = NULL;
*exit_code = 0;
while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
if (o == 256) {
*config_file = strdup(optarg);
} else if (o == 'h') {
usage(0);
return true;
} else if (o == 'b') {
*fork = true;
} else if (o == 'd') {
log_warn("-d will be ignored, please use the DISPLAY "
"environment variable");
@ -489,7 +438,6 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
*all_xerrors = true;
} else if (o == 318) {
printf("%s\n", COMPTON_VERSION);
*exit_code = 0;
return true;
} else if (o == 'S') {
log_warn("-S will be ignored");
@ -503,8 +451,12 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
}
// Check for abundant positional arguments
if (optind < argc)
log_fatal("compton doesn't accept positional arguments.");
if (optind < argc) {
// log is not initialized here yet
fprintf(stderr, "compton doesn't accept positional arguments.\n");
*exit_code = 1;
return true;
}
return false;
}
@ -545,15 +497,22 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// clang-format off
// Short options
case 'h': usage(0); break;
case 318:
case 'h':
// These options should cause compton to exit early,
// so assert(false) here
assert(false);
break;
case 'd':
case 'b':
case 'S':
case 314:
case 318:
case 320: break;
case 320:
// These options are handled by get_early_config()
break;
P_CASELONG('D', fade_delta);
case 'I': opt->fade_in_step = normalize_d(atof(optarg)) * OPAQUE; break;
case 'O': opt->fade_out_step = normalize_d(atof(optarg)) * OPAQUE; break;
case 'I': opt->fade_in_step = normalize_d(atof(optarg)); break;
case 'O': opt->fade_out_step = normalize_d(atof(optarg)); break;
case 'c': shadow_enable = true; break;
case 'C':
winopt_mask[WINTYPE_DOCK].shadow = true;
@ -582,7 +541,7 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
P_CASELONG('l', shadow_offset_x);
P_CASELONG('t', shadow_offset_y);
case 'i':
opt->inactive_opacity = (normalize_d(atof(optarg)) * OPAQUE);
opt->inactive_opacity = normalize_d(atof(optarg));
break;
case 'e': opt->frame_opacity = atof(optarg); break;
case 'z':
@ -596,7 +555,6 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
case 's':
log_error("-n, -a, and -s have been removed.");
break;
P_CASEBOOL('b', fork_after_register);
// Long options
case 256:
// --config
@ -630,10 +588,15 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
P_CASEBOOL(268, detect_client_opacity);
P_CASELONG(269, refresh_rate);
case 270:
// --vsync
opt->vsync = parse_vsync(optarg);
if (opt->vsync >= NUM_VSYNC)
exit(1);
if (optarg) {
opt->vsync = parse_vsync(optarg);
log_warn("--vsync doesn't take argument anymore. \"%s\" "
"is interpreted as \"%s\" for compatibility, but "
"this will stop working soon",
optarg, opt->vsync ? "true" : "false");
} else {
opt->vsync = true;
}
break;
case 271:
// --alpha-step
@ -646,7 +609,11 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
"when possible");
break;
P_CASEBOOL(274, sw_opti);
P_CASEBOOL(275, vsync_aggressive);
case 275:
// --vsync-aggressive
log_warn("--vsync-aggressive has been deprecated, please remove it"
" from the command line options");
break;
P_CASEBOOL(276, use_ewmh_active_win);
P_CASEBOOL(277, respect_prop_shadow);
P_CASEBOOL(278, unredir_if_possible);
@ -685,7 +652,8 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
break;
P_CASEBOOL(291, glx_no_stencil);
case 292:
log_warn("--glx-copy-from-front %s", deprecation_message);
log_error("--glx-copy-from-front %s", deprecation_message);
exit(1);
break;
P_CASELONG(293, benchmark);
case 294:
@ -693,7 +661,8 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
opt->benchmark_wid = strtol(optarg, NULL, 0);
break;
case 295:
log_warn("--glx-use-copysubbuffermesa %s", deprecation_message);
log_error("--glx-use-copysubbuffermesa %s", deprecation_message);
exit(1);
break;
case 296:
// --blur-background-exclude
@ -701,27 +670,47 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
break;
case 297:
// --active-opacity
opt->active_opacity = (normalize_d(atof(optarg)) * OPAQUE);
opt->active_opacity = normalize_d(atof(optarg));
break;
P_CASEBOOL(298, glx_no_rebind_pixmap);
case 299:
case 299: {
// --glx-swap-method
opt->glx_swap_method = parse_glx_swap_method(optarg);
if (opt->glx_swap_method == -2)
exit(1);
char *endptr;
long tmpval = strtol(optarg, &endptr, 10);
bool should_remove = true;
if (*endptr || !(*optarg)) {
// optarg is not a number, or an empty string
tmpval = -1;
}
if (strcmp(optarg, "undefined") != 0 && tmpval != 0) {
// If not undefined, we will use damage and buffer-age to
// limit the rendering area.
opt->use_damage = true;
should_remove = false;
}
log_warn("--glx-swap-method has been deprecated, your setting "
"\"%s\" should be %s.",
optarg,
!should_remove ? "replaced by `--use-damage`" :
"removed");
break;
}
case 300:
// --fade-exclude
condlst_add(&opt->fade_blacklist, optarg);
break;
case 301:
// --blur-kern
if (!parse_conv_kern_lst(optarg, opt->blur_kerns,
if (!parse_blur_kern_lst(optarg, opt->blur_kerns,
MAX_BLUR_PASS, &conv_kern_hasneg))
exit(1);
break;
P_CASELONG(302, resize_damage);
P_CASEBOOL(303, glx_use_gpushader4);
case 303:
// --glx-use-gpushader4
log_warn("--glx-use-gpushader4 is deprecated since v6."
" Please remove it from command line options.");
break;
case 304:
// --opacity-rule
if (!parse_rule_opacity(&opt->opacity_rules, optarg))
@ -773,8 +762,8 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
break;
}
P_CASEBOOL(319, no_x_selection);
P_CASEBOOL(731, reredir_on_root_change);
P_CASEBOOL(732, glx_reinit_on_root_change);
P_CASEBOOL(323, use_damage);
P_CASEBOOL(733, experimental_backends);
P_CASEBOOL(800, monitor_repaint);
case 801: opt->print_diagnostics = true; break;
default: usage(1); break;
@ -823,35 +812,18 @@ void get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// Fill default blur kernel
if (opt->blur_background && !opt->blur_kerns[0]) {
// Convolution filter parameter (box blur)
// gaussian or binomial filters are definitely superior, yet looks
// like they aren't supported as of xorg-server-1.13.0
static const xcb_render_fixed_t convolution_blur[] = {
// Must convert to XFixed with DOUBLE_TO_XFIXED()
// Matrix size
DOUBLE_TO_XFIXED(3),
DOUBLE_TO_XFIXED(3),
// Matrix
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
DOUBLE_TO_XFIXED(1),
};
opt->blur_kerns[0] = ccalloc(ARR_SIZE(convolution_blur), xcb_render_fixed_t);
memcpy(opt->blur_kerns[0], convolution_blur, sizeof(convolution_blur));
CHECK(parse_blur_kern_lst("3x3box", opt->blur_kerns, MAX_BLUR_PASS,
&conv_kern_hasneg));
}
if (opt->resize_damage < 0)
if (opt->resize_damage < 0) {
log_warn("Negative --resize-damage will not work correctly.");
}
if (opt->backend == BKEND_XRENDER && conv_kern_hasneg)
if (opt->backend == BKEND_XRENDER && conv_kern_hasneg) {
log_warn("A convolution kernel with negative values may not work "
"properly under X Render backend.");
}
}
// vim: set noet sw=8 ts=8 :

View File

@ -17,7 +17,7 @@ typedef struct session session_t;
/// Get config options that are needed to parse the rest of the options
/// Return true if we should quit
bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors,
int *exit_code);
bool *fork, int *exit_code);
/**
* Process arguments and configuration files.

View File

@ -3,54 +3,90 @@
#include <stdlib.h>
#include <string.h>
#include <xcb/xcb_image.h>
#include <xcb/composite.h>
#include <xcb/sync.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xcb_image.h>
#include <xcb/xcb_renderutil.h>
#include "common.h"
#include "options.h"
#ifdef CONFIG_OPENGL
#include <GL/glx.h>
#include "backend/gl/glx.h"
#include "opengl.h"
#endif
#include "vsync.h"
#include "win.h"
#include "kernel.h"
#include "compiler.h"
#include "x.h"
#include "config.h"
#include "region.h"
#include "kernel.h"
#include "log.h"
#include "region.h"
#include "types.h"
#include "utils.h"
#include "vsync.h"
#include "win.h"
#include "x.h"
#include "backend/backend_common.h"
#include "render.h"
#ifdef 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) {
static inline bool
paint_bind_tex(session_t *ps, paint_t *ppaint, unsigned wid, unsigned hei, bool repeat,
int depth, xcb_visualid_t visual, bool force) {
#ifdef CONFIG_OPENGL
// XXX This is a mess. But this will go away after the backend refactor.
static thread_local struct glx_fbconfig_info *argb_fbconfig = NULL;
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);
struct glx_fbconfig_info *fbcfg;
if (!visual) {
assert(depth == 32);
if (!argb_fbconfig) {
argb_fbconfig =
glx_find_fbconfig(ps->dpy, ps->scr,
(struct xvisual_info){.red_size = 8,
.green_size = 8,
.blue_size = 8,
.alpha_size = 8,
.visual_depth = 32});
}
if (!argb_fbconfig) {
log_error("Failed to find appropriate FBConfig for 32 bit depth");
return false;
}
fbcfg = argb_fbconfig;
} else {
auto m = x_get_visual_info(ps->c, visual);
if (m.visual_depth < 0) {
return false;
}
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;
}
if (depth && depth != m.visual_depth) {
log_error("Mismatching visual depth: %d != %d", depth, m.visual_depth);
return false;
}
if (!ppaint->fbcfg) {
ppaint->fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, m);
}
if (!ppaint->fbcfg) {
log_error("Failed to find appropriate FBConfig for X pixmap");
return false;
}
fbcfg = ppaint->fbcfg;
}
if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))
return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei,
repeat, fbcfg);
#endif
return true;
}
/**
* Check if current backend uses XRender for rendering.
@ -59,6 +95,33 @@ static inline bool bkend_use_xrender(session_t *ps) {
return BKEND_XRENDER == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
}
int maximum_buffer_age(session_t *ps) {
if (bkend_use_glx(ps) && ps->o.use_damage) {
return CGLX_MAX_BUFFER_AGE;
}
return 1;
}
static int get_buffer_age(session_t *ps) {
#ifdef CONFIG_OPENGL
if (bkend_use_glx(ps)) {
if (!glxext.has_GLX_EXT_buffer_age && ps->o.use_damage) {
log_warn("GLX_EXT_buffer_age not supported by your driver,"
"`use-damage` has to be disabled");
ps->o.use_damage = false;
}
if (ps->o.use_damage) {
unsigned int val;
glXQueryDrawable(ps->dpy, get_tgt_window(ps),
GLX_BACK_BUFFER_AGE_EXT, &val);
return (int)val ?: -1;
}
return -1;
}
#endif
return ps->o.use_damage ? 1 : -1;
}
/**
* Reset filter on a <code>Picture</code>.
*/
@ -68,11 +131,12 @@ static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
#undef FILTER
}
/// Set the input/output clip region of the target buffer (not the actual target!)
static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg) {
switch (ps->o.backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID:
x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0, reg);
x_set_picture_clip_region(ps->c, ps->tgt_buffer.pict, 0, 0, reg);
break;
#ifdef CONFIG_OPENGL
case BKEND_GLX: glx_set_clip(ps, reg); break;
@ -173,18 +237,20 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
* Paint a window itself and dim it if asked.
*/
void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
glx_mark(ps, w->id, true);
// Fetch Pixmap
if (!w->paint.pixmap && ps->has_name_pixmap) {
if (!w->paint.pixmap) {
w->paint.pixmap = xcb_generate_id(ps->c);
set_ignore_cookie(
ps, xcb_composite_name_window_pixmap(ps->c, w->id, w->paint.pixmap));
}
xcb_drawable_t draw = w->paint.pixmap;
if (!draw)
draw = w->id;
if (!draw) {
log_error("Failed to get pixmap from window %#010x (%s), window won't be "
"visible",
w->id, w->name);
return;
}
// XRender: Build picture
if (bkend_use_xrender(ps) && !w->paint.pict) {
@ -193,14 +259,14 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
};
w->paint.pict = x_create_picture_with_pictfmt_and_pixmap(
ps, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
ps->c, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
}
// GLX: Build texture
// Let glx_bind_pixmap() determine pixmap size, because if the user
// is resizing windows, the width and height we get may not be up-to-date,
// causing the jittering issue M4he reported in #7.
if (!paint_bind_tex(ps, &w->paint, 0, 0, 0,
if (!paint_bind_tex(ps, &w->paint, 0, 0, false, 0, w->a.visual,
(!ps->o.glx_no_rebind_pixmap && w->pixmap_damaged))) {
log_error("Failed to bind texture for window %#010x.", w->id);
}
@ -220,8 +286,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
// Invert window color, if required
if (bkend_use_xrender(ps) && w->invert_color) {
xcb_render_picture_t newpict =
x_create_picture_with_pictfmt(ps, wid, hei, w->pictfmt, 0, NULL);
xcb_render_picture_t newpict = x_create_picture_with_pictfmt(
ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL);
if (newpict) {
// Apply clipping region to save some CPU
if (reg_paint) {
@ -229,7 +295,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
pixman_region32_init(&reg);
pixman_region32_copy(&reg, (region_t *)reg_paint);
pixman_region32_translate(&reg, -x, -y);
// FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0, 0, reg);
// FIXME XFixesSetPictureClipRegion(ps->dpy, newpict, 0,
// 0, reg);
pixman_region32_fini(&reg);
}
@ -248,10 +315,8 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
}
}
const double dopacity = get_opacity_percent(w);
if (w->frame_opacity == 1) {
paint_region(ps, w, 0, 0, wid, hei, dopacity, reg_paint, pict);
paint_region(ps, w, 0, 0, wid, hei, w->opacity, reg_paint, pict);
} else {
// Painting parameters
const margin_t extents = win_calc_frame_extents(w);
@ -261,7 +326,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
const int r = extents.right;
#define COMP_BDR(cx, cy, cwid, chei) \
paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity *dopacity, \
paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \
reg_paint, pict)
// Sanitize the margins, in case some broken WM makes
@ -313,7 +378,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
// body
paint_region(ps, w, cleft, ctop, body_width, body_height,
dopacity, reg_paint, pict);
w->opacity, reg_paint, pict);
} while (0);
}
@ -326,7 +391,7 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
if (w->dim) {
double dim_opacity = ps->o.inactive_dim;
if (!ps->o.inactive_dim_fixed)
dim_opacity *= get_opacity_percent(w);
dim_opacity *= w->opacity;
switch (ps->o.backend) {
case BKEND_XRENDER:
@ -360,8 +425,6 @@ void paint_one(session_t *ps, win *w, const region_t *reg_paint) {
default: assert(false);
}
}
glx_mark(ps, w->id, false);
}
extern const char *background_props_str[];
@ -376,29 +439,15 @@ static bool get_root_tile(session_t *ps) {
ps->root_tile_fill = false;
bool fill = false;
xcb_pixmap_t pixmap = XCB_NONE;
// Get the values of background attributes
for (int p = 0; background_props_str[p]; p++) {
winprop_t prop =
wid_get_prop(ps, ps->root, get_atom(ps, background_props_str[p]), 1L,
XCB_ATOM_PIXMAP, 32);
if (prop.nitems) {
pixmap = *prop.p32;
fill = false;
free_winprop(&prop);
break;
}
free_winprop(&prop);
}
xcb_pixmap_t pixmap = x_get_root_back_pixmap(ps);
// Make sure the pixmap we got is valid
if (pixmap && !x_validate_pixmap(ps, pixmap))
if (pixmap && !x_validate_pixmap(ps->c, pixmap))
pixmap = XCB_NONE;
// Create a pixmap if there isn't any
if (!pixmap) {
pixmap = x_create_pixmap(ps, ps->depth, ps->root, 1, 1);
pixmap = x_create_pixmap(ps->c, ps->depth, ps->root, 1, 1);
if (pixmap == XCB_NONE) {
log_error("Failed to create pixmaps for root tile.");
return false;
@ -411,7 +460,7 @@ static bool get_root_tile(session_t *ps) {
.repeat = true,
};
ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap(
ps, ps->vis, pixmap, XCB_RENDER_CP_REPEAT, &pa);
ps->c, ps->vis, pixmap, XCB_RENDER_CP_REPEAT, &pa);
// Fill pixmap if needed
if (fill) {
@ -432,8 +481,7 @@ static bool get_root_tile(session_t *ps) {
ps->root_tile_paint.pixmap = pixmap;
#ifdef CONFIG_OPENGL
if (BKEND_GLX == ps->o.backend)
return glx_bind_pixmap(ps, &ps->root_tile_paint.ptex,
ps->root_tile_paint.pixmap, 0, 0, 0);
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, ps->vis, false);
#endif
return true;
@ -465,17 +513,16 @@ static bool win_build_shadow(session_t *ps, win *w, double opacity) {
xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE;
xcb_gcontext_t gc = XCB_NONE;
shadow_image =
make_shadow(ps->c, ps->gaussian_map, opacity, width, height);
shadow_image = make_shadow(ps->c, ps->gaussian_map, opacity, width, height);
if (!shadow_image) {
log_error("failed to make shadow");
return XCB_NONE;
}
shadow_pixmap =
x_create_pixmap(ps, 8, ps->root, shadow_image->width, shadow_image->height);
x_create_pixmap(ps->c, 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);
x_create_pixmap(ps->c, 32, ps->root, shadow_image->width, shadow_image->height);
if (!shadow_pixmap || !shadow_pixmap_argb) {
log_error("failed to create shadow pixmaps");
@ -483,9 +530,9 @@ static bool win_build_shadow(session_t *ps, win *w, double opacity) {
}
shadow_picture = x_create_picture_with_standard_and_pixmap(
ps, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
ps->c, 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);
ps->c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
if (!shadow_picture || !shadow_picture_argb)
goto shadow_picture_err;
@ -531,7 +578,7 @@ shadow_picture_err:
*/
static inline void win_paint_shadow(session_t *ps, win *w, region_t *reg_paint) {
// Bind shadow pixmap to GLX texture if needed
paint_bind_tex(ps, &w->shadow_paint, 0, 0, 32, false);
paint_bind_tex(ps, &w->shadow_paint, 0, 0, false, 32, 0, false);
if (!paint_isvalid(ps, &w->shadow_paint)) {
log_error("Window %#010x is missing shadow data.", w->id);
@ -543,18 +590,6 @@ static inline void win_paint_shadow(session_t *ps, win *w, region_t *reg_paint)
w->shadow_paint.ptex, reg_paint, NULL);
}
/**
* 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);
}
/**
* @brief Blur an area on a buffer.
*
@ -570,15 +605,14 @@ static inline void normalize_conv_kern(int wid, int hei, xcb_render_fixed_t *ker
*
* @return true if successful, false otherwise
*/
static bool
xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wid,
int hei, xcb_render_fixed_t **blur_kerns, const region_t *reg_clip) {
static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wid,
int hei, xcb_render_fixed_t **blur_kerns, const region_t *reg_clip) {
assert(blur_kerns[0]);
// Directly copying from tgt_buffer to it does not work, so we create a
// Picture in the middle.
xcb_render_picture_t tmp_picture =
x_create_picture_with_pictfmt(ps, wid, hei, NULL, 0, NULL);
x_create_picture_with_visual(ps->c, ps->root, wid, hei, ps->vis, 0, NULL);
if (!tmp_picture) {
log_error("Failed to build intermediate Picture.");
@ -586,14 +620,15 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int x, int y, int wi
}
if (reg_clip && tmp_picture)
x_set_picture_clip_region(ps, tmp_picture, 0, 0, reg_clip);
x_set_picture_clip_region(ps->c, tmp_picture, 0, 0, reg_clip);
xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture;
for (int i = 0; blur_kerns[i]; ++i) {
assert(i < MAX_BLUR_PASS - 1);
xcb_render_fixed_t *convolution_blur = blur_kerns[i];
int kwid = XFIXED_TO_DOUBLE(convolution_blur[0]),
khei = XFIXED_TO_DOUBLE(convolution_blur[1]);
// `x / 65536.0` converts from X fixed point to double
int kwid = ((double)convolution_blur[0]) / 65536.0,
khei = ((double)convolution_blur[1]) / 65536.0;
bool rd_from_tgt = (tgt_buffer == src_pict);
// Copy from source picture to destination. The filter must
@ -638,7 +673,7 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture
// Adjust blur strength according to window opacity, to make it appear
// better during fading
if (!ps->o.blur_background_fixed) {
double pct = 1.0 - get_opacity_percent(w) * (1.0 - 1.0 / 9.0);
double pct = 1.0 - w->opacity * (1.0 - 1.0 / 9.0);
factor_center = pct * 8.0 / (1.1 - pct);
}
@ -647,7 +682,9 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture
case BKEND_XR_GLX_HYBRID: {
// Normalize blur kernels
for (int i = 0; i < MAX_BLUR_PASS; ++i) {
xcb_render_fixed_t *kern_src = ps->o.blur_kerns[i];
// Note: `x * 65536` converts double `x` to a X fixed point
// representation. `x / 65536` is the other way.
auto kern_src = ps->o.blur_kerns[i];
xcb_render_fixed_t *kern_dst = ps->blur_kerns_cache[i];
assert(i < MAX_BLUR_PASS);
if (!kern_src) {
@ -655,30 +692,19 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture
break;
}
assert(!kern_dst ||
(kern_src[0] == kern_dst[0] && kern_src[1] == kern_dst[1]));
assert(!kern_dst || (kern_src->w == kern_dst[0] / 65536 &&
kern_src->h == kern_dst[1] / 65536));
// Skip for fixed factor_center if the cache exists already
if (ps->o.blur_background_fixed && kern_dst)
if (ps->o.blur_background_fixed && kern_dst) {
continue;
int kwid = XFIXED_TO_DOUBLE(kern_src[0]),
khei = XFIXED_TO_DOUBLE(kern_src[1]);
// Allocate cache space if needed
if (!kern_dst) {
kern_dst = ccalloc(kwid * khei + 2, xcb_render_fixed_t);
ps->blur_kerns_cache[i] = kern_dst;
}
// Modify the factor of the center pixel
kern_src[2 + (khei / 2) * kwid + kwid / 2] =
DOUBLE_TO_XFIXED(factor_center);
// Copy over
memcpy(kern_dst, kern_src,
(kwid * khei + 2) * sizeof(xcb_render_fixed_t));
normalize_conv_kern(kwid, khei, kern_dst + 2);
// If kern_dst is allocated, it's always allocated to the right
// size
size_t size = kern_dst ? kern_src->w * kern_src->h + 2 : 0;
x_picture_filter_from_conv(kern_src, factor_center, &kern_dst, &size);
ps->blur_kerns_cache[i] = kern_dst;
}
// Minimize the region we try to blur, if the window itself is not
@ -708,12 +734,43 @@ static inline void win_blur_background(session_t *ps, win *w, xcb_render_picture
}
}
/**
* Resize a region.
*/
static inline void resize_region(region_t *region, short mod) {
if (!mod || !region)
return;
// Loop through all rectangles
int nrects;
int nnewrects = 0;
pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects);
auto newrects = ccalloc(nrects, pixman_box32_t);
for (int i = 0; i < nrects; i++) {
int x1 = rects[i].x1 - mod;
int y1 = rects[i].y1 - mod;
int x2 = rects[i].x2 + mod;
int y2 = rects[i].y2 + mod;
int wid = x2 - x1;
int hei = y2 - y1;
if (wid <= 0 || hei <= 0)
continue;
newrects[nnewrects] =
(pixman_box32_t){.x1 = x1, .x2 = x2, .y1 = y1, .y2 = y2};
++nnewrects;
}
pixman_region32_fini(region);
pixman_region32_init_rects(region, newrects, nnewrects);
free(newrects);
}
/// paint all windows
/// region = ??
/// region_real = the damage region
void paint_all(session_t *ps, region_t *region, const region_t *region_real, win *const t) {
void paint_all(session_t *ps, win *const t, bool ignore_damage) {
if (ps->o.xrender_sync_fence) {
if (!x_fence_sync(ps, ps->sync_fence)) {
if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) {
log_error("x_fence_sync failed, xrender-sync-fence will be "
"disabled from now on.");
xcb_sync_destroy_fence(ps->c, ps->sync_fence);
@ -722,30 +779,38 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
}
}
if (!region_real) {
region_real = region;
region_t region;
pixman_region32_init(&region);
int buffer_age = get_buffer_age(ps);
if (buffer_age == -1 || buffer_age > ps->ndamage || ignore_damage) {
pixman_region32_copy(&region, &ps->screen_reg);
} else {
for (int i = 0; i < get_buffer_age(ps); i++) {
const int curr = ((ps->damage - ps->damage_ring) + i) % ps->ndamage;
pixman_region32_union(&region, &region, &ps->damage_ring[curr]);
}
}
if (!pixman_region32_not_empty(&region)) {
return;
}
#ifdef DEBUG_REPAINT
static struct timespec last_paint = {0};
#endif
if (!region)
region_real = region = &ps->screen_reg;
else
// Remove the damaged area out of screen
pixman_region32_intersect(region, region, &ps->screen_reg);
if (ps->o.resize_damage > 0) {
resize_region(&region, ps->o.resize_damage);
}
#ifdef CONFIG_OPENGL
if (bkend_use_glx(ps))
glx_paint_pre(ps, region);
#endif
// Remove the damaged area out of screen
pixman_region32_intersect(&region, &region, &ps->screen_reg);
if (!paint_isvalid(ps, &ps->tgt_buffer)) {
if (!ps->tgt_buffer.pixmap) {
free_paint(ps, &ps->tgt_buffer);
ps->tgt_buffer.pixmap = x_create_pixmap(
ps, ps->depth, ps->root, ps->root_width, ps->root_height);
ps->c, ps->depth, ps->root, ps->root_width, ps->root_height);
if (ps->tgt_buffer.pixmap == XCB_NONE) {
log_fatal("Failed to allocate a screen-sized pixmap for"
"painting");
@ -755,30 +820,38 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
if (BKEND_GLX != ps->o.backend)
ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap(
ps, ps->vis, ps->tgt_buffer.pixmap, 0, 0);
ps->c, ps->vis, ps->tgt_buffer.pixmap, 0, 0);
}
if (BKEND_XRENDER == ps->o.backend) {
x_set_picture_clip_region(ps, ps->tgt_picture, 0, 0, region_real);
x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, &region);
}
#ifdef CONFIG_OPENGL
if (bkend_use_glx(ps)) {
ps->psglx->z = 0.0;
}
#endif
region_t reg_tmp, *reg_paint;
pixman_region32_init(&reg_tmp);
if (t) {
// Calculate the region upon which the root window is to be painted
// based on the ignore region of the lowest window, if available
pixman_region32_subtract(&reg_tmp, region, t->reg_ignore);
// Calculate the region upon which the root window is to be
// painted based on the ignore region of the lowest window, if
// available
pixman_region32_subtract(&reg_tmp, &region, t->reg_ignore);
reg_paint = &reg_tmp;
} else {
reg_paint = region;
reg_paint = &region;
}
set_tgt_clip(ps, reg_paint);
paint_root(ps, reg_paint);
// Windows are sorted from bottom to top
// Each window has a reg_ignore, which is the region obscured by all the windows
// on top of that window. This is used to reduce the number of pixels painted.
// Each window has a reg_ignore, which is the region obscured by all the
// windows on top of that window. This is used to reduce the number of
// pixels painted.
//
// Whether this is beneficial is to be determined XXX
for (win *w = t; w; w = w->prev_trans) {
@ -790,40 +863,40 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
if (!win_build_shadow(ps, w, 1))
log_error("build shadow failed");
// Shadow doesn't need to be painted underneath the body of
// the window Because no one can see it
pixman_region32_subtract(&reg_tmp, region, w->reg_ignore);
// Shadow doesn't need to be painted underneath the body
// of the windows above. Because no one can see it
pixman_region32_subtract(&reg_tmp, &region, 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(&reg_tmp, &reg_tmp,
&ps->shadow_exclude_reg);
// Might be worth while to crop the region to shadow border
// Might be worth while to crop the region to shadow
// border
pixman_region32_intersect_rect(
&reg_tmp, &reg_tmp, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, w->shadow_width, w->shadow_height);
// Mask out the body of the window from the shadow if needed
// Doing it here instead of in make_shadow() for saving GPU
// power and handling shaped windows (XXX unconfirmed)
// Mask out the body of the window from the shadow if
// needed Doing it here instead of in make_shadow() for
// saving GPU power and handling shaped windows (XXX
// unconfirmed)
if (!ps->o.wintype_option[w->window_type].full_shadow)
pixman_region32_subtract(&reg_tmp, &reg_tmp, &bshape);
#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.
// 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.
// 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(
&reg_tmp, &reg_tmp,
&ps->xinerama_scr_regs[w->xinerama_scr]);
#endif
// Detect if the region is empty before painting
if (pixman_region32_not_empty(&reg_tmp)) {
@ -832,10 +905,11 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
}
}
// Calculate the region based on the reg_ignore of the next (higher)
// window and the bounding region
// XXX XXX
pixman_region32_subtract(&reg_tmp, region, w->reg_ignore);
// Calculate the paint region based on the reg_ignore of the current
// window and its bounding region.
// Remember, reg_ignore is the union of all windows above the current
// window.
pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
pixman_region32_intersect(&reg_tmp, &reg_tmp, &bshape);
pixman_region32_fini(&bshape);
@ -855,6 +929,13 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
// Free up all temporary regions
pixman_region32_fini(&reg_tmp);
// Move the head of the damage ring
ps->damage = ps->damage - 1;
if (ps->damage < ps->damage_ring) {
ps->damage = ps->damage_ring + ps->ndamage - 1;
}
pixman_region32_clear(ps->damage);
// Do this as early as possible
set_tgt_clip(ps, &ps->screen_reg);
@ -873,50 +954,43 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
#endif
}
// Wait for VBlank. We could do it aggressively (send the painting
// request and XFlush() on VBlank) or conservatively (send the request
// only on VBlank).
if (!ps->o.vsync_aggressive)
vsync_wait(ps);
if (ps->vsync_wait) {
ps->vsync_wait(ps);
}
switch (ps->o.backend) {
case BKEND_XRENDER:
if (ps->o.monitor_repaint) {
// Copy the screen content to a new picture, and highlight
// the paint region. This is not very efficient, but since
// it's for debug only, we don't really care
// Copy the screen content to a new picture, and highlight the
// paint region. This is not very efficient, but since it's for
// debug only, we don't really care
// First, we clear tgt_buffer.pict's clip region, since we
// want to copy everything
x_set_picture_clip_region(ps, ps->tgt_buffer.pict, 0, 0,
&ps->screen_reg);
// Then we create a new picture, and copy content to it
xcb_render_pictforminfo_t *pictfmt =
x_get_pictform_for_visual(ps, ps->vis);
// First we create a new picture, and copy content from the buffer
// to it
auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis);
xcb_render_picture_t new_pict = x_create_picture_with_pictfmt(
ps, ps->root_width, ps->root_height, pictfmt, 0, NULL);
ps->c, ps->root, ps->root_width, ps->root_height, pictfmt, 0, NULL);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
ps->tgt_buffer.pict, XCB_NONE, new_pict, 0, 0,
0, 0, 0, 0, ps->root_width, ps->root_height);
// Next, we set the region of paint and highlight it
x_set_picture_clip_region(ps, new_pict, 0, 0, region_real);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER,
ps->white_picture,
x_set_picture_clip_region(ps->c, new_pict, 0, 0, &region);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture,
ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0, 0,
0, 0, 0, 0, ps->root_width, ps->root_height);
// Finally, clear clip region and put the whole thing on screen
x_set_picture_clip_region(ps, new_pict, 0, 0, &ps->screen_reg);
// Finally, clear clip regions of new_pict and the screen, and put
// the whole thing on screen
x_set_picture_clip_region(ps->c, new_pict, 0, 0, &ps->screen_reg);
x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, &ps->screen_reg);
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, new_pict,
XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0,
ps->root_width, ps->root_height);
xcb_render_free_picture(ps->c, new_pict);
} else
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
ps->tgt_buffer.pict, XCB_NONE,
ps->tgt_picture, 0, 0, 0, 0, 0, 0,
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->tgt_buffer.pict,
XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0,
ps->root_width, ps->root_height);
break;
#ifdef CONFIG_OPENGL
@ -929,25 +1003,21 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
glXWaitX();
assert(ps->tgt_buffer.pixmap);
paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height,
ps->depth, !ps->o.glx_no_rebind_pixmap);
false, ps->depth, ps->vis, !ps->o.glx_no_rebind_pixmap);
if (ps->o.vsync_use_glfinish)
glFinish();
else
glFlush();
glXWaitX();
glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
ps->root_height, 0, 1.0, false, false, region_real, NULL);
ps->root_height, 0, 1.0, false, false, &region, NULL);
// falls through
case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
#endif
default: assert(0);
}
glx_mark_frame(ps);
if (ps->o.vsync_aggressive)
vsync_wait(ps);
xcb_flush(ps->c);
x_sync(ps->c);
#ifdef CONFIG_OPENGL
if (glx_has_context(ps)) {
@ -967,6 +1037,9 @@ void paint_all(session_t *ps, region_t *region, const region_t *region_real, win
log_trace(" %#010lx", w->id);
#endif
// Free the paint region
pixman_region32_fini(&region);
// Check if fading is finished on all painted windows
{
win *pprev = NULL;
@ -1016,7 +1089,7 @@ static bool init_alpha_picts(session_t *ps) {
for (int i = 0; i <= MAX_ALPHA; ++i) {
double o = (double)i / MAX_ALPHA;
ps->alpha_picts[i] = solid_picture(ps, false, o, 0, 0, 0);
ps->alpha_picts[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0);
if (ps->alpha_picts[i] == XCB_NONE)
return false;
}
@ -1025,6 +1098,9 @@ static bool init_alpha_picts(session_t *ps) {
bool init_render(session_t *ps) {
// Initialize OpenGL as early as possible
#ifdef CONFIG_OPENGL
glxext_init(ps->dpy, ps->scr);
#endif
if (bkend_use_glx(ps)) {
#ifdef CONFIG_OPENGL
if (!glx_init(ps, true))
@ -1059,24 +1135,23 @@ bool init_render(session_t *ps) {
// Blur filter
if (ps->o.blur_background || ps->o.blur_background_frame) {
bool ret;
bool ret = false;
if (ps->o.backend == BKEND_GLX) {
#ifdef CONFIG_OPENGL
ret = glx_init_blur(ps);
#else
assert(false);
#endif
} else
} else {
ret = xr_init_blur(ps);
if (!ret)
return false;
}
if (!ret) {
return ret;
}
}
ps->gaussian_map = gaussian_kernel(ps->o.shadow_radius);
shadow_preprocess(ps->gaussian_map);
ps->black_picture = solid_picture(ps, true, 1, 0, 0, 0);
ps->white_picture = solid_picture(ps, true, 1, 1, 1, 1);
ps->black_picture = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0);
ps->white_picture = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1);
if (ps->black_picture == XCB_NONE || ps->white_picture == XCB_NONE) {
log_error("Failed to create solid xrender pictures.");
@ -1088,8 +1163,8 @@ bool init_render(session_t *ps) {
if (!ps->o.shadow_red && !ps->o.shadow_green && !ps->o.shadow_blue) {
ps->cshadow_picture = ps->black_picture;
} else {
ps->cshadow_picture = solid_picture(
ps, true, 1, ps->o.shadow_red, ps->o.shadow_green, ps->o.shadow_blue);
ps->cshadow_picture = solid_picture(ps->c, ps->root, true, 1, ps->o.shadow_red,
ps->o.shadow_green, ps->o.shadow_blue);
if (ps->cshadow_picture == XCB_NONE) {
log_error("Failed to create shadow picture.");
return false;
@ -1131,12 +1206,12 @@ void deinit_render(session_t *ps) {
free_picture(ps->c, &ps->black_picture);
free_picture(ps->c, &ps->white_picture);
free_conv(ps->gaussian_map);
// Free other X resources
free_root_tile(ps);
#ifdef CONFIG_OPENGL
free(ps->root_tile_paint.fbcfg);
glx_destroy(ps);
#endif
}

View File

@ -5,6 +5,9 @@
#include <xcb/xcb.h>
#include <xcb/render.h>
#include <stdbool.h>
#ifdef CONFIG_OPENGL
#include "backend/gl/glx.h"
#endif
#include "region.h"
typedef struct _glx_texture glx_texture_t;
@ -16,6 +19,9 @@ typedef struct paint {
xcb_pixmap_t pixmap;
xcb_render_picture_t pict;
glx_texture_t *ptex;
#ifdef CONFIG_OPENGL
struct glx_fbconfig_info *fbcfg;
#endif
} paint_t;
void
@ -27,7 +33,7 @@ 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);
paint_all(session_t *ps, win * const t, bool ignore_damage);
void free_picture(xcb_connection_t *c, xcb_render_picture_t *p);
@ -36,3 +42,5 @@ void free_root_tile(session_t *ps);
bool init_render(session_t *ps);
void deinit_render(session_t *ps);
int maximum_buffer_age(session_t *);

View File

@ -7,37 +7,79 @@
#include "string_utils.h"
#include "utils.h"
#pragma GCC diagnostic push
// gcc warns about legitimate strncpy in mstrjoin and mstrextend
// strncpy(str, src1, len1) intentional truncates the null byte from src1.
// strncpy(str+len1, src2, len2) uses bound depends on the source argument,
// but str is allocated with len1+len2+1, so this strncpy can't overflow
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wstringop-truncation"
#pragma GCC diagnostic ignored "-Wstringop-overflow"
/**
* Allocate the space and join two strings.
*/
char *mstrjoin(const char *src1, const char *src2) {
auto str = ccalloc(strlen(src1)+strlen(src2)+1, char);
auto len1 = strlen(src1);
auto len2 = strlen(src2);
auto len = len1 + len2 + 1;
auto str = ccalloc(len, char);
strcpy(str, src1);
strcat(str, src2);
strncpy(str, src1, len1);
strncpy(str + len1, src2, len2);
str[len - 1] = '\0';
return str;
}
/**
* Allocate the space and join two strings;
*/
char *
mstrjoin3(const char *src1, const char *src2, const char *src3) {
auto str = ccalloc(strlen(src1)+strlen(src2)+strlen(src3)+1, char);
strcpy(str, src1);
strcat(str, src2);
strcat(str, src3);
return str;
return str;
}
/**
* Concatenate a string on heap with another string.
*/
void mstrextend(char **psrc1, const char *src2) {
*psrc1 = crealloc(*psrc1, (*psrc1 ? strlen(*psrc1) : 0)+strlen(src2)+1);
if (!*psrc1) {
*psrc1 = strdup(src2);
return;
}
strcat(*psrc1, src2);
auto len1 = strlen(*psrc1);
auto len2 = strlen(src2);
auto len = len1 + len2 + 1;
*psrc1 = crealloc(*psrc1, len);
strncpy(*psrc1 + len1, src2, len2);
(*psrc1)[len - 1] = '\0';
}
#pragma GCC diagnostic pop
/// Parse a floating point number of form (+|-)?[0-9]*(\.[0-9]*)
double strtod_simple(const char *src, const char **end) {
double neg = 1;
if (*src == '-') {
neg = -1;
src++;
} else if (*src == '+') {
src++;
}
double ret = 0;
while (*src >= '0' && *src <= '9') {
ret = ret * 10 + (*src - '0');
src++;
}
if (*src == '.') {
double frac = 0, mult = 0.1;
src++;
while (*src >= '0' && *src <= '9') {
frac += mult * (*src - '0');
mult *= 0.1;
src++;
}
ret += frac;
}
*end = src;
return ret * neg;
}

View File

@ -7,48 +7,46 @@
#define mstrncmp(s1, s2) strncmp((s1), (s2), strlen(s1))
char *mstrjoin(const char *src1, const char *src2);
char *
mstrjoin3(const char *src1, const char *src2, const char *src3);
char *mstrjoin3(const char *src1, const char *src2, const char *src3);
void mstrextend(char **psrc1, const char *src2);
/// Parse a floating point number of form (+|-)?[0-9]*(\.[0-9]*)
double strtod_simple(const char *, const char **);
static inline int uitostr(unsigned int n, char *buf) {
int ret = 0;
unsigned int tmp = n;
while (tmp > 0) {
tmp /= 10;
ret++;
}
int ret = 0;
unsigned int tmp = n;
while (tmp > 0) {
tmp /= 10;
ret++;
}
if (ret == 0)
ret = 1;
if (ret == 0)
ret = 1;
int pos = ret;
while (pos--) {
buf[pos] = n%10 + '0';
n /= 10;
}
return ret;
int pos = ret;
while (pos--) {
buf[pos] = n % 10 + '0';
n /= 10;
}
return ret;
}
static inline const char *
skip_space_const(const char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
static inline const char *skip_space_const(const char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
}
static inline char *
skip_space_mut(char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
static inline char *skip_space_mut(char *src) {
if (!src)
return NULL;
while (*src && isspace(*src))
src++;
return src;
}
#define skip_space(x) _Generic((x), \
char *: skip_space_mut, \
const char *: skip_space_const \
)(x)
#define skip_space(x) \
_Generic((x), char * : skip_space_mut, const char * : skip_space_const)(x)

View File

@ -9,27 +9,28 @@
/// Enumeration type to represent switches.
typedef enum {
OFF, // false
ON, // true
UNSET
OFF = 0, // false
ON, // true
UNSET
} switch_t;
/// Structure representing a X geometry.
typedef struct {
int wid;
int hei;
int x;
int y;
int wid;
int hei;
int x;
int y;
} geometry_t;
/// A structure representing margins around a rectangle.
typedef struct {
unsigned int top;
unsigned int left;
unsigned int bottom;
unsigned int right;
unsigned int top;
unsigned int left;
unsigned int bottom;
unsigned int right;
} margin_t;
typedef uint32_t opacity_t;
#define MARGIN_INIT { 0, 0, 0, 0 }
#define MARGIN_INIT \
{ 0, 0, 0, 0 }

View File

@ -1,12 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -29,6 +30,22 @@ safe_isnan(double a) {
return isnan(a);
}
/// Same as assert false, but make sure we abort _even in release builds_.
/// Silence compiler warning caused by release builds making some code paths reachable.
#define BUG() \
do { \
assert(false); \
abort(); \
} while (0)
/// Same as assert, but evaluates the expression even in release builds
#define CHECK(expr) \
do { \
__auto_type __tmp = (expr); \
assert(__tmp); \
(void)__tmp; \
} while (0)
/**
* Normalize an int value to a specific range.
*
@ -73,6 +90,10 @@ static inline long attr_const min_l(long a, long b) {
return (a > b ? b : a);
}
static inline int attr_const popcountl(unsigned long a) {
return __builtin_popcountl(a);
}
/**
* Normalize a double value to a specific range.
*
@ -99,8 +120,8 @@ static inline double attr_const normalize_d(double d) {
return normalize_d_range(d, 0.0, 1.0);
}
attr_noret void report_allocation_failure(const char *func, const char *file,
unsigned int line);
attr_noret void
report_allocation_failure(const char *func, const char *file, unsigned int line);
/**
* @brief Quit if the passed-in pointer is empty.

View File

@ -3,22 +3,21 @@
/// Function pointers to init VSync modes.
#include <GL/glx.h>
#include "common.h"
#include "log.h"
#ifdef CONFIG_OPENGL
#include "backend/gl/glx.h"
#include "opengl.h"
#endif
#ifdef CONFIG_VSYNC_DRM
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <drm.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
#include "config.h"
@ -28,198 +27,109 @@
/**
* Wait for next VSync, DRM method.
*
* Stolen from: https://github.com/MythTV/mythtv/blob/master/mythtv/libs/libmythtv/vsync.cpp
* 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;
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;
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);
do {
ret = ioctl(ps->drm_fd, DRM_IOCTL_WAIT_VBLANK, &vbl);
vbl.request.type &= ~_DRM_VBLANK_RELATIVE;
} while (ret && errno == EINTR);
if (ret)
log_error("VBlank ioctl did not work, unimplemented in this drmver?");
return ret;
if (ret)
log_error("VBlank ioctl did not work, unimplemented in this drmver?");
return ret;
}
#endif
/**
* 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) {
log_error("Failed to open device.");
return false;
}
static bool vsync_drm_init(session_t *ps) {
// Should we always open card0?
if (ps->drm_fd < 0 && (ps->drm_fd = open("/dev/dri/card0", O_RDWR)) < 0) {
log_error("Failed to open device.");
return false;
}
if (vsync_drm_wait(ps))
return false;
if (vsync_drm_wait(ps))
return false;
return true;
#else
log_error("compton is not compiled with DRM VSync support.");
return false;
#endif
return true;
}
#endif
#ifdef CONFIG_OPENGL
/**
* Initialize OpenGL VSync.
*
* Stolen from: http://git.tuxfamily.org/?p=ccm/cairocompmgr.git;a=commitdiff;h=efa4ceb97da501e8630ca7f12c99b1dce853c73e
* 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;
static bool vsync_opengl_init(session_t *ps) {
if (!ensure_glx_context(ps))
return false;
if (!glx_hasglxext(ps, "GLX_SGI_video_sync")) {
log_error("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) {
log_error("Failed to get glXWait/GetVideoSyncSGI function.");
return false;
}
return true;
#else
log_error("compton is not compiled with OpenGL VSync support.");
return false;
#endif
return glxext.has_GLX_SGI_video_sync;
}
static bool
vsync_opengl_oml_init(session_t *ps) {
#ifdef CONFIG_OPENGL
if (!ensure_glx_context(ps))
return false;
static bool vsync_opengl_oml_init(session_t *ps) {
if (!ensure_glx_context(ps))
return false;
if (!glx_hasglxext(ps, "GLX_OML_sync_control")) {
log_error("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) {
log_error("Failed to get OML_sync_control functions.");
return false;
}
return true;
#else
log_error("compton is not compiled with OpenGL VSync support.");
return false;
#endif
return glxext.has_GLX_OML_sync_control;
}
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 {
log_error("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 inline bool vsync_opengl_swc_swap_interval(session_t *ps, unsigned int interval) {
if (glxext.has_GLX_MESA_swap_control)
return glXSwapIntervalMESA(interval) == 0;
else if (glxext.has_GLX_SGI_swap_control)
return glXSwapIntervalSGI(interval) == 0;
else if (glxext.has_GLX_EXT_swap_control) {
GLXDrawable d = glXGetCurrentDrawable();
if (d == None) {
// We don't have a context??
return false;
}
glXSwapIntervalEXT(ps->dpy, glXGetCurrentDrawable(), interval);
return true;
}
return false;
}
static bool
vsync_opengl_swc_init(session_t *ps) {
#ifdef CONFIG_OPENGL
if (!bkend_use_glx(ps)) {
log_warn("OpenGL swap control requires the GLX backend.");
return false;
}
static bool vsync_opengl_swc_init(session_t *ps) {
if (!bkend_use_glx(ps)) {
log_error("OpenGL swap control requires the GLX backend.");
return false;
}
if (!vsync_opengl_swc_swap_interval(ps, 1)) {
log_error("Failed to load a swap control extension.");
return false;
}
if (!vsync_opengl_swc_swap_interval(ps, 1)) {
log_error("Failed to load a swap control extension.");
return false;
}
return true;
#else
log_error("compton is not compiled with OpenGL VSync support.");
return false;
#endif
return true;
}
static bool
vsync_opengl_mswc_init(session_t *ps) {
log_warn("opengl-mswc is deprecated, please use opengl-swc instead.");
return vsync_opengl_swc_init(ps);
}
bool (*const VSYNC_FUNCS_INIT[NUM_VSYNC])(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_OPENGL
/**
* Wait for next VSync, OpenGL method.
*/
static int
vsync_opengl_wait(session_t *ps) {
unsigned vblank_count = 0;
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;
glXGetVideoSyncSGI(&vblank_count);
glXWaitVideoSyncSGI(2, (vblank_count + 1) % 2, &vblank_count);
return 0;
}
/**
@ -227,78 +137,68 @@ vsync_opengl_wait(session_t *ps) {
*
* 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;
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[NUM_VSYNC])(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);
glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc);
return 0;
}
#endif
/// Function pointers to deinitialize VSync.
void (*const VSYNC_FUNCS_DEINIT[NUM_VSYNC])(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);
#ifdef CONFIG_OPENGL
if (bkend_use_glx(ps)) {
// Mesa turns on swap control by default, undo that
vsync_opengl_swc_swap_interval(ps, 0);
}
#endif
#ifdef CONFIG_VSYNC_DRM
log_warn("The DRM vsync method is deprecated, please don't enable it.");
#endif
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);
if (!ps->o.vsync) {
return true;
}
#ifdef CONFIG_OPENGL
if (bkend_use_glx(ps)) {
if (!vsync_opengl_swc_init(ps)) {
return false;
}
ps->vsync_wait = NULL; // glXSwapBuffers will automatically wait
// for vsync, we don't need to do anything.
return true;
}
#endif
// Oh no, we are not using glx backend.
// Throwing things at wall.
#ifdef CONFIG_OPENGL
if (vsync_opengl_oml_init(ps)) {
log_info("Using the opengl-oml vsync method");
ps->vsync_wait = vsync_opengl_oml_wait;
return true;
}
if (vsync_opengl_init(ps)) {
log_info("Using the opengl vsync method");
ps->vsync_wait = vsync_opengl_wait;
return true;
}
#endif
#ifdef CONFIG_VSYNC_DRM
if (vsync_drm_init(ps)) {
log_info("Using the drm vsync method");
ps->vsync_wait = vsync_drm_wait;
return true;
}
#endif
log_error("No supported vsync method found for this backend");
return false;
}

View File

@ -5,5 +5,3 @@
typedef struct session session_t;
bool vsync_init(session_t *ps);
void vsync_wait(session_t *ps);
void vsync_deinit(session_t *ps);

2168
src/win.c

File diff suppressed because it is too large Load Diff

530
src/win.h
View File

@ -3,22 +3,22 @@
// Copyright (c) 2013 Richard Grenville <pyxlcy@gmail.com>
#pragma once
#include <stdbool.h>
#include <xcb/xcb.h>
#include <xcb/render.h>
#include <xcb/damage.h>
#include <xcb/render.h>
#include <xcb/xcb.h>
// FIXME shouldn't need this
#ifdef CONFIG_OPENGL
#include <GL/gl.h>
#endif
#include "x.h"
#include "c2.h"
#include "compiler.h"
#include "region.h"
#include "types.h"
#include "c2.h"
#include "render.h"
#include "types.h"
#include "utils.h"
#include "x.h"
typedef struct session session_t;
typedef struct _glx_texture glx_texture_t;
@ -27,43 +27,83 @@ typedef struct _glx_texture glx_texture_t;
// FIXME this type should be in opengl.h
// it is very unideal for it to be here
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;
/// 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;
} glx_blur_cache_t;
#endif
typedef enum {
WINTYPE_UNKNOWN,
WINTYPE_DESKTOP,
WINTYPE_DOCK,
WINTYPE_TOOLBAR,
WINTYPE_MENU,
WINTYPE_UTILITY,
WINTYPE_SPLASH,
WINTYPE_DIALOG,
WINTYPE_NORMAL,
WINTYPE_DROPDOWN_MENU,
WINTYPE_POPUP_MENU,
WINTYPE_TOOLTIP,
WINTYPE_NOTIFY,
WINTYPE_COMBO,
WINTYPE_DND,
NUM_WINTYPES
WINTYPE_UNKNOWN,
WINTYPE_DESKTOP,
WINTYPE_DOCK,
WINTYPE_TOOLBAR,
WINTYPE_MENU,
WINTYPE_UTILITY,
WINTYPE_SPLASH,
WINTYPE_DIALOG,
WINTYPE_NORMAL,
WINTYPE_DROPDOWN_MENU,
WINTYPE_POPUP_MENU,
WINTYPE_TOOLTIP,
WINTYPE_NOTIFY,
WINTYPE_COMBO,
WINTYPE_DND,
NUM_WINTYPES
} wintype_t;
/// Enumeration type of window painting mode.
typedef enum {
WMODE_TRANS, // The window body is (potentially) transparent
WMODE_FRAME_TRANS, // The window body is opaque, but the frame is not
WMODE_SOLID, // The window is opaque including the frame
WMODE_TRANS, // The window body is (potentially) transparent
WMODE_FRAME_TRANS, // The window body is opaque, but the frame is not
WMODE_SOLID, // The window is opaque including the frame
} winmode_t;
/// Transition table:
/// (DESTROYED is when the win struct is destroyed and freed)
/// ('o' means in all other cases)
/// (Window is created in the UNMAPPED state)
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | |UNMAPPING|DESTROYING|MAPPING|FADING |UNMAPPED| MAPPED |DESTROYED|
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | UNMAPPING | o | Window |Window | - | Fading | - | - |
/// | | |destroyed |mapped | |finished| | |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | DESTROYING | - | o | - | - | - | - | Fading |
/// | | | | | | | |finished |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | MAPPING | Window | Window | o | - | - | Fading | - |
/// | |unmapped |destroyed | | | |finished| |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | FADING | Window | Window | - | o | - | Fading | - |
/// | |unmapped |destroyed | | | |finished| |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | UNMAPPED | - | - |Window | - | o | - | Window |
/// | | | |mapped | | | |destroyed|
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | MAPPED | Window | Window | - |Opacity| - | o | - |
/// | |unmapped |destroyed | |change | | | |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+
typedef enum {
// The window is being faded out because it's unmapped.
WSTATE_UNMAPPING,
// The window is being faded out because it's destroyed,
WSTATE_DESTROYING,
// The window is being faded in
WSTATE_MAPPING,
// Window opacity is not at the target level
WSTATE_FADING,
// The window is mapped, no fading is in progress.
WSTATE_MAPPED,
// The window is unmapped, no fading is in progress.
WSTATE_UNMAPPED,
} winstate_t;
/**
* About coordinate systems
*
@ -71,204 +111,185 @@ typedef enum {
* X goes from left to right, Y goes downwards.
*
* Global: the origin is the top left corner of the Xorg screen.
* Local: the origin is the top left corner of the window, including border.
* Local: the origin is the top left corner of the window, border is
* considered part of the window.
*/
/// 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.
win *prev_trans;
/// backend data attached to this window. Only available when
/// `state` is not UNMAPPED
void *win_image;
void *shadow_image;
/// Pointer to the next lower window in window stack.
win *next;
/// Pointer to the next higher window to paint.
win *prev_trans;
// TODO rethink reg_ignore
// Core members
/// ID of the top-level frame window.
xcb_window_t id;
/// Window attributes.
xcb_get_window_attributes_reply_t a;
xcb_get_geometry_reply_t g;
#ifdef CONFIG_XINERAMA
/// Xinerama screen this window is on.
int xinerama_scr;
#endif
/// Window visual pict format;
xcb_render_pictforminfo_t *pictfmt;
/// Window painting mode.
winmode_t mode;
/// Whether the window has been damaged at least once.
bool ever_damaged;
/// Whether the window was damaged after last paint.
bool pixmap_damaged;
/// Damage of the window.
xcb_damage_damage_t damage;
/// Paint info of the window.
paint_t paint;
// Core members
/// ID of the top-level frame window.
xcb_window_t id;
/// The "mapped state" of this window, doesn't necessary
/// match X mapped state, because of fading.
winstate_t state;
/// Window attributes.
xcb_get_window_attributes_reply_t a;
xcb_get_geometry_reply_t g;
/// Xinerama screen this window is on.
int xinerama_scr;
/// Window visual pict format;
const xcb_render_pictforminfo_t *pictfmt;
/// Window painting mode.
winmode_t mode;
/// Whether the window has been damaged at least once.
bool ever_damaged;
/// Whether the window was damaged after last paint.
bool pixmap_damaged;
/// Damage of the window.
xcb_damage_damage_t damage;
/// Paint info of the window.
paint_t paint;
/// Bounding shape of the window. In local coordinates.
/// See above about coordinate systems.
region_t bounding_shape;
/// Window flags. Definitions above.
int_fast16_t flags;
/// Whether there's a pending <code>ConfigureNotify</code> happening
/// when the window is unmapped.
bool need_configure;
/// Queued <code>ConfigureNotify</code> when the window is unmapped.
xcb_configure_notify_event_t queue_configure;
/// The region of screen that will be obscured when windows above is painted,
/// in global coordinates.
/// We use this to reduce the pixels that needed to be paint when painting
/// this window and anything underneath. Depends on window frame
/// opacity state, window geometry, window mapped/unmapped state,
/// window mode of the windows above. DOES NOT INCLUDE the body of THIS WINDOW.
/// NULL means reg_ignore has not been calculated for this window.
rc_region_t *reg_ignore;
/// Whether the reg_ignore of all windows beneath this window are valid
bool reg_ignore_valid;
/// Cached width/height of the window including border.
int widthb, heightb;
/// Whether the window has been destroyed.
bool destroyed;
/// Whether the window is bounding-shaped.
bool bounding_shaped;
/// Whether the window just have rounded corners.
bool rounded_corners;
/// Whether this window is to be painted.
bool to_paint;
/// Whether the window is painting excluded.
bool paint_excluded;
/// Whether the window is unredirect-if-possible excluded.
bool unredir_if_possible_excluded;
/// Whether this window is in open/close state.
bool in_openclose;
/// Bounding shape of the window. In local coordinates.
/// See above about coordinate systems.
region_t bounding_shape;
/// Window flags. Definitions above.
int_fast16_t flags;
/// Whether there's a pending <code>ConfigureNotify</code> happening
/// when the window is unmapped.
bool need_configure;
/// Queued <code>ConfigureNotify</code> when the window is unmapped.
xcb_configure_notify_event_t queue_configure;
/// The region of screen that will be obscured when windows above is painted,
/// in global coordinates.
/// We use this to reduce the pixels that needed to be paint when painting
/// this window and anything underneath. Depends on window frame
/// opacity state, window geometry, window mapped/unmapped state,
/// window mode of the windows above. DOES NOT INCLUDE the body of THIS WINDOW.
/// NULL means reg_ignore has not been calculated for this window.
rc_region_t *reg_ignore;
/// Whether the reg_ignore of all windows beneath this window are valid
bool reg_ignore_valid;
/// Cached width/height of the window including border.
int widthb, heightb;
/// Whether the window is bounding-shaped.
bool bounding_shaped;
/// Whether the window just have rounded corners.
bool rounded_corners;
/// Whether this window is to be painted.
bool to_paint;
/// Whether the window is painting excluded.
bool paint_excluded;
/// Whether the window is unredirect-if-possible excluded.
bool unredir_if_possible_excluded;
/// Whether this window is in open/close state.
bool in_openclose;
// Client window related members
/// ID of the top-level client window of the window.
xcb_window_t client_win;
/// Type of the window.
wintype_t window_type;
/// Whether it looks like a WM window. We consider a window WM window if
/// it does not have a decedent with WM_STATE and it is not override-
/// redirected itself.
bool wmwin;
/// Leader window ID of the window.
xcb_window_t leader;
/// Cached topmost window ID of the window.
xcb_window_t cache_leader;
// Client window related members
/// ID of the top-level client window of the window.
xcb_window_t client_win;
/// Type of the window.
wintype_t window_type;
/// Whether it looks like a WM window. We consider a window WM window if
/// it does not have a decedent with WM_STATE and it is not override-
/// redirected itself.
bool wmwin;
/// Leader window ID of the window.
xcb_window_t leader;
/// Cached topmost window ID of the window.
xcb_window_t cache_leader;
// Focus-related members
/// Whether the window is to be considered focused.
bool focused;
/// Override value of window focus state. Set by D-Bus method calls.
switch_t focused_force;
// Focus-related members
/// Whether the window is to be considered focused.
bool focused;
/// Override value of window focus state. Set by D-Bus method calls.
switch_t focused_force;
// Blacklist related members
/// Name of the window.
char *name;
/// Window instance class of the window.
char *class_instance;
/// Window general class of the window.
char *class_general;
/// <code>WM_WINDOW_ROLE</code> value of the window.
char *role;
const c2_lptr_t *cache_sblst;
const c2_lptr_t *cache_fblst;
const c2_lptr_t *cache_fcblst;
const c2_lptr_t *cache_ivclst;
const c2_lptr_t *cache_bbblst;
const c2_lptr_t *cache_oparule;
const c2_lptr_t *cache_pblst;
const c2_lptr_t *cache_uipblst;
// Blacklist related members
/// Name of the window.
char *name;
/// Window instance class of the window.
char *class_instance;
/// Window general class of the window.
char *class_general;
/// <code>WM_WINDOW_ROLE</code> value of the window.
char *role;
// Opacity-related members
/// Current window opacity.
opacity_t opacity;
/// Target window opacity.
opacity_t opacity_tgt;
/// true if window (or client window, for broken window managers
/// not transferring client window's _NET_WM_OPACITY value) has opacity prop
bool has_opacity_prop;
/// Cached value of opacity window attribute.
opacity_t opacity_prop;
/// true if opacity is set by some rules
bool opacity_is_set;
/// Last window opacity value we set.
opacity_t opacity_set;
// Opacity-related members
/// Current window opacity.
double opacity;
/// Target window opacity.
double opacity_tgt;
/// true if window (or client window, for broken window managers
/// not transferring client window's _NET_WM_OPACITY value) has opacity prop
bool has_opacity_prop;
/// Cached value of opacity window attribute.
opacity_t opacity_prop;
/// true if opacity is set by some rules
bool opacity_is_set;
/// Last window opacity value we set.
double opacity_set;
// Fading-related members
/// Do not fade if it's false. Change on window type change.
/// Used by fading blacklist in the future.
bool fade;
/// Fade state on last paint.
bool fade_last;
/// Override value of window fade state. Set by D-Bus method calls.
switch_t fade_force;
/// Callback to be called after fading completed.
void (*fade_callback) (session_t *ps, win **w);
// Fading-related members
/// Override value of window fade state. Set by D-Bus method calls.
switch_t fade_force;
// Frame-opacity-related members
/// Current window frame opacity. Affected by window opacity.
double frame_opacity;
/// Frame extents. Acquired from _NET_FRAME_EXTENTS.
margin_t frame_extents;
// Frame-opacity-related members
/// Current window frame opacity. Affected by window opacity.
double frame_opacity;
/// Frame extents. Acquired from _NET_FRAME_EXTENTS.
margin_t frame_extents;
// Shadow-related members
/// Whether a window has shadow. Calculated.
bool shadow;
/// Shadow state on last paint.
bool shadow_last;
/// Override value of window shadow state. Set by D-Bus method calls.
switch_t shadow_force;
/// Opacity of the shadow. Affected by window opacity and frame opacity.
double shadow_opacity;
/// X offset of shadow. Affected by commandline argument.
int shadow_dx;
/// Y offset of shadow. Affected by commandline argument.
int shadow_dy;
/// Width of shadow. Affected by window size and commandline argument.
int shadow_width;
/// Height of shadow. Affected by window size and commandline argument.
int shadow_height;
/// Picture to render shadow. Affected by window size.
paint_t shadow_paint;
/// The value of _COMPTON_SHADOW attribute of the window. Below 0 for
/// none.
long prop_shadow;
// Shadow-related members
/// Whether a window has shadow. Calculated.
bool shadow;
/// Override value of window shadow state. Set by D-Bus method calls.
switch_t shadow_force;
/// Opacity of the shadow. Affected by window opacity and frame opacity.
double shadow_opacity;
/// X offset of shadow. Affected by commandline argument.
int shadow_dx;
/// Y offset of shadow. Affected by commandline argument.
int shadow_dy;
/// Width of shadow. Affected by window size and commandline argument.
int shadow_width;
/// Height of shadow. Affected by window size and commandline argument.
int shadow_height;
/// Picture to render shadow. Affected by window size.
paint_t shadow_paint;
/// The value of _COMPTON_SHADOW attribute of the window. Below 0 for
/// none.
long prop_shadow;
// Dim-related members
/// Whether the window is to be dimmed.
bool dim;
// Dim-related members
/// Whether the window is to be dimmed.
bool dim;
/// Whether to invert window color.
bool invert_color;
/// Color inversion state on last paint.
bool invert_color_last;
/// Override value of window color inversion state. Set by D-Bus method
/// calls.
switch_t invert_color_force;
/// Whether to invert window color.
bool invert_color;
/// Override value of window color inversion state. Set by D-Bus method
/// calls.
switch_t invert_color_force;
/// Whether to blur window background.
bool blur_background;
/// Background state on last paint.
bool blur_background_last;
/// Whether to blur window background.
bool blur_background;
#ifdef CONFIG_OPENGL
/// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache;
/// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache;
#endif
};
int win_get_name(session_t *ps, win *w);
int win_get_role(session_t *ps, win *w);
void win_determine_mode(session_t *ps, win *w);
winmode_t attr_pure win_calc_mode(const win *w);
/**
* Set real focused state of a window.
*/
void win_set_focused(session_t *ps, win *w, bool focused);
void win_determine_fade(session_t *ps, win *w);
bool attr_pure win_should_fade(session_t *ps, const win *w);
void win_update_prop_shadow_raw(session_t *ps, win *w);
void win_update_prop_shadow(session_t *ps, win *w);
void win_set_shadow(session_t *ps, win *w, bool shadow_new);
@ -279,16 +300,21 @@ void win_set_blur_background(session_t *ps, win *w, bool blur_background_new);
void win_determine_blur_background(session_t *ps, win *w);
void win_on_wtype_change(session_t *ps, win *w);
void win_on_factor_change(session_t *ps, win *w);
void calc_win_size(session_t *ps, win *w);
void calc_shadow_geometry(session_t *ps, win *w);
void win_upd_wintype(session_t *ps, win *w);
/**
* Update cache data in struct _win that depends on window size.
*/
void win_on_win_size_change(session_t *ps, win *w);
void win_update_wintype(session_t *ps, win *w);
void win_mark_client(session_t *ps, win *w, xcb_window_t client);
void win_unmark_client(session_t *ps, win *w);
void win_recheck_client(session_t *ps, win *w);
xcb_window_t win_get_leader_raw(session_t *ps, win *w, int recursions);
bool win_get_class(session_t *ps, win *w);
void win_calc_opacity(session_t *ps, win *w);
void win_calc_dim(session_t *ps, win *w);
double attr_pure win_calc_opacity_target(session_t *ps, const win *w);
bool attr_pure win_should_dim(session_t *ps, const win *w);
void win_update_screen(session_t *, win *);
/// Prepare window for fading because opacity target changed
void win_start_fade(session_t *, win **);
/**
* Reread opacity property of a window.
*/
@ -313,8 +339,8 @@ void win_update_bounding_shape(session_t *ps, win *w);
* Note w->shadow and shadow geometry must be correct before calling this
* function.
*/
void win_extents(win *w, region_t *res);
region_t win_extents_by_val(win *w);
void win_extents(const win *w, region_t *res);
region_t win_extents_by_val(const win *w);
/**
* Add a window to damaged area.
*
@ -327,81 +353,79 @@ void add_damage_from_win(session_t *ps, win *w);
*
* Return region in global coordinates.
*/
void win_get_region_noframe_local(win *w, region_t *);
region_t win_get_region_noframe_local_by_val(win *w);
void win_get_region_noframe_local(const win *w, region_t *);
region_t win_get_region_noframe_local_by_val(const win *w);
/// Get the region for the frame of the window
void win_get_region_frame_local(const win *w, region_t *res);
/// Get the region for the frame of the window, by value
region_t win_get_region_frame_local_by_val(const win *w);
/**
* Retrieve frame extents from a window.
*/
void
win_update_frame_extents(session_t *ps, win *w, xcb_window_t client);
bool add_win(session_t *ps, xcb_window_t id, xcb_window_t prev);
void win_update_frame_extents(session_t *ps, win *w, xcb_window_t client);
void add_win(session_t *ps, xcb_window_t id, xcb_window_t prev);
/// Unmap or destroy a window
void unmap_win(session_t *ps, win **, bool destroy);
/**
* 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);
void map_win(session_t *ps, win *w);
void map_win_by_id(session_t *ps, xcb_window_t id);
/**
* Execute fade callback of a window if fading finished.
*/
void
win_check_fade_finished(session_t *ps, win **_w);
void win_check_fade_finished(session_t *ps, win **_w);
// 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, const win *w);
/// Skip the current in progress fading of window,
/// transition the window straight to its end state
void win_skip_fading(session_t *ps, win **_w);
/**
* Get the leader of a window.
*
* This function updates w->cache_leader if necessary.
*/
static inline xcb_window_t
win_get_leader(session_t *ps, win *w) {
return win_get_leader_raw(ps, w, 0);
static inline xcb_window_t win_get_leader(session_t *ps, win *w) {
return win_get_leader_raw(ps, w, 0);
}
/// check if window has ARGB visual
bool win_has_alpha(win *w);
bool attr_pure win_has_alpha(const win *w);
/// check if reg_ignore_valid is true for all windows above us
bool win_is_region_ignore_valid(session_t *ps, win *w);
bool attr_pure win_is_region_ignore_valid(session_t *ps, const win *w);
static inline region_t
win_get_bounding_shape_global_by_val(win *w) {
region_t ret;
pixman_region32_init(&ret);
pixman_region32_copy(&ret, &w->bounding_shape);
pixman_region32_translate(&ret, w->g.x, w->g.y);
return ret;
/// Free all resources in a struct win
void free_win_res(session_t *ps, win *w);
static inline region_t win_get_bounding_shape_global_by_val(win *w) {
region_t ret;
pixman_region32_init(&ret);
pixman_region32_copy(&ret, &w->bounding_shape);
pixman_region32_translate(&ret, w->g.x, w->g.y);
return ret;
}
/**
* Calculate the extents of the frame of the given window based on EWMH
* _NET_FRAME_EXTENTS and the X window border width.
*/
static inline margin_t attr_pure
win_calc_frame_extents(const win *w) {
margin_t result = w->frame_extents;
result.top = max_i(result.top, w->g.border_width);
result.left = max_i(result.left, w->g.border_width);
result.bottom = max_i(result.bottom, w->g.border_width);
result.right = max_i(result.right, w->g.border_width);
return result;
static inline margin_t attr_pure win_calc_frame_extents(const win *w) {
margin_t result = w->frame_extents;
result.top = max_i(result.top, w->g.border_width);
result.left = max_i(result.left, w->g.border_width);
result.bottom = max_i(result.bottom, w->g.border_width);
result.right = max_i(result.right, w->g.border_width);
return result;
}
/**
* Check whether a window has WM frames.
*/
static inline bool attr_pure
win_has_frame(const win *w) {
return w->g.border_width
|| w->frame_extents.top || w->frame_extents.left
|| w->frame_extents.right || w->frame_extents.bottom;
static inline bool attr_pure win_has_frame(const win *w) {
return w->g.border_width || w->frame_extents.top || w->frame_extents.left ||
w->frame_extents.right || w->frame_extents.bottom;
}

782
src/x.c
View File

@ -4,22 +4,23 @@
#include <stdlib.h>
#include <X11/Xutil.h>
#include <xcb/xcb.h>
#include <xcb/xcb_renderutil.h>
#include <xcb/xfixes.h>
#include <xcb/sync.h>
#include <pixman.h>
#include <xcb/composite.h>
#include <xcb/damage.h>
#include <xcb/render.h>
#include <pixman.h>
#include <GL/glx.h>
#include <xcb/sync.h>
#include <xcb/xcb.h>
#include <xcb/xcb_renderutil.h>
#include <xcb/xfixes.h>
#include "utils.h"
#include "region.h"
#include "compiler.h"
#include "backend/gl/glx.h"
#include "common.h"
#include "x.h"
#include "compiler.h"
#include "kernel.h"
#include "log.h"
#include "region.h"
#include "utils.h"
#include "x.h"
/**
* Get a specific attribute of a window.
@ -36,34 +37,28 @@
* @return a <code>winprop_t</code> structure containing the attribute
* and number of items. A blank one on failure.
*/
winprop_t
wid_get_prop_adv(const session_t *ps, xcb_window_t w, xcb_atom_t atom, long offset,
long length, xcb_atom_t rtype, int rformat) {
xcb_get_property_reply_t *r = xcb_get_property_reply(ps->c,
xcb_get_property(ps->c, 0, w, atom, rtype, offset, length), NULL);
winprop_t wid_get_prop_adv(const session_t *ps, xcb_window_t w, xcb_atom_t atom,
long offset, long length, xcb_atom_t rtype, int rformat) {
xcb_get_property_reply_t *r = xcb_get_property_reply(
ps->c, xcb_get_property(ps->c, 0, w, atom, rtype, offset, length), NULL);
if (r && xcb_get_property_value_length(r) &&
(rtype == XCB_GET_PROPERTY_TYPE_ANY || r->type == rtype) &&
(!rformat || r->format == rformat) &&
(r->format == 8 || r->format == 16 || r->format == 32))
{
int len = xcb_get_property_value_length(r);
return (winprop_t) {
.ptr = xcb_get_property_value(r),
.nitems = len/(r->format/8),
.type = r->type,
.format = r->format,
.r = r,
};
}
if (r && xcb_get_property_value_length(r) &&
(rtype == XCB_GET_PROPERTY_TYPE_ANY || r->type == rtype) &&
(!rformat || r->format == rformat) &&
(r->format == 8 || r->format == 16 || r->format == 32)) {
int len = xcb_get_property_value_length(r);
return (winprop_t){
.ptr = xcb_get_property_value(r),
.nitems = len / (r->format / 8),
.type = r->type,
.format = r->format,
.r = r,
};
}
free(r);
return (winprop_t) {
.ptr = NULL,
.nitems = 0,
.type = XCB_GET_PROPERTY_TYPE_ANY,
.format = 0
};
free(r);
return (winprop_t){
.ptr = NULL, .nitems = 0, .type = XCB_GET_PROPERTY_TYPE_ANY, .format = 0};
}
/**
@ -71,232 +66,254 @@ wid_get_prop_adv(const session_t *ps, xcb_window_t w, xcb_atom_t atom, long offs
*
* @return the value if successful, 0 otherwise
*/
xcb_window_t
wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t aprop) {
// Get the attribute
xcb_window_t p = XCB_NONE;
winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XCB_ATOM_WINDOW, 32);
xcb_window_t wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t aprop) {
// Get the attribute
xcb_window_t p = XCB_NONE;
winprop_t prop = wid_get_prop(ps, wid, aprop, 1L, XCB_ATOM_WINDOW, 32);
// Return it
if (prop.nitems) {
p = *prop.p32;
}
// Return it
if (prop.nitems) {
p = *prop.p32;
}
free_winprop(&prop);
free_winprop(&prop);
return p;
return p;
}
/**
* Get the value of a text property of a window.
*/
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop,
char ***pstrlst, int *pnstr) {
XTextProperty text_prop = { NULL, XCB_NONE, 0, 0 };
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
int *pnstr) {
XTextProperty text_prop = {NULL, XCB_NONE, 0, 0};
if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value))
return false;
if (!(XGetTextProperty(ps->dpy, wid, &text_prop, prop) && text_prop.value))
return false;
if (Success !=
XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr)
|| !*pnstr) {
*pnstr = 0;
if (*pstrlst)
XFreeStringList(*pstrlst);
cxfree(text_prop.value);
return false;
}
if (Success != XmbTextPropertyToTextList(ps->dpy, &text_prop, pstrlst, pnstr) ||
!*pnstr) {
*pnstr = 0;
if (*pstrlst)
XFreeStringList(*pstrlst);
cxfree(text_prop.value);
return false;
}
cxfree(text_prop.value);
return true;
cxfree(text_prop.value);
return true;
}
static inline void x_get_server_pictfmts(session_t *ps) {
if (ps->pictfmts)
return;
xcb_generic_error_t *e = NULL;
// Get window picture format
ps->pictfmts =
xcb_render_query_pict_formats_reply(ps->c,
xcb_render_query_pict_formats(ps->c), &e);
if (e || !ps->pictfmts) {
log_fatal("failed to get pict formats\n");
abort();
}
// A cache of pict formats. We assume they don't change during the lifetime
// of compton
static thread_local xcb_render_query_pict_formats_reply_t *g_pictfmts = NULL;
static inline void x_get_server_pictfmts(xcb_connection_t *c) {
if (g_pictfmts)
return;
xcb_generic_error_t *e = NULL;
// Get window picture format
g_pictfmts =
xcb_render_query_pict_formats_reply(c, xcb_render_query_pict_formats(c), &e);
if (e || !g_pictfmts) {
log_fatal("failed to get pict formats\n");
abort();
}
}
xcb_render_pictforminfo_t *x_get_pictform_for_visual(session_t *ps, xcb_visualid_t visual) {
if (!ps->pictfmts)
x_get_server_pictfmts(ps);
const xcb_render_pictforminfo_t *
x_get_pictform_for_visual(xcb_connection_t *c, xcb_visualid_t visual) {
x_get_server_pictfmts(c);
xcb_render_pictvisual_t *pv = xcb_render_util_find_visual_format(ps->pictfmts, visual);
for(xcb_render_pictforminfo_iterator_t i =
xcb_render_query_pict_formats_formats_iterator(ps->pictfmts); i.rem;
xcb_render_pictforminfo_next(&i)) {
if (i.data->id == pv->format) {
return i.data;
}
}
return NULL;
xcb_render_pictvisual_t *pv = xcb_render_util_find_visual_format(g_pictfmts, visual);
for (xcb_render_pictforminfo_iterator_t i =
xcb_render_query_pict_formats_formats_iterator(g_pictfmts);
i.rem; xcb_render_pictforminfo_next(&i)) {
if (i.data->id == pv->format) {
return i.data;
}
}
return NULL;
}
static xcb_visualid_t attr_pure x_get_visual_for_pictfmt(xcb_render_query_pict_formats_reply_t *r,
xcb_render_pictformat_t fmt) {
for (auto screen = xcb_render_query_pict_formats_screens_iterator(r); screen.rem;
xcb_render_pictscreen_next(&screen)) {
for (auto depth = xcb_render_pictscreen_depths_iterator(screen.data);
depth.rem; xcb_render_pictdepth_next(&depth)) {
for (auto pv = xcb_render_pictdepth_visuals_iterator(depth.data);
pv.rem; xcb_render_pictvisual_next(&pv)) {
if (pv.data->format == fmt) {
return pv.data->visual;
}
}
}
}
return XCB_NONE;
}
xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) {
x_get_server_pictfmts(c);
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
}
int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
auto setup = xcb_get_setup(c);
for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
xcb_screen_next(&screen)) {
for (auto depth = xcb_screen_allowed_depths_iterator(screen.data);
depth.rem; xcb_depth_next(&depth)) {
const int len = xcb_depth_visuals_length(depth.data);
const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
for (int i = 0; i < len; i++) {
if (visual == visuals[i].visual_id) {
return depth.data->depth;
}
}
}
}
return -1;
}
xcb_render_picture_t
x_create_picture_with_pictfmt_and_pixmap(
session_t *ps, xcb_render_pictforminfo_t * pictfmt,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
{
void *buf = NULL;
if (attr) {
xcb_render_create_picture_value_list_serialize(&buf, valuemask, attr);
if (!buf) {
log_error("failed to serialize picture attributes");
return XCB_NONE;
}
}
x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *c,
const xcb_render_pictforminfo_t *pictfmt,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr) {
void *buf = NULL;
if (attr) {
xcb_render_create_picture_value_list_serialize(&buf, valuemask, attr);
if (!buf) {
log_error("failed to serialize picture attributes");
return XCB_NONE;
}
}
xcb_render_picture_t tmp_picture = xcb_generate_id(ps->c);
xcb_generic_error_t *e =
xcb_request_check(ps->c, xcb_render_create_picture_checked(ps->c, tmp_picture,
pixmap, pictfmt->id, valuemask, buf));
free(buf);
if (e) {
log_error("failed to create picture");
return XCB_NONE;
}
return tmp_picture;
xcb_render_picture_t tmp_picture = xcb_generate_id(c);
xcb_generic_error_t *e =
xcb_request_check(c, xcb_render_create_picture_checked(
c, tmp_picture, pixmap, pictfmt->id, valuemask, buf));
free(buf);
if (e) {
x_print_error(e->full_sequence, e->major_code, e->minor_code, e->error_code);
log_error("failed to create picture");
return XCB_NONE;
}
return tmp_picture;
}
xcb_render_picture_t
x_create_picture_with_visual_and_pixmap(
session_t *ps, xcb_visualid_t visual,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
{
xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(ps, visual);
return x_create_picture_with_pictfmt_and_pixmap(ps, pictfmt, pixmap, valuemask, attr);
x_create_picture_with_visual_and_pixmap(xcb_connection_t *c, xcb_visualid_t visual,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr) {
const xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(c, visual);
return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
}
xcb_render_picture_t
x_create_picture_with_standard_and_pixmap(
session_t *ps, xcb_pict_standard_t standard,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
{
if (!ps->pictfmts)
x_get_server_pictfmts(ps);
x_create_picture_with_standard_and_pixmap(xcb_connection_t *c, xcb_pict_standard_t standard,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr) {
x_get_server_pictfmts(c);
xcb_render_pictforminfo_t *pictfmt =
xcb_render_util_find_standard_format(ps->pictfmts, standard);
assert(pictfmt);
return x_create_picture_with_pictfmt_and_pixmap(ps, pictfmt, pixmap, valuemask, attr);
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
assert(pictfmt);
return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
}
/**
* Create an picture.
*/
xcb_render_picture_t
x_create_picture_with_pictfmt(session_t *ps, int wid, int hei,
xcb_render_pictforminfo_t *pictfmt, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
{
if (!pictfmt)
pictfmt = x_get_pictform_for_visual(ps, ps->vis);
x_create_picture_with_pictfmt(xcb_connection_t *c, xcb_drawable_t d, int wid, int hei,
const xcb_render_pictforminfo_t *pictfmt, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr) {
int depth = pictfmt->depth;
if (!pictfmt) {
log_fatal("Default visual is invalid");
abort();
}
xcb_pixmap_t tmp_pixmap = x_create_pixmap(c, depth, d, wid, hei);
if (!tmp_pixmap)
return XCB_NONE;
int depth = pictfmt->depth;
xcb_render_picture_t picture = x_create_picture_with_pictfmt_and_pixmap(
c, pictfmt, tmp_pixmap, valuemask, attr);
xcb_pixmap_t tmp_pixmap = x_create_pixmap(ps, depth, ps->root, wid, hei);
if (!tmp_pixmap)
return XCB_NONE;
xcb_free_pixmap(c, tmp_pixmap);
xcb_render_picture_t picture =
x_create_picture_with_pictfmt_and_pixmap(ps, pictfmt, tmp_pixmap, valuemask, attr);
xcb_free_pixmap(ps->c, tmp_pixmap);
return picture;
return picture;
}
xcb_render_picture_t
x_create_picture_with_visual(session_t *ps, int w, int h,
xcb_visualid_t visual, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
{
xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(ps, visual);
return x_create_picture_with_pictfmt(ps, w, h, pictfmt, valuemask, attr);
x_create_picture_with_visual(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
xcb_visualid_t visual, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr) {
auto pictfmt = x_get_pictform_for_visual(c, visual);
return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
}
bool x_fetch_region(session_t *ps, xcb_xfixes_region_t r, pixman_region32_t *res) {
xcb_generic_error_t *e = NULL;
xcb_xfixes_fetch_region_reply_t *xr = xcb_xfixes_fetch_region_reply(ps->c,
xcb_xfixes_fetch_region(ps->c, r), &e);
if (!xr) {
log_error("Failed to fetch rectangles");
return false;
}
bool x_fetch_region(xcb_connection_t *c, xcb_xfixes_region_t r, pixman_region32_t *res) {
xcb_generic_error_t *e = NULL;
xcb_xfixes_fetch_region_reply_t *xr =
xcb_xfixes_fetch_region_reply(c, xcb_xfixes_fetch_region(c, r), &e);
if (!xr) {
log_error("Failed to fetch rectangles");
return false;
}
int nrect = xcb_xfixes_fetch_region_rectangles_length(xr);
auto b = ccalloc(nrect, pixman_box32_t);
xcb_rectangle_t *xrect = xcb_xfixes_fetch_region_rectangles(xr);
for (int i = 0; i < nrect; i++) {
b[i] = (pixman_box32_t) {
.x1 = xrect[i].x,
.y1 = xrect[i].y,
.x2 = xrect[i].x + xrect[i].width,
.y2 = xrect[i].y + xrect[i].height
};
}
bool ret = pixman_region32_init_rects(res, b, nrect);
free(b);
free(xr);
return ret;
int nrect = xcb_xfixes_fetch_region_rectangles_length(xr);
auto b = ccalloc(nrect, pixman_box32_t);
xcb_rectangle_t *xrect = xcb_xfixes_fetch_region_rectangles(xr);
for (int i = 0; i < nrect; i++) {
b[i] = (pixman_box32_t){.x1 = xrect[i].x,
.y1 = xrect[i].y,
.x2 = xrect[i].x + xrect[i].width,
.y2 = xrect[i].y + xrect[i].height};
}
bool ret = pixman_region32_init_rects(res, b, nrect);
free(b);
free(xr);
return ret;
}
void x_set_picture_clip_region(session_t *ps, xcb_render_picture_t pict,
int clip_x_origin, int clip_y_origin, const region_t *reg) {
int nrects;
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
auto xrects = ccalloc(nrects, xcb_rectangle_t);
for (int i = 0; i < nrects; i++)
xrects[i] = (xcb_rectangle_t){
.x = rects[i].x1,
.y = rects[i].y1,
.width = rects[i].x2 - rects[i].x1,
.height = rects[i].y2 - rects[i].y1,
};
void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict,
int clip_x_origin, int clip_y_origin, const region_t *reg) {
int nrects;
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
auto xrects = ccalloc(nrects, xcb_rectangle_t);
for (int i = 0; i < nrects; i++)
xrects[i] = (xcb_rectangle_t){
.x = rects[i].x1,
.y = rects[i].y1,
.width = rects[i].x2 - rects[i].x1,
.height = rects[i].y2 - rects[i].y1,
};
xcb_generic_error_t *e =
xcb_request_check(ps->c, xcb_render_set_picture_clip_rectangles_checked(ps->c, pict,
clip_x_origin, clip_y_origin, nrects, xrects));
if (e)
log_error("Failed to set clip region");
free(e);
free(xrects);
return;
xcb_generic_error_t *e =
xcb_request_check(c, xcb_render_set_picture_clip_rectangles_checked(
c, pict, clip_x_origin, clip_y_origin, nrects, xrects));
if (e)
log_error("Failed to set clip region");
free(e);
free(xrects);
return;
}
void x_clear_picture_clip_region(session_t *ps, xcb_render_picture_t pict) {
xcb_render_change_picture_value_list_t v = {
.clipmask = XCB_NONE
};
xcb_generic_error_t *e =
xcb_request_check(ps->c, xcb_render_change_picture(ps->c, pict,
XCB_RENDER_CP_CLIP_MASK, &v));
if (e)
log_error("failed to clear clip region");
free(e);
return;
void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) {
xcb_render_change_picture_value_list_t v = {.clipmask = XCB_NONE};
xcb_generic_error_t *e = xcb_request_check(
c, xcb_render_change_picture(c, pict, XCB_RENDER_CP_CLIP_MASK, &v));
if (e)
log_error("failed to clear clip region");
free(e);
return;
}
enum {
XSyncBadCounter = 0,
XSyncBadAlarm = 1,
XSyncBadFence = 2,
enum { XSyncBadCounter = 0,
XSyncBadAlarm = 1,
XSyncBadFence = 2,
};
/**
@ -304,106 +321,102 @@ enum {
*
* XXX consider making this error to string
*/
void
x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_code) {
session_t * const ps = ps_g;
void x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_code) {
session_t *const ps = ps_g;
int o = 0;
const char *name = "Unknown";
int o = 0;
const char *name = "Unknown";
if (major == ps->composite_opcode
&& minor == XCB_COMPOSITE_REDIRECT_SUBWINDOWS) {
log_fatal("Another composite manager is already running "
"(and does not handle _NET_WM_CM_Sn correctly)");
exit(1);
}
if (major == ps->composite_opcode && minor == XCB_COMPOSITE_REDIRECT_SUBWINDOWS) {
log_fatal("Another composite manager is already running "
"(and does not handle _NET_WM_CM_Sn correctly)");
exit(1);
}
#define CASESTRRET2(s) case s: name = #s; break
#define CASESTRRET2(s) \
case s: name = #s; break
o = error_code - ps->xfixes_error;
switch (o) {
CASESTRRET2(XCB_XFIXES_BAD_REGION);
}
o = error_code - ps->xfixes_error;
switch (o) { CASESTRRET2(XCB_XFIXES_BAD_REGION); }
o = error_code - ps->damage_error;
switch (o) {
CASESTRRET2(XCB_DAMAGE_BAD_DAMAGE);
}
o = error_code - ps->damage_error;
switch (o) { CASESTRRET2(XCB_DAMAGE_BAD_DAMAGE); }
o = error_code - ps->render_error;
switch (o) {
CASESTRRET2(XCB_RENDER_PICT_FORMAT);
CASESTRRET2(XCB_RENDER_PICTURE);
CASESTRRET2(XCB_RENDER_PICT_OP);
CASESTRRET2(XCB_RENDER_GLYPH_SET);
CASESTRRET2(XCB_RENDER_GLYPH);
}
o = error_code - ps->render_error;
switch (o) {
CASESTRRET2(XCB_RENDER_PICT_FORMAT);
CASESTRRET2(XCB_RENDER_PICTURE);
CASESTRRET2(XCB_RENDER_PICT_OP);
CASESTRRET2(XCB_RENDER_GLYPH_SET);
CASESTRRET2(XCB_RENDER_GLYPH);
}
#ifdef CONFIG_OPENGL
if (ps->glx_exists) {
o = error_code - ps->glx_error;
switch (o) {
CASESTRRET2(GLX_BAD_SCREEN);
CASESTRRET2(GLX_BAD_ATTRIBUTE);
CASESTRRET2(GLX_NO_EXTENSION);
CASESTRRET2(GLX_BAD_VISUAL);
CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_VALUE);
CASESTRRET2(GLX_BAD_ENUM);
}
}
if (ps->glx_exists) {
o = error_code - ps->glx_error;
switch (o) {
CASESTRRET2(GLX_BAD_SCREEN);
CASESTRRET2(GLX_BAD_ATTRIBUTE);
CASESTRRET2(GLX_NO_EXTENSION);
CASESTRRET2(GLX_BAD_VISUAL);
CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_VALUE);
CASESTRRET2(GLX_BAD_ENUM);
}
}
#endif
if (ps->xsync_exists) {
o = error_code - ps->xsync_error;
switch (o) {
CASESTRRET2(XSyncBadCounter);
CASESTRRET2(XSyncBadAlarm);
CASESTRRET2(XSyncBadFence);
}
}
if (ps->xsync_exists) {
o = error_code - ps->xsync_error;
switch (o) {
CASESTRRET2(XSyncBadCounter);
CASESTRRET2(XSyncBadAlarm);
CASESTRRET2(XSyncBadFence);
}
}
switch (error_code) {
CASESTRRET2(BadAccess);
CASESTRRET2(BadAlloc);
CASESTRRET2(BadAtom);
CASESTRRET2(BadColor);
CASESTRRET2(BadCursor);
CASESTRRET2(BadDrawable);
CASESTRRET2(BadFont);
CASESTRRET2(BadGC);
CASESTRRET2(BadIDChoice);
CASESTRRET2(BadImplementation);
CASESTRRET2(BadLength);
CASESTRRET2(BadMatch);
CASESTRRET2(BadName);
CASESTRRET2(BadPixmap);
CASESTRRET2(BadRequest);
CASESTRRET2(BadValue);
CASESTRRET2(BadWindow);
}
switch (error_code) {
CASESTRRET2(BadAccess);
CASESTRRET2(BadAlloc);
CASESTRRET2(BadAtom);
CASESTRRET2(BadColor);
CASESTRRET2(BadCursor);
CASESTRRET2(BadDrawable);
CASESTRRET2(BadFont);
CASESTRRET2(BadGC);
CASESTRRET2(BadIDChoice);
CASESTRRET2(BadImplementation);
CASESTRRET2(BadLength);
CASESTRRET2(BadMatch);
CASESTRRET2(BadName);
CASESTRRET2(BadPixmap);
CASESTRRET2(BadRequest);
CASESTRRET2(BadValue);
CASESTRRET2(BadWindow);
}
#undef CASESTRRET2
log_debug("X error %d %s request %d minor %d serial %lu",
error_code, name, major, minor, serial);
log_debug("X error %d %s request %d minor %d serial %lu", error_code, name, major,
minor, serial);
}
/**
* Create a pixmap and check that creation succeeded.
*/
xcb_pixmap_t
x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height) {
xcb_pixmap_t pix = xcb_generate_id(ps->c);
xcb_void_cookie_t cookie = xcb_create_pixmap_checked(ps->c, depth, pix, drawable, width, height);
xcb_generic_error_t *err = xcb_request_check(ps->c, cookie);
if (err == NULL)
return pix;
xcb_pixmap_t x_create_pixmap(xcb_connection_t *c, uint8_t depth, xcb_drawable_t drawable,
uint16_t width, uint16_t height) {
xcb_pixmap_t pix = xcb_generate_id(c);
xcb_void_cookie_t cookie =
xcb_create_pixmap_checked(c, depth, pix, drawable, width, height);
xcb_generic_error_t *err = xcb_request_check(c, cookie);
if (err == NULL)
return pix;
log_error("Failed to create pixmap:");
ev_xcb_error(ps, err);
free(err);
return XCB_NONE;
log_error("Failed to create pixmap:");
x_print_error(err->sequence, err->major_code, err->minor_code, err->error_code);
free(err);
return XCB_NONE;
}
/**
@ -412,87 +425,156 @@ x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t
* Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
* are better ways.
*/
bool
x_validate_pixmap(session_t *ps, xcb_pixmap_t pixmap) {
bool x_validate_pixmap(xcb_connection_t *c, xcb_pixmap_t pixmap) {
if (pixmap == XCB_NONE) {
return false;
}
auto r = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, pixmap), NULL);
if (!r) {
return false;
}
auto r = xcb_get_geometry_reply(c, xcb_get_geometry(c, pixmap), NULL);
if (!r) {
return false;
}
bool ret = r->width && r->height;
free(r);
return ret;
bool ret = r->width && r->height;
free(r);
return ret;
}
/// Names of root window properties that could point to a pixmap of
/// background.
static const char *background_props_str[] = {
"_XROOTPMAP_ID",
"_XSETROOT_ID",
0,
"_XROOTPMAP_ID",
"_XSETROOT_ID",
0,
};
xcb_pixmap_t x_get_root_back_pixmap(session_t *ps) {
xcb_pixmap_t pixmap = XCB_NONE;
xcb_pixmap_t pixmap = XCB_NONE;
// Get the values of background attributes
for (int p = 0; background_props_str[p]; p++) {
xcb_atom_t prop_atom = get_atom(ps, background_props_str[p]);
winprop_t prop =
wid_get_prop(ps, ps->root, prop_atom, 1, XCB_ATOM_PIXMAP, 32);
if (prop.nitems) {
pixmap = *prop.p32;
free_winprop(&prop);
break;
}
free_winprop(&prop);
}
// Get the values of background attributes
for (int p = 0; background_props_str[p]; p++) {
xcb_atom_t prop_atom = get_atom(ps, background_props_str[p]);
winprop_t prop =
wid_get_prop(ps, ps->root, prop_atom, 1, XCB_ATOM_PIXMAP, 32);
if (prop.nitems) {
pixmap = *prop.p32;
free_winprop(&prop);
break;
}
free_winprop(&prop);
}
return pixmap;
return pixmap;
}
bool x_atom_is_background_prop(session_t *ps, xcb_atom_t atom) {
for (int p = 0; background_props_str[p]; p++) {
xcb_atom_t prop_atom = get_atom(ps, background_props_str[p]);
if (prop_atom == atom)
return true;
}
return false;
bool x_is_root_back_pixmap_atom(session_t *ps, xcb_atom_t atom) {
for (int p = 0; background_props_str[p]; p++) {
xcb_atom_t prop_atom = get_atom(ps, background_props_str[p]);
if (prop_atom == atom)
return true;
}
return false;
}
/**
* Synchronizes a X Render drawable to ensure all pending painting requests
* are completed.
*/
bool x_fence_sync(session_t *ps, xcb_sync_fence_t f) {
if (ps->xsync_exists) {
// TODO(richardgv): If everybody just follows the rules stated in X Sync
// prototype, we need only one fence per screen, but let's stay a bit
// cautious right now
bool x_fence_sync(xcb_connection_t *c, xcb_sync_fence_t f) {
// TODO(richardgv): If everybody just follows the rules stated in X Sync
// prototype, we need only one fence per screen, but let's stay a bit
// cautious right now
auto e = xcb_request_check(ps->c, xcb_sync_trigger_fence_checked(ps->c, f));
if (e) {
log_error("Failed to trigger the fence.");
free(e);
return false;
}
auto e = xcb_request_check(c, xcb_sync_trigger_fence_checked(c, f));
if (e) {
log_error("Failed to trigger the fence.");
free(e);
return false;
}
e = xcb_request_check(ps->c, xcb_sync_await_fence_checked(ps->c, 1, &f));
if (e) {
log_error("Failed to await on a fence.");
free(e);
return false;
}
e = xcb_request_check(c, xcb_sync_await_fence_checked(c, 1, &f));
if (e) {
log_error("Failed to await on a fence.");
free(e);
return false;
}
e = xcb_request_check(ps->c, xcb_sync_reset_fence_checked(ps->c, f));
if (e) {
log_error("Failed to reset the fence.");
free(e);
return false;
}
}
return true;
e = xcb_request_check(c, xcb_sync_reset_fence_checked(c, f));
if (e) {
log_error("Failed to reset the fence.");
free(e);
return false;
}
return true;
}
// xcb-render specific macros
#define XFIXED_TO_DOUBLE(value) (((double)(value)) / 65536)
#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t)(((double)(value)) * 65536))
/**
* Convert a struct conv to a X picture convolution filter, normalizing the kernel
* in the process. Allow the caller to specify the element at the center of the kernel,
* for compatibility with legacy code.
*
* @param[in] kernel the convolution kernel
* @param[in] center the element to put at the center of the matrix
* @param[inout] ret pointer to an array of `size`, if `size` is too small, more space
* will be allocated, and `*ret` will be updated
* @param[inout] size size of the array pointed to by `ret`, in number of elements
* @return number of elements filled into `*ret`
*/
size_t x_picture_filter_from_conv(const conv *kernel, double center,
xcb_render_fixed_t **ret, size_t *size) {
if (*size < (size_t)(kernel->w * kernel->h + 2)) {
*size = kernel->w * kernel->h + 2;
*ret = crealloc(*ret, *size);
}
auto buf = *ret;
buf[0] = DOUBLE_TO_XFIXED(kernel->w);
buf[1] = DOUBLE_TO_XFIXED(kernel->h);
double sum = center;
for (int i = 0; i < kernel->w * kernel->h; i++) {
sum += kernel->data[i];
}
// Note for floating points a / b != a * (1 / b), but this shouldn't have any real
// impact on the result
double factor = sum != 0 ? 1.0 / sum : 1;
for (int i = 0; i < kernel->w * kernel->h; i++) {
buf[i + 2] = DOUBLE_TO_XFIXED(kernel->data[i] * factor);
}
buf[kernel->h / 2 * kernel->w + kernel->w / 2 + 2] =
DOUBLE_TO_XFIXED(center * factor);
return kernel->w * kernel->h + 2;
}
/// Generate a search criteria for fbconfig from a X visual.
/// Returns {-1, -1, -1, -1, -1, -1} on failure
struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual) {
auto pictfmt = x_get_pictform_for_visual(c, visual);
auto depth = x_get_visual_depth(c, visual);
if (!pictfmt || depth == -1) {
log_error("Invalid visual %#03x", visual);
return (struct xvisual_info){-1, -1, -1, -1, -1, -1};
}
if (pictfmt->type != XCB_RENDER_PICT_TYPE_DIRECT) {
log_error("compton cannot handle non-DirectColor visuals. Report an "
"issue if you see this error message.");
return (struct xvisual_info){-1, -1, -1, -1, -1, -1};
}
int red_size = popcountl(pictfmt->direct.red_mask),
blue_size = popcountl(pictfmt->direct.blue_mask),
green_size = popcountl(pictfmt->direct.green_mask),
alpha_size = popcountl(pictfmt->direct.alpha_mask);
return (struct xvisual_info){
.red_size = red_size,
.green_size = green_size,
.blue_size = blue_size,
.alpha_size = alpha_size,
.visual_depth = depth,
.visual = visual,
};
}

197
src/x.h
View File

@ -1,46 +1,65 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <xcb/xcb.h>
#include <xcb/render.h>
#include <xcb/sync.h>
#include <xcb/xfixes.h>
#include <xcb/xcb.h>
#include <xcb/xcb_renderutil.h>
#include <xcb/xfixes.h>
#include "compiler.h"
#include "kernel.h"
#include "region.h"
typedef struct session session_t;
/// Structure representing Window property value.
typedef struct winprop {
union {
void *ptr;
int8_t *p8;
int16_t *p16;
int32_t *p32;
uint32_t *c32; // 32bit cardinal
};
unsigned long nitems;
xcb_atom_t type;
int format;
union {
void *ptr;
int8_t *p8;
int16_t *p16;
int32_t *p32;
uint32_t *c32; // 32bit cardinal
};
unsigned long nitems;
xcb_atom_t type;
int format;
xcb_get_property_reply_t *r;
xcb_get_property_reply_t *r;
} winprop_t;
#define XCB_SYNCED_VOID(func, c, ...) xcb_request_check(c, func##_checked(c, __VA_ARGS__));
#define XCB_SYNCED(func, c, ...) ({ \
xcb_generic_error_t *e = NULL; \
__auto_type r = func##_reply(c, func(c, __VA_ARGS__), &e); \
if (e) { \
x_print_error(e->sequence, e->major_code, e->minor_code, e->error_code); \
free(e); \
} \
r; \
})
struct xvisual_info {
/// Bit depth of the red component
int red_size;
/// Bit depth of the green component
int green_size;
/// Bit depth of the blue component
int blue_size;
/// Bit depth of the alpha component
int alpha_size;
/// The depth of X visual
int visual_depth;
xcb_visualid_t visual;
};
#define XCB_SYNCED_VOID(func, c, ...) \
xcb_request_check(c, func##_checked(c, __VA_ARGS__));
#define XCB_SYNCED(func, c, ...) \
({ \
xcb_generic_error_t *e = NULL; \
__auto_type r = func##_reply(c, func(c, __VA_ARGS__), &e); \
if (e) { \
x_print_error(e->sequence, e->major_code, e->minor_code, \
e->error_code); \
free(e); \
} \
r; \
})
/**
* Send a request to X server and get the reply to make sure all previous
@ -49,9 +68,8 @@ typedef struct winprop {
* xcb_get_input_focus is used here because it is the same request used by
* libX11
*/
static inline void
x_sync(xcb_connection_t *c) {
free(xcb_get_input_focus_reply(c, xcb_get_input_focus(c), NULL));
static inline void x_sync(xcb_connection_t *c) {
free(xcb_get_input_focus_reply(c, xcb_get_input_focus(c), NULL));
}
/**
@ -69,17 +87,15 @@ x_sync(xcb_connection_t *c) {
* @return a <code>winprop_t</code> structure containing the attribute
* and number of items. A blank one on failure.
*/
winprop_t
wid_get_prop_adv(const session_t *ps, xcb_window_t w, xcb_atom_t atom, long offset,
long length, xcb_atom_t rtype, int rformat);
winprop_t wid_get_prop_adv(const session_t *ps, xcb_window_t w, xcb_atom_t atom,
long offset, long length, xcb_atom_t rtype, int rformat);
/**
* Wrapper of wid_get_prop_adv().
*/
static inline winprop_t
wid_get_prop(const session_t *ps, xcb_window_t wid, xcb_atom_t atom, long length,
xcb_atom_t rtype, int rformat) {
return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat);
static inline winprop_t wid_get_prop(const session_t *ps, xcb_window_t wid, xcb_atom_t atom,
long length, xcb_atom_t rtype, int rformat) {
return wid_get_prop_adv(ps, wid, atom, 0L, length, rtype, rformat);
}
/**
@ -87,89 +103,110 @@ wid_get_prop(const session_t *ps, xcb_window_t wid, xcb_atom_t atom, long length
*
* @return the value if successful, 0 otherwise
*/
xcb_window_t
wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t aprop);
xcb_window_t wid_get_prop_window(session_t *ps, xcb_window_t wid, xcb_atom_t aprop);
/**
* Get the value of a text property of a window.
*/
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop,
char ***pstrlst, int *pnstr);
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
int *pnstr);
xcb_render_pictforminfo_t *x_get_pictform_for_visual(session_t *, xcb_visualid_t);
const xcb_render_pictforminfo_t *
x_get_pictform_for_visual(xcb_connection_t *, xcb_visualid_t);
int x_get_visual_depth(xcb_connection_t *, xcb_visualid_t);
xcb_render_picture_t x_create_picture_with_pictfmt_and_pixmap(
session_t *ps, xcb_render_pictforminfo_t *pictfmt,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1, 2);
xcb_render_picture_t
x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *,
const xcb_render_pictforminfo_t *pictfmt,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1, 2);
xcb_render_picture_t x_create_picture_with_visual_and_pixmap(
session_t *ps, xcb_visualid_t visual,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
xcb_render_picture_t
x_create_picture_with_visual_and_pixmap(xcb_connection_t *, xcb_visualid_t visual,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
xcb_render_picture_t x_create_picture_with_standard_and_pixmap(
session_t *ps, xcb_pict_standard_t standard,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
xcb_render_picture_t
x_create_picture_with_standard_and_pixmap(xcb_connection_t *, xcb_pict_standard_t standard,
xcb_pixmap_t pixmap, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
/**
* Create an picture.
*/
xcb_render_picture_t
x_create_picture_with_pictfmt(session_t *ps, int wid, int hei,
xcb_render_pictforminfo_t *pictfmt, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr);
x_create_picture_with_pictfmt(xcb_connection_t *, xcb_drawable_t, int wid, int hei,
const xcb_render_pictforminfo_t *pictfmt, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1, 5);
xcb_render_picture_t
x_create_picture_with_visual(session_t *ps, int w, int h,
xcb_visualid_t visual, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr);
x_create_picture_with_visual(xcb_connection_t *, xcb_drawable_t, int w, int h,
xcb_visualid_t visual, unsigned long valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
/// Fetch a X region and store it in a pixman region
bool x_fetch_region(session_t *ps, xcb_xfixes_region_t r, region_t *res);
bool x_fetch_region(xcb_connection_t *, xcb_xfixes_region_t r, region_t *res);
void x_set_picture_clip_region(session_t *ps, xcb_render_picture_t,
int clip_x_origin, int clip_y_origin, const region_t *);
void x_set_picture_clip_region(xcb_connection_t *, xcb_render_picture_t,
int clip_x_origin, int clip_y_origin, const region_t *);
void x_clear_picture_clip_region(session_t *ps, xcb_render_picture_t pict);
void x_clear_picture_clip_region(xcb_connection_t *, xcb_render_picture_t pict);
/**
* X11 error handler function.
*
* XXX consider making this error to string
*/
void
x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_code);
void x_print_error(unsigned long serial, uint8_t major, uint8_t minor, uint8_t error_code);
xcb_pixmap_t
x_create_pixmap(session_t *ps, uint8_t depth, xcb_drawable_t drawable, uint16_t width, uint16_t height);
xcb_pixmap_t x_create_pixmap(xcb_connection_t *, uint8_t depth, xcb_drawable_t drawable,
uint16_t width, uint16_t height);
bool
x_validate_pixmap(session_t *ps, xcb_pixmap_t pxmap);
bool x_validate_pixmap(xcb_connection_t *, xcb_pixmap_t pxmap);
/**
* Free a <code>winprop_t</code>.
*
* @param pprop pointer to the <code>winprop_t</code> to free.
*/
static inline void
free_winprop(winprop_t *pprop) {
// Empty the whole structure to avoid possible issues
if (pprop->r)
free(pprop->r);
pprop->ptr = NULL;
pprop->r = NULL;
pprop->nitems = 0;
static inline void free_winprop(winprop_t *pprop) {
// Empty the whole structure to avoid possible issues
if (pprop->r)
free(pprop->r);
pprop->ptr = NULL;
pprop->r = NULL;
pprop->nitems = 0;
}
/// Get the back pixmap of the root window
xcb_pixmap_t x_get_root_back_pixmap(session_t *ps);
/// Return true if the atom refers to a property name that is used for the
/// root window background pixmap
bool x_atom_is_background_prop(session_t *ps, xcb_atom_t atom);
bool x_is_root_back_pixmap_atom(session_t *ps, xcb_atom_t atom);
bool x_fence_sync(session_t *, xcb_sync_fence_t);
bool x_fence_sync(xcb_connection_t *, xcb_sync_fence_t);
/**
* Convert a struct conv to a X picture convolution filter, normalizing the kernel
* in the process. Allow the caller to specify the element at the center of the kernel,
* for compatibility with legacy code.
*
* @param[in] kernel the convolution kernel
* @param[in] center the element to put at the center of the matrix
* @param[inout] ret pointer to an array of `size`, if `size` is too small, more space
* will be allocated, and `*ret` will be updated.
* @param[inout] size size of the array pointed to by `ret`.
*/
size_t x_picture_filter_from_conv(const conv *kernel, double center,
xcb_render_fixed_t **ret, size_t *size);
/// Generate a search criteria for fbconfig from a X visual.
/// Returns {-1, -1, -1, -1, -1, -1} on failure
struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual);
xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);

View File

@ -8,64 +8,58 @@
static xrc_xid_record_t *gs_xid_records = NULL;
#define HASH_ADD_XID(head, xidfield, add) \
HASH_ADD(hh, head, xidfield, sizeof(xid), add)
#define HASH_ADD_XID(head, xidfield, add) HASH_ADD(hh, head, xidfield, sizeof(xid), add)
#define HASH_FIND_XID(head, findxid, out) \
HASH_FIND(hh, head, findxid, sizeof(xid), out)
#define HASH_FIND_XID(head, findxid, out) HASH_FIND(hh, head, findxid, sizeof(xid), out)
#define M_CPY_POS_DATA(prec) \
prec->file = file; \
prec->func = func; \
prec->line = line; \
#define M_CPY_POS_DATA(prec) \
prec->file = file; \
prec->func = func; \
prec->line = line;
/**
* @brief Add a record of given XID to the allocation table.
*/
void
xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) {
auto prec = ccalloc(1, xrc_xid_record_t);
prec->xid = xid;
prec->type = type;
M_CPY_POS_DATA(prec);
void xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS) {
auto prec = ccalloc(1, xrc_xid_record_t);
prec->xid = xid;
prec->type = type;
M_CPY_POS_DATA(prec);
HASH_ADD_XID(gs_xid_records, xid, prec);
HASH_ADD_XID(gs_xid_records, xid, prec);
}
/**
* @brief Delete a record of given XID in the allocation table.
*/
void
xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) {
xrc_xid_record_t *prec = NULL;
HASH_FIND_XID(gs_xid_records, &xid, prec);
if (!prec) {
log_error("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.",
file, line, func, xid);
return;
}
HASH_DEL(gs_xid_records, prec);
free(prec);
void xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS) {
xrc_xid_record_t *prec = NULL;
HASH_FIND_XID(gs_xid_records, &xid, prec);
if (!prec) {
log_error("XRC: %s:%d %s(): Can't find XID %#010lx we want to delete.",
file, line, func, xid);
return;
}
HASH_DEL(gs_xid_records, prec);
free(prec);
}
/**
* @brief Report about issues found in the XID allocation table.
*/
void
xrc_report_xid(void) {
for (xrc_xid_record_t *prec = gs_xid_records; prec; prec = prec->hh.next)
log_trace("XRC: %s:%d %s(): %#010lx (%s) not freed.\n",
prec->file, prec->line, prec->func, prec->xid, prec->type);
void xrc_report_xid(void) {
for (xrc_xid_record_t *prec = gs_xid_records; prec; prec = prec->hh.next)
log_trace("XRC: %s:%d %s(): %#010lx (%s) not freed.\n", prec->file,
prec->line, prec->func, prec->xid, prec->type);
}
/**
* @brief Clear the XID allocation table.
*/
void
xrc_clear_xid(void) {
xrc_xid_record_t *prec = NULL, *ptmp = NULL;
HASH_ITER(hh, gs_xid_records, prec, ptmp) {
HASH_DEL(gs_xid_records, prec);
free(prec);
}
void xrc_clear_xid(void) {
xrc_xid_record_t *prec = NULL, *ptmp = NULL;
HASH_ITER(hh, gs_xid_records, prec, ptmp) {
HASH_DEL(gs_xid_records, prec);
free(prec);
}
}

View File

@ -6,60 +6,57 @@
#include "uthash.h"
typedef struct {
XID xid;
const char *type;
const char *file;
const char *func;
int line;
UT_hash_handle hh;
XID xid;
const char *type;
const char *file;
const char *func;
int line;
UT_hash_handle hh;
} xrc_xid_record_t;
#define M_POS_DATA_PARAMS const char *file, int line, const char *func
#define M_POS_DATA_PASSTHROUGH file, line, func
#define M_POS_DATA __FILE__, __LINE__, __func__
void
xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS);
void xrc_add_xid_(XID xid, const char *type, M_POS_DATA_PARAMS);
#define xrc_add_xid(xid, type) xrc_add_xid_(xid, type, M_POS_DATA)
void
xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS);
void xrc_delete_xid_(XID xid, M_POS_DATA_PARAMS);
#define xrc_delete_xid(xid) xrc_delete_xid_(xid, M_POS_DATA)
void
xrc_report_xid(void);
void xrc_report_xid(void);
void
xrc_clear_xid(void);
void xrc_clear_xid(void);
// Pixmap
static inline void
xcb_create_pixmap_(xcb_connection_t *c, uint8_t depth, xcb_pixmap_t pixmap,
xcb_drawable_t drawable, uint16_t width, uint16_t height, M_POS_DATA_PARAMS) {
xcb_create_pixmap(c, depth, pixmap, drawable, width, height);
xrc_add_xid_(pixmap, "Pixmap", M_POS_DATA_PASSTHROUGH);
static inline void xcb_create_pixmap_(xcb_connection_t *c, uint8_t depth,
xcb_pixmap_t pixmap, xcb_drawable_t drawable,
uint16_t width, uint16_t height, M_POS_DATA_PARAMS) {
xcb_create_pixmap(c, depth, pixmap, drawable, width, height);
xrc_add_xid_(pixmap, "Pixmap", M_POS_DATA_PASSTHROUGH);
}
#define xcb_create_pixmap(c, depth, pixmap, drawable, width, height) \
xcb_create_pixmap_(c, depth, pixmap, drawable, width, height, M_POS_DATA)
#define xcb_create_pixmap(c, depth, pixmap, drawable, width, height) \
xcb_create_pixmap_(c, depth, pixmap, drawable, width, height, M_POS_DATA)
static inline xcb_void_cookie_t
xcb_composite_name_window_pixmap_(xcb_connection_t *c, xcb_window_t window, xcb_pixmap_t pixmap, M_POS_DATA_PARAMS) {
xcb_void_cookie_t ret = xcb_composite_name_window_pixmap(c, window, pixmap);
xrc_add_xid_(pixmap, "PixmapC", M_POS_DATA_PASSTHROUGH);
return ret;
xcb_composite_name_window_pixmap_(xcb_connection_t *c, xcb_window_t window,
xcb_pixmap_t pixmap, M_POS_DATA_PARAMS) {
xcb_void_cookie_t ret = xcb_composite_name_window_pixmap(c, window, pixmap);
xrc_add_xid_(pixmap, "PixmapC", M_POS_DATA_PASSTHROUGH);
return ret;
}
#define xcb_composite_name_window_pixmap(dpy, window, pixmap) \
xcb_composite_name_window_pixmap_(dpy, window, pixmap, M_POS_DATA)
#define xcb_composite_name_window_pixmap(dpy, window, pixmap) \
xcb_composite_name_window_pixmap_(dpy, window, pixmap, M_POS_DATA)
static inline void
xcb_free_pixmap_(xcb_connection_t *c, xcb_pixmap_t pixmap, M_POS_DATA_PARAMS) {
xcb_free_pixmap(c, pixmap);
xrc_delete_xid_(pixmap, M_POS_DATA_PASSTHROUGH);
xcb_free_pixmap(c, pixmap);
xrc_delete_xid_(pixmap, M_POS_DATA_PASSTHROUGH);
}
#define xcb_free_pixmap(c, pixmap) xcb_free_pixmap_(c, pixmap, M_POS_DATA);