Compare commits
129 Commits
580aef939f
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df4c6a3d9b | ||
|
|
3b9e2c5812 | ||
|
|
b99c7db73e | ||
|
|
7c15a1438a | ||
|
|
2b52edd9e3 | ||
|
|
9175489f65 | ||
|
|
45ffdf9849 | ||
|
|
96de4f07ca | ||
|
|
a93bbc30e5 | ||
|
|
6315faed20 | ||
|
|
39534b98f0 | ||
|
|
77c4ebaafc | ||
|
|
e8ca1912cd | ||
|
|
39de1e49cf | ||
|
|
fffc7ce535 | ||
|
|
fe5b416ed6 | ||
|
|
2417084628 | ||
|
|
4299336146 | ||
|
|
a3f50e8a3b | ||
|
|
217284642a | ||
|
|
7336e4142b | ||
|
|
c178be2874 | ||
|
|
7da885e6c0 | ||
|
|
5a8c61daed | ||
|
|
b1fce63a26 | ||
|
|
b73e6a0c45 | ||
|
|
98e842e83b | ||
|
|
2241c7d87a | ||
|
|
21c2da549a | ||
|
|
2b74392ec7 | ||
|
|
367643e98a | ||
|
|
a5c4095082 | ||
|
|
15aa5014a6 | ||
|
|
4d624bbee5 | ||
|
|
dc9d1fe248 | ||
|
|
1c7466c07c | ||
|
|
44cfa0b0ef | ||
|
|
bb2b4801f7 | ||
|
|
36e6b73cde | ||
|
|
90f57e9b1a | ||
|
|
157ecd2077 | ||
|
|
3a791ab559 | ||
|
|
f5e8351507 | ||
|
|
9f14c43989 | ||
|
|
97d9c960e0 | ||
|
|
d41a291fd6 | ||
|
|
c3a7cf29ab | ||
|
|
3b1930d2c6 | ||
|
|
92a32808ae | ||
|
|
d3c467f4c0 | ||
|
|
4be96a92c7 | ||
|
|
ab766b6386 | ||
|
|
7e975cdc5d | ||
|
|
349f2f37db | ||
|
|
7846b17c54 | ||
|
|
a667886959 | ||
|
|
6c08650f3c | ||
|
|
7f533c0b23 | ||
|
|
efa3d9c227 | ||
|
|
0b965b2e5a | ||
|
|
ba2e24af3e | ||
|
|
0278a1fb0b | ||
|
|
931c1b8bf6 | ||
|
|
6906e6694a | ||
|
|
f1bff49b1c | ||
|
|
fe3f53f3a4 | ||
|
|
57956fb219 | ||
|
|
c51020aef7 | ||
|
|
eec8bf79d2 | ||
|
|
227cb55ca5 | ||
|
|
e9ff18e1bd | ||
|
|
43d8d3ed5d | ||
|
|
bf0832cd4c | ||
|
|
9c3204cc72 | ||
|
|
bc1f99f2ae | ||
|
|
d21fb34e55 | ||
|
|
7f18e74b8f | ||
|
|
2173654fbd | ||
|
|
123ef51210 | ||
|
|
4ecc3e65a9 | ||
|
|
4a7a9e8079 | ||
|
|
dacadb9fc3 | ||
|
|
144e78fd5d | ||
|
|
d647ccca16 | ||
|
|
9fe7e65e3e | ||
|
|
ad5a042803 | ||
|
|
0a82f460e0 | ||
|
|
49490ab99f | ||
|
|
9bf39b8d1d | ||
|
|
11a195747a | ||
|
|
89690c9843 | ||
|
|
935885d396 | ||
|
|
611f8b80c2 | ||
|
|
8f848c2b1d | ||
|
|
a10a64f984 | ||
|
|
1434881567 | ||
|
|
fa21c44ee7 | ||
|
|
dbb81b5116 | ||
|
|
f7596fd43a | ||
|
|
f7a950a638 | ||
|
|
fbe7ed5699 | ||
|
|
0004173ecd | ||
|
|
1e5de4067b | ||
|
|
04b80760d8 | ||
|
|
a88393a7df | ||
|
|
8e8a62ae29 | ||
|
|
223533acec | ||
|
|
bcc7d37546 | ||
|
|
c9aee893d2 | ||
|
|
a1dcadf81b | ||
|
|
c86652a5c4 | ||
|
|
157cb57ea7 | ||
|
|
ad8feaad12 | ||
|
|
f54315b51c | ||
|
|
fd18476f4b | ||
|
|
8e3ff3d63b | ||
|
|
04b027d495 | ||
|
|
b93ad16e5a | ||
|
|
68b48617c7 | ||
|
|
59d5b95483 | ||
|
|
0a49da1b91 | ||
|
|
260adcdc15 | ||
|
|
4fbc9b33ad | ||
|
|
9e397ef4e9 | ||
|
|
70c729d389 | ||
|
|
23c151c8dc | ||
|
|
b7434c7b76 | ||
|
|
5a8da46952 | ||
|
|
f6b0b04f5b |
@@ -23,5 +23,9 @@ CheckOptions:
|
||||
value: 255.0;1.0;
|
||||
- key: readability-function-cognitive-complexity.IgnoreMacros
|
||||
value: true
|
||||
- key: readability-function-cognitive-complexity.Threshold
|
||||
value: 50
|
||||
- key: readability-function-cognitive-complexity.DescribeBasicIncrements
|
||||
value: true
|
||||
- key: bugprone-signed-char-misuse.CharTypdefsToIgnore
|
||||
value: int8_t
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,6 +16,7 @@ compton
|
||||
build/
|
||||
compile_commands.json
|
||||
build.ninja
|
||||
make.sh
|
||||
|
||||
# language servers
|
||||
.ccls-cache
|
||||
|
||||
11
README.md
11
README.md
@@ -85,12 +85,23 @@ $ ninja -C build
|
||||
|
||||
### To install
|
||||
|
||||
#### AUR (arch)
|
||||
- picom-ftlabs-git
|
||||
Thanks to @Fxzzi for maintaining the package.
|
||||
|
||||
|
||||
``` bash
|
||||
$ ninja -C build install
|
||||
```
|
||||
|
||||
Default install prefix is `/usr/local`, you can change it with `meson configure -Dprefix=<path> build`
|
||||
|
||||
## Running
|
||||
To launch with all animations as a background process you can use:
|
||||
`picom --animations -b`
|
||||
|
||||
To only have specific animations, enable them with cli flags (see `picom --help`) or add them to your picom config.
|
||||
|
||||
## How to Contribute
|
||||
|
||||
All contributions are welcome!
|
||||
|
||||
@@ -1,29 +1,76 @@
|
||||
#################################
|
||||
# Animations #
|
||||
|
||||
# !These animations WILL NOT work correctly for any other wm other than phyOS-dwm fork!
|
||||
|
||||
# fly-in: Windows fly in from random directions to the screen
|
||||
# maximize: Windows pop from center of the screen to their respective positions
|
||||
# minimize: Windows minimize from their position to the center of the screen
|
||||
# slide-in-center: Windows move from upper-center of the screen to their respective positions
|
||||
# slide-out-center: Windows move to the upper-center of the screen
|
||||
# slide-left: Windows are created from the right-most window position and slide leftwards
|
||||
# slide right: Windows are created from the left-most window position and slide rightwards
|
||||
# slide-down: Windows are moved from the top of the screen and slide downward
|
||||
# slide-up: Windows are moved from their position to top of the screen
|
||||
# squeeze: Windows are either closed or created to/from their center y-position (the animation is similar to a blinking eye)
|
||||
# squeeze-bottom: Similar to squeeze, but the animation starts from bottom-most y-position
|
||||
# zoom: Windows are either created or destroyed from/to their center (not the screen center)
|
||||
|
||||
#################################
|
||||
|
||||
#enable or disable animations
|
||||
animations = true;
|
||||
#change animation speed of windows in current tag e.g open window in current tag
|
||||
animation-stiffness-in-tag = 125;
|
||||
#change animation speed of windows when tag changes
|
||||
animation-stiffness-tag-change = 90.0;
|
||||
|
||||
animation-window-mass = 0.4;
|
||||
animation-dampening = 15;
|
||||
animation-clamping = true;
|
||||
|
||||
#open windows
|
||||
animation-for-open-window = "zoom";
|
||||
#minimize or close windows
|
||||
animation-for-unmap-window = "squeeze";
|
||||
#popup windows
|
||||
animation-for-transient-window = "slide-up"; #available options: slide-up, slide-down, slide-left, slide-right, squeeze, squeeze-bottom, zoom
|
||||
|
||||
#set animation for windows being transitioned out while changings tags
|
||||
animation-for-prev-tag = "minimize";
|
||||
#enables fading for windows being transitioned out while changings tags
|
||||
enable-fading-prev-tag = true;
|
||||
|
||||
#set animation for windows being transitioned in while changings tags
|
||||
animation-for-next-tag = "slide-in-center";
|
||||
#enables fading for windows being transitioned in while changings tags
|
||||
enable-fading-next-tag = true;
|
||||
|
||||
#################################
|
||||
# Shadows #
|
||||
#################################
|
||||
|
||||
|
||||
# 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.
|
||||
#
|
||||
# shadow = false
|
||||
shadow = true;
|
||||
shadow = false;
|
||||
|
||||
# The blur radius for shadows, in pixels. (defaults to 12)
|
||||
# shadow-radius = 12
|
||||
shadow-radius = 7;
|
||||
shadow-radius = 60;
|
||||
|
||||
# The opacity of shadows. (0.0 - 1.0, defaults to 0.75)
|
||||
# shadow-opacity = .75
|
||||
|
||||
# The left offset for shadows, in pixels. (defaults to -15)
|
||||
# shadow-offset-x = -15
|
||||
shadow-offset-x = -7;
|
||||
shadow-offset-x = -20;
|
||||
|
||||
# The top offset for shadows, in pixels. (defaults to -15)
|
||||
# shadow-offset-y = -15
|
||||
shadow-offset-y = -7;
|
||||
shadow-offset-y = -20;
|
||||
|
||||
# Red color value of shadow (0.0 - 1.0, defaults to 0).
|
||||
# shadow-red = 0
|
||||
@@ -48,6 +95,9 @@ shadow-exclude = [
|
||||
"class_g = 'Conky'",
|
||||
"class_g ?= 'Notify-osd'",
|
||||
"class_g = 'Cairo-clock'",
|
||||
"class_g = 'dwm'",
|
||||
"class_g = 'chromium'",
|
||||
"class_g *?= 'slop'",
|
||||
"_GTK_FRAME_EXTENTS@:c"
|
||||
];
|
||||
|
||||
@@ -73,19 +123,18 @@ shadow-exclude = [
|
||||
|
||||
# Fade windows in/out when opening/closing and when opacity changes,
|
||||
# unless no-fading-openclose is used.
|
||||
# fading = false
|
||||
fading = true;
|
||||
|
||||
# Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028)
|
||||
# fade-in-step = 0.028
|
||||
fade-in-step = 0.03;
|
||||
fade-in-step = 0.023;
|
||||
|
||||
# Opacity change between steps while fading out. (0.01 - 1.0, defaults to 0.03)
|
||||
# fade-out-step = 0.03
|
||||
fade-out-step = 0.03;
|
||||
fade-out-step = 0.035;
|
||||
|
||||
# The time between steps in fade step, in milliseconds. (> 0, defaults to 10)
|
||||
# fade-delta = 10
|
||||
fade-delta = 10
|
||||
|
||||
# Specify a list of conditions of windows that should not be faded.
|
||||
# fade-exclude = []
|
||||
@@ -104,15 +153,13 @@ fade-out-step = 0.03;
|
||||
|
||||
# Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
|
||||
# inactive-opacity = 1
|
||||
inactive-opacity = 0.8;
|
||||
|
||||
# Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default)
|
||||
# frame-opacity = 1.0
|
||||
frame-opacity = 0.7;
|
||||
|
||||
# Let inactive opacity set by -i override the '_NET_WM_WINDOW_OPACITY' values of windows.
|
||||
# inactive-opacity-override = true
|
||||
inactive-opacity-override = false;
|
||||
inactive-opacity-override = true;
|
||||
|
||||
# Default opacity for active windows. (0.0 - 1.0, defaults to 1.0)
|
||||
# active-opacity = 1.0
|
||||
@@ -122,21 +169,13 @@ inactive-opacity-override = false;
|
||||
|
||||
# Specify a list of conditions of windows that should never be considered focused.
|
||||
# focus-exclude = []
|
||||
focus-exclude = [ "class_g = 'Cairo-clock'" ];
|
||||
focus-exclude = [
|
||||
"class_g = 'Cairo-clock'" ,
|
||||
];
|
||||
|
||||
# Use fixed inactive dim value, instead of adjusting according to window opacity.
|
||||
# inactive-dim-fixed = 1.0
|
||||
|
||||
# Specify a list of opacity rules, in the format `PERCENT:PATTERN`,
|
||||
# like `50:name *= "Firefox"`. picom-trans is recommended over this.
|
||||
# Note we don't make any guarantee about possible conflicts with other
|
||||
# programs that set '_NET_WM_WINDOW_OPACITY' on frame or client windows.
|
||||
# example:
|
||||
# opacity-rule = [ "80:class_g = 'URxvt'" ];
|
||||
#
|
||||
# opacity-rule = []
|
||||
|
||||
|
||||
#################################
|
||||
# Corners #
|
||||
#################################
|
||||
@@ -144,52 +183,21 @@ focus-exclude = [ "class_g = 'Cairo-clock'" ];
|
||||
# Sets the radius of rounded window corners. When > 0, the compositor will
|
||||
# round the corners of windows. Does not interact well with
|
||||
# `transparent-clipping`.
|
||||
corner-radius = 0
|
||||
corner-radius = 11;
|
||||
|
||||
# Exclude conditions for rounded corners.
|
||||
rounded-corners-exclude = [
|
||||
"window_type = 'dock'",
|
||||
"window_type = 'desktop'"
|
||||
];
|
||||
#rounded-corners-exclude = [
|
||||
# "window_type = 'dock'",
|
||||
# "window_type = 'desktop'"
|
||||
#];
|
||||
|
||||
|
||||
#################################
|
||||
# Background-Blurring #
|
||||
#################################
|
||||
|
||||
|
||||
# Parameters for background blurring, see the *BLUR* section for more information.
|
||||
# blur-method =
|
||||
# blur-size = 12
|
||||
#
|
||||
# blur-deviation = false
|
||||
#
|
||||
# blur-strength = 5
|
||||
|
||||
# Blur background of semi-transparent / ARGB windows.
|
||||
# Bad in performance, with driver-dependent behavior.
|
||||
# The name of the switch may change without prior notifications.
|
||||
#
|
||||
# blur-background = false
|
||||
|
||||
# Blur background of windows when the window frame is not opaque.
|
||||
# Implies:
|
||||
# blur-background
|
||||
# Bad in performance, with driver-dependent behavior. The name may change.
|
||||
#
|
||||
# blur-background-frame = false
|
||||
|
||||
|
||||
# Use fixed blur strength rather than adjusting according to window opacity.
|
||||
# blur-background-fixed = false
|
||||
|
||||
|
||||
# Specify the blur convolution kernel, with the following format:
|
||||
# example:
|
||||
# blur-kern = "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";
|
||||
#
|
||||
# blur-kern = ""
|
||||
blur-kern = "3x3box";
|
||||
blur: {
|
||||
method = "dual_kawase";
|
||||
strength = 9;
|
||||
background = true;
|
||||
background-frame = false;
|
||||
background-fixed = false;
|
||||
}
|
||||
|
||||
|
||||
# Exclude conditions for background blur.
|
||||
@@ -197,24 +205,25 @@ blur-kern = "3x3box";
|
||||
blur-background-exclude = [
|
||||
"window_type = 'dock'",
|
||||
"window_type = 'desktop'",
|
||||
"_GTK_FRAME_EXTENTS@:c"
|
||||
"_GTK_FRAME_EXTENTS@:c",
|
||||
"class_g = 'Chromium'",
|
||||
"class_g = 'Discord'",
|
||||
"class_g = 'Dunst'",
|
||||
"class_g = 'Peek'",
|
||||
"class_g *?= 'slop'",
|
||||
];
|
||||
|
||||
#################################
|
||||
# General Settings #
|
||||
#################################
|
||||
|
||||
# Enable remote control via D-Bus. See the man page for more details.
|
||||
# dbus = true
|
||||
|
||||
# Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers.
|
||||
# daemon = false
|
||||
|
||||
# Specify the backend to use: `xrender`, `glx`, `egl` or `xr_glx_hybrid`.
|
||||
# `xrender` is the default one.
|
||||
#
|
||||
# backend = "glx"
|
||||
backend = "xrender";
|
||||
backend = "glx"
|
||||
|
||||
# Use higher precision during rendering, and apply dither when presenting the
|
||||
# rendered screen. Reduces banding artifacts, but might cause performance
|
||||
@@ -222,8 +231,7 @@ backend = "xrender";
|
||||
dithered-present = false;
|
||||
|
||||
# Enable/disable VSync.
|
||||
# vsync = false
|
||||
vsync = true;
|
||||
# vsync = true
|
||||
|
||||
# Try to detect WM windows (a non-override-redirect window with no
|
||||
# child that has 'WM_STATE') and mark them as active.
|
||||
@@ -239,25 +247,25 @@ mark-ovredir-focused = true;
|
||||
# shaped windows. The accuracy is not very high, unfortunately.
|
||||
#
|
||||
# detect-rounded-corners = false
|
||||
detect-rounded-corners = true;
|
||||
detect-rounded-corners = false;
|
||||
|
||||
# Detect '_NET_WM_WINDOW_OPACITY' on client windows, useful for window managers
|
||||
# not passing '_NET_WM_WINDOW_OPACITY' of client windows to frame windows.
|
||||
#
|
||||
# detect-client-opacity = false
|
||||
detect-client-opacity = true;
|
||||
detect-client-opacity = false;
|
||||
|
||||
# Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window,
|
||||
# rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy,
|
||||
# provided that the WM supports it.
|
||||
#
|
||||
# use-ewmh-active-win = false
|
||||
use-ewmh-active-win = true;
|
||||
|
||||
# Unredirect all windows if a full-screen opaque window is detected,
|
||||
# to maximize performance for full-screen windows. Known to cause flickering
|
||||
# when redirecting/unredirecting windows.
|
||||
#
|
||||
# unredir-if-possible = false
|
||||
unredir-if-possible = false;
|
||||
|
||||
# Delay before unredirecting the window, in milliseconds. Defaults to 0.
|
||||
# unredir-if-possible-delay = 0
|
||||
@@ -301,7 +309,7 @@ detect-transient = true;
|
||||
# practically happened) and may not work with blur-background.
|
||||
# My tests show a 15% performance boost. Recommended.
|
||||
#
|
||||
# glx-no-stencil = false
|
||||
glx-no-stencil = true;
|
||||
|
||||
# GLX backend: Avoid rebinding pixmap on window damage.
|
||||
# Probably could improve performance on rapid window content changes,
|
||||
@@ -322,24 +330,18 @@ use-damage = true;
|
||||
# calls are finished before picom starts drawing. Needed on nvidia-drivers
|
||||
# with GLX backend for some users.
|
||||
#
|
||||
# xrender-sync-fence = false
|
||||
xrender-sync-fence = true;
|
||||
|
||||
# GLX backend: Use specified GLSL fragment shader for rendering window
|
||||
# contents. Read the man page for a detailed explanation of the interface.
|
||||
# GLX backend: Use specified GLSL fragment shader for rendering window contents.
|
||||
# See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl`
|
||||
# in the source tree for examples.
|
||||
#
|
||||
# window-shader-fg = "default"
|
||||
|
||||
# Use rules to set per-window shaders. Syntax is SHADER_PATH:PATTERN, similar
|
||||
# to opacity-rule. SHADER_PATH can be "default". This overrides window-shader-fg.
|
||||
#
|
||||
# window-shader-fg-rule = [
|
||||
# "my_shader.frag:window_type != 'dock'"
|
||||
# ]
|
||||
window-shader-fg = "default";
|
||||
|
||||
# Force all windows to be painted with blending. Useful if you
|
||||
# have a glx-fshader-win that could turn opaque pixels transparent.
|
||||
#
|
||||
# force-win-blend = false
|
||||
# force-win-blend = true;
|
||||
|
||||
# Do not use EWMH to detect fullscreen windows.
|
||||
# Reverts to checking if a window is fullscreen based only on its size and coordinates.
|
||||
@@ -356,7 +358,7 @@ use-damage = true;
|
||||
# Make transparent windows clip other windows like non-transparent windows do,
|
||||
# instead of blending on top of them.
|
||||
#
|
||||
# transparent-clipping = false
|
||||
transparent-clipping = false;
|
||||
|
||||
# Specify a list of conditions of windows that should never have transparent
|
||||
# clipping applied. Useful for screenshot tools, where you need to be able to
|
||||
@@ -429,3 +431,9 @@ wintypes:
|
||||
popup_menu = { opacity = 0.8; }
|
||||
dropdown_menu = { opacity = 0.8; }
|
||||
};
|
||||
|
||||
opacity-rule = [
|
||||
"100:class_g = 'St' && focused",
|
||||
"50:class_g = 'St' && !focused",
|
||||
"100:fullscreen",
|
||||
];
|
||||
|
||||
51
src/atom.c
51
src/atom.c
@@ -2,12 +2,20 @@
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "atom.h"
|
||||
#include "cache.h"
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
#include "compiler.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
|
||||
xcb_connection_t *c = ud;
|
||||
struct atom_entry {
|
||||
struct cache_handle entry;
|
||||
xcb_atom_t atom;
|
||||
};
|
||||
|
||||
static inline int atom_getter(struct cache *cache attr_unused, const char *atom_name,
|
||||
struct cache_handle **value, void *user_data) {
|
||||
xcb_connection_t *c = user_data;
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
||||
c, xcb_intern_atom(c, 0, to_u16_checked(strlen(atom_name)), atom_name), NULL);
|
||||
|
||||
@@ -18,9 +26,32 @@ static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
|
||||
free(reply);
|
||||
} else {
|
||||
log_error("Failed to intern atoms");
|
||||
*err = 1;
|
||||
return -1;
|
||||
}
|
||||
return (void *)(intptr_t)atom;
|
||||
|
||||
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
||||
entry->atom = atom;
|
||||
*value = &entry->entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
atom_entry_free(struct cache *cache attr_unused, struct cache_handle *handle) {
|
||||
struct atom_entry *entry = cache_entry(handle, struct atom_entry, entry);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
xcb_atom_t get_atom(struct atom *a, const char *key, xcb_connection_t *c) {
|
||||
struct cache_handle *entry = NULL;
|
||||
if (cache_get_or_fetch(&a->c, key, &entry, c, atom_getter) < 0) {
|
||||
log_error("Failed to get atom %s", key);
|
||||
return XCB_NONE;
|
||||
}
|
||||
return cache_entry(entry, struct atom_entry, entry)->atom;
|
||||
}
|
||||
|
||||
xcb_atom_t get_atom_cached(struct atom *a, const char *key) {
|
||||
return cache_entry(cache_get(&a->c, key), struct atom_entry, entry)->atom;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -28,11 +59,15 @@ static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
|
||||
*/
|
||||
struct atom *init_atoms(xcb_connection_t *c) {
|
||||
auto atoms = ccalloc(1, struct atom);
|
||||
atoms->c = new_cache((void *)c, atom_getter, NULL);
|
||||
#define ATOM_GET(x) \
|
||||
atoms->a##x = (xcb_atom_t)(intptr_t)cache_get_or_fetch(atoms->c, #x, NULL)
|
||||
atoms->c = CACHE_INIT;
|
||||
#define ATOM_GET(x) atoms->a##x = get_atom(atoms, #x, c)
|
||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
|
||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
|
||||
#undef ATOM_GET
|
||||
return atoms;
|
||||
}
|
||||
|
||||
void destroy_atoms(struct atom *a) {
|
||||
cache_invalidate_all(&a->c, atom_entry_free);
|
||||
free(a);
|
||||
}
|
||||
|
||||
23
src/atom.h
23
src/atom.h
@@ -1,6 +1,4 @@
|
||||
#pragma once
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "cache.h"
|
||||
@@ -53,23 +51,18 @@
|
||||
|
||||
#define ATOM_DEF(x) xcb_atom_t a##x
|
||||
|
||||
struct atom_entry;
|
||||
struct atom {
|
||||
struct cache *c;
|
||||
struct cache c;
|
||||
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST1);
|
||||
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST2);
|
||||
};
|
||||
|
||||
struct atom *init_atoms(xcb_connection_t *);
|
||||
/// Create a new atom object with a xcb connection. `struct atom` does not hold
|
||||
/// a reference to the connection.
|
||||
struct atom *init_atoms(xcb_connection_t *c);
|
||||
|
||||
static inline xcb_atom_t get_atom(struct atom *a, const char *key) {
|
||||
return (xcb_atom_t)(intptr_t)cache_get_or_fetch(a->c, key, NULL);
|
||||
}
|
||||
xcb_atom_t get_atom(struct atom *a, const char *key, xcb_connection_t *c);
|
||||
xcb_atom_t get_atom_cached(struct atom *a, const char *key);
|
||||
|
||||
static inline xcb_atom_t get_atom_cached(struct atom *a, const char *key) {
|
||||
return (xcb_atom_t)(intptr_t)cache_get(a->c, key);
|
||||
}
|
||||
|
||||
static inline void destroy_atoms(struct atom *a) {
|
||||
cache_free(a->c);
|
||||
free(a);
|
||||
}
|
||||
void destroy_atoms(struct atom *a);
|
||||
|
||||
@@ -56,6 +56,58 @@ region_t get_damage(session_t *ps, bool all_damage) {
|
||||
return region;
|
||||
}
|
||||
|
||||
static void process_window_for_painting(session_t *ps, struct managed_win *w,
|
||||
void *win_image, double additional_alpha,
|
||||
region_t *reg_bound, region_t *reg_visible,
|
||||
region_t *reg_paint, region_t *reg_paint_in_bound) {
|
||||
// For window image processing, we don't have to limit the process
|
||||
// region to damage for correctness. (see <damager-note> for
|
||||
// details)
|
||||
|
||||
// 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
|
||||
coord_t window_coord = {.x = w->g.x, .y = w->g.y};
|
||||
coord_t dest_coord = {.x = w->g.x + w->widthb, .y = w->g.y + w->heightb};
|
||||
|
||||
region_t reg_visible_local;
|
||||
region_t reg_bound_local;
|
||||
{
|
||||
// The bounding shape, in window local coordinates
|
||||
pixman_region32_init(®_bound_local);
|
||||
pixman_region32_copy(®_bound_local, reg_bound);
|
||||
pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y);
|
||||
|
||||
pixman_region32_init(®_visible_local);
|
||||
pixman_region32_intersect(®_visible_local, reg_visible, reg_paint);
|
||||
pixman_region32_translate(®_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(®_visible_local, ®_visible_local,
|
||||
®_bound_local);
|
||||
}
|
||||
|
||||
auto new_img = ps->backend_data->ops->clone_image(ps->backend_data, win_image,
|
||||
®_visible_local);
|
||||
auto reg_frame = win_get_region_frame_local_by_val(w);
|
||||
double alpha = additional_alpha * w->opacity;
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_OPACITY, new_img, &alpha);
|
||||
ps->backend_data->ops->image_op(ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img,
|
||||
®_frame, ®_visible_local,
|
||||
(double[]){w->frame_opacity});
|
||||
pixman_region32_fini(®_frame);
|
||||
ps->backend_data->ops->compose(ps->backend_data, new_img,
|
||||
window_coord, NULL, dest_coord,
|
||||
reg_paint_in_bound, reg_visible, true);
|
||||
ps->backend_data->ops->release_image(ps->backend_data, new_img);
|
||||
pixman_region32_fini(®_visible_local);
|
||||
pixman_region32_fini(®_bound_local);
|
||||
}
|
||||
|
||||
void handle_device_reset(session_t *ps) {
|
||||
log_error("Device reset detected");
|
||||
// Wait for reset to complete
|
||||
@@ -210,8 +262,8 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
|
||||
if (ps->root_image) {
|
||||
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
|
||||
(coord_t){0}, NULL, (coord_t){0},
|
||||
®_paint, ®_visible);
|
||||
(coord_t){0}, NULL, (coord_t){.x = ps->root_width, .y = ps->root_height},
|
||||
®_paint, ®_visible, true);
|
||||
} else {
|
||||
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
|
||||
®_paint);
|
||||
@@ -262,6 +314,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
* option */
|
||||
auto real_win_mode = w->mode;
|
||||
coord_t window_coord = {.x = w->g.x, .y = w->g.y};
|
||||
coord_t dest_coord = {.x = w->g.x + w->widthb, .y = w->g.y + w->heightb};
|
||||
|
||||
if (w->blur_background &&
|
||||
(ps->o.force_win_blend || real_win_mode == WMODE_TRANS ||
|
||||
@@ -390,7 +443,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
}
|
||||
ps->backend_data->ops->compose(
|
||||
ps->backend_data, w->shadow_image, shadow_coord,
|
||||
inverted_mask, window_coord, ®_shadow, ®_visible);
|
||||
inverted_mask, window_coord, ®_shadow, ®_visible, false);
|
||||
if (inverted_mask) {
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_INVERTED,
|
||||
@@ -434,6 +487,17 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_BORDER_WIDTH,
|
||||
w->win_image, &border_width);
|
||||
if (w->old_win_image) {
|
||||
// TODO(dccsillag): explain why the following is
|
||||
// "necessary"
|
||||
double zero = 0.0;
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_BORDER_WIDTH,
|
||||
w->old_win_image, &zero);
|
||||
ps->backend_data->ops->set_image_property(
|
||||
ps->backend_data, IMAGE_PROPERTY_CORNER_RADIUS,
|
||||
w->old_win_image, &zero);
|
||||
}
|
||||
}
|
||||
|
||||
ps->backend_data->ops->set_image_property(
|
||||
@@ -456,53 +520,43 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
}
|
||||
|
||||
// Draw window on target
|
||||
if (w->frame_opacity == 1) {
|
||||
bool is_animating = 0 <= w->animation_progress && w->animation_progress < 1.0;
|
||||
if (w->frame_opacity == 1 && !is_animating) {
|
||||
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
|
||||
window_coord, NULL, window_coord,
|
||||
®_paint_in_bound, ®_visible);
|
||||
window_coord, NULL, dest_coord,
|
||||
®_paint_in_bound, ®_visible, true);
|
||||
} else {
|
||||
// For window image processing, we don't have to limit the process
|
||||
// region to damage for correctness. (see <damager-note> for
|
||||
// details)
|
||||
if (is_animating && w->old_win_image) {
|
||||
bool is_focused = win_is_focused_raw(w);
|
||||
if (!is_focused && w->focused && w->opacity_is_set)
|
||||
is_focused = true;
|
||||
assert(w->old_win_image);
|
||||
|
||||
// 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;
|
||||
region_t reg_bound_local;
|
||||
{
|
||||
// The bounding shape, in window local coordinates
|
||||
pixman_region32_init(®_bound_local);
|
||||
pixman_region32_copy(®_bound_local, ®_bound);
|
||||
pixman_region32_translate(®_bound_local, -w->g.x, -w->g.y);
|
||||
bool resizing =
|
||||
w->g.width != w->pending_g.width ||
|
||||
w->g.height != w->pending_g.height;
|
||||
|
||||
pixman_region32_init(®_visible_local);
|
||||
pixman_region32_intersect(®_visible_local,
|
||||
®_visible, ®_paint);
|
||||
pixman_region32_translate(®_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(
|
||||
®_visible_local, ®_visible_local, ®_bound_local);
|
||||
// Only animate opacity here if we are resizing
|
||||
// a transparent window
|
||||
process_window_for_painting(ps, w, w->win_image,
|
||||
is_focused ? 1.0 : w->opacity >= 1 ? 1.0 : w->animation_progress,
|
||||
®_bound, ®_visible,
|
||||
®_paint, ®_paint_in_bound);
|
||||
|
||||
// Only do this if size changes as otherwise moving
|
||||
// transparent windows will flicker and if you just
|
||||
// move so slightly they will keep flickering
|
||||
if (resizing && (!is_focused || !w->opacity_is_set)) {
|
||||
process_window_for_painting(ps, w, w->old_win_image,
|
||||
1.0 - w->animation_progress,
|
||||
®_bound, ®_visible,
|
||||
®_paint, ®_paint_in_bound);
|
||||
}
|
||||
} else {
|
||||
process_window_for_painting(
|
||||
ps, w, w->win_image, 1.0, ®_bound, ®_visible,
|
||||
®_paint, ®_paint_in_bound);
|
||||
}
|
||||
|
||||
auto new_img = ps->backend_data->ops->clone_image(
|
||||
ps->backend_data, w->win_image, ®_visible_local);
|
||||
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, ®_frame,
|
||||
®_visible_local, (double[]){w->frame_opacity});
|
||||
pixman_region32_fini(®_frame);
|
||||
ps->backend_data->ops->compose(ps->backend_data, new_img,
|
||||
window_coord, NULL, window_coord,
|
||||
®_paint_in_bound, ®_visible);
|
||||
ps->backend_data->ops->release_image(ps->backend_data, new_img);
|
||||
pixman_region32_fini(®_visible_local);
|
||||
pixman_region32_fini(®_bound_local);
|
||||
}
|
||||
skip:
|
||||
pixman_region32_fini(®_bound);
|
||||
|
||||
@@ -181,7 +181,7 @@ struct backend_operations {
|
||||
*/
|
||||
void (*compose)(backend_t *backend_data, image_handle image, coord_t image_dst,
|
||||
image_handle mask, coord_t mask_dst, const region_t *reg_paint,
|
||||
const region_t *reg_visible) attr_nonnull(1, 2, 6, 7);
|
||||
const region_t *reg_visible, bool lerp) attr_nonnull(1, 2, 6, 7);
|
||||
|
||||
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
|
||||
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
||||
|
||||
@@ -68,7 +68,7 @@ static void dummy_check_image(struct backend_base *base, image_handle image) {
|
||||
void dummy_compose(struct backend_base *base, image_handle image, coord_t dst attr_unused,
|
||||
image_handle mask attr_unused, coord_t mask_dst attr_unused,
|
||||
const region_t *reg_paint attr_unused,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
const region_t *reg_visible attr_unused, bool lerp attr_unused) {
|
||||
auto dummy attr_unused = (struct dummy_data *)base;
|
||||
dummy_check_image(base, image);
|
||||
assert(mask == NULL || (struct backend_image *)mask == &dummy->mask);
|
||||
|
||||
@@ -420,7 +420,7 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
|
||||
}
|
||||
|
||||
glUniform1i(win_shader->uniform_mask_tex, 2);
|
||||
glUniform2f(win_shader->uniform_mask_offset, (float)mask_offset.x,
|
||||
glUniform2f(win_shader->uniform_mask_offset, (float)mask_offset.x ,
|
||||
(float)mask_offset.y);
|
||||
if (mask != NULL) {
|
||||
glUniform1i(win_shader->uniform_mask_inverted, mask->color_inverted);
|
||||
@@ -502,6 +502,7 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
|
||||
image_dst.y = root_height - image_dst.y;
|
||||
image_dst.y -= extent_height;
|
||||
|
||||
|
||||
for (int i = 0; i < nrects; i++) {
|
||||
// Y-flip. Note after this, crect.y1 > crect.y2
|
||||
rect_t crect = rects[i];
|
||||
@@ -551,11 +552,11 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
|
||||
}
|
||||
|
||||
// TODO(yshui) make use of reg_visible
|
||||
void gl_compose(backend_t *base, image_handle image, coord_t image_dst,
|
||||
image_handle mask_, coord_t mask_dst, const region_t *reg_tgt,
|
||||
const region_t *reg_visible attr_unused) {
|
||||
void gl_compose(backend_t *base, image_handle image_data, coord_t image_dst, image_handle mask_,
|
||||
coord_t mask_dst, const region_t *reg_tgt,
|
||||
const region_t *reg_visible attr_unused, bool lerp) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
auto img = (struct backend_image *)image;
|
||||
auto img = (struct backend_image *)image_data;
|
||||
auto mask = (struct backend_image *)mask_;
|
||||
auto inner = (struct gl_texture *)img->inner;
|
||||
|
||||
@@ -580,6 +581,14 @@ void gl_compose(backend_t *base, image_handle image, coord_t image_dst,
|
||||
coord_t mask_offset = {.x = mask_dst.x - image_dst.x, .y = mask_dst.y - image_dst.y};
|
||||
x_rect_to_coords(nrects, rects, image_dst, inner->height, inner->height,
|
||||
gd->height, inner->y_inverted, coord, indices);
|
||||
|
||||
if (lerp) {
|
||||
for (unsigned int i = 2; i < 16; i+=4) {
|
||||
coord[i+0] = lerp_range(0, mask_offset.x, 0, inner->width, coord[i+0]);
|
||||
coord[i+1] = lerp_range(0, mask_offset.y, 0, inner->height, coord[i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
_gl_compose(base, img, gd->back_fbo, mask, mask_offset, coord, indices, nrects);
|
||||
|
||||
free(indices);
|
||||
|
||||
@@ -150,8 +150,8 @@ bool gl_last_render_time(backend_t *backend_data, struct timespec *time);
|
||||
/**
|
||||
* @brief Render a region with texture data.
|
||||
*/
|
||||
void gl_compose(backend_t *, image_handle image, coord_t image_dst, image_handle mask,
|
||||
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible);
|
||||
void gl_compose(backend_t *, image_handle image_data, coord_t image_dst, image_handle mask_,
|
||||
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible, bool lerp);
|
||||
|
||||
void gl_resize(struct gl_data *, int width, int height);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "backend/backend.h"
|
||||
#include "backend/backend_common.h"
|
||||
#include "common.h"
|
||||
#include "compiler.h"
|
||||
#include "config.h"
|
||||
#include "kernel.h"
|
||||
#include "log.h"
|
||||
@@ -360,7 +361,7 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
|
||||
}
|
||||
|
||||
static void compose(backend_t *base, image_handle image_, coord_t dst, image_handle mask_,
|
||||
coord_t mask_dst, const region_t *reg_paint, const region_t *reg_visible) {
|
||||
coord_t mask_dst, const region_t *reg_paint, const region_t *reg_visible, attr_unused bool lerp) {
|
||||
struct _xrender_data *xd = (void *)base;
|
||||
auto image = (struct xrender_image *)image_;
|
||||
auto mask = (struct xrender_image *)mask_;
|
||||
|
||||
5
src/c2.c
5
src/c2.c
@@ -1047,7 +1047,7 @@ static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
|
||||
|
||||
// Get target atom if it's not a predefined one
|
||||
if (pleaf->predef == C2_L_PUNDEFINED) {
|
||||
pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt);
|
||||
pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt, ps->c.c);
|
||||
if (!pleaf->tgtatom) {
|
||||
log_error("Failed to get atom for target \"%s\".", pleaf->tgt);
|
||||
return false;
|
||||
@@ -1509,7 +1509,8 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
|
||||
else {
|
||||
char **strlst = NULL;
|
||||
int nstr = 0;
|
||||
if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, &nstr)) {
|
||||
if (wid_get_text_prop(&ps->c, ps->atoms, wid, pleaf->tgtatom,
|
||||
&strlst, &nstr)) {
|
||||
if (pleaf->index < 0 && nstr > 0 && strlen(strlst[0]) > 0) {
|
||||
ntargets = to_u32_checked(nstr);
|
||||
targets = (const char **)strlst;
|
||||
|
||||
101
src/cache.c
101
src/cache.c
@@ -1,94 +1,43 @@
|
||||
#include <uthash.h>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "utils.h"
|
||||
#include "cache.h"
|
||||
|
||||
struct cache_entry {
|
||||
char *key;
|
||||
void *value;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
struct cache {
|
||||
cache_getter_t getter;
|
||||
cache_free_t free;
|
||||
void *user_data;
|
||||
struct cache_entry *entries;
|
||||
};
|
||||
|
||||
static inline struct cache_entry *cache_get_entry(struct cache *c, const char *key) {
|
||||
struct cache_entry *e;
|
||||
struct cache_handle *cache_get(struct cache *c, const char *key) {
|
||||
struct cache_handle *e;
|
||||
HASH_FIND_STR(c->entries, key, e);
|
||||
return e;
|
||||
}
|
||||
|
||||
void *cache_get(struct cache *c, const char *key) {
|
||||
struct cache_entry *e = cache_get_entry(c, key);
|
||||
return e ? e->value : NULL;
|
||||
int cache_get_or_fetch(struct cache *c, const char *key, struct cache_handle **value,
|
||||
void *user_data, cache_getter_t getter) {
|
||||
*value = cache_get(c, key);
|
||||
if (*value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int err = getter(c, key, value, user_data);
|
||||
assert(err <= 0);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
(*value)->key = strdup(key);
|
||||
|
||||
HASH_ADD_STR(c->entries, key, *value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *cache_get_or_fetch(struct cache *c, const char *key, int *err) {
|
||||
struct cache_entry *e = cache_get_entry(c, key);
|
||||
if (e) {
|
||||
return e->value;
|
||||
}
|
||||
|
||||
int tmperr;
|
||||
if (!err) {
|
||||
err = &tmperr;
|
||||
}
|
||||
|
||||
*err = 0;
|
||||
e = ccalloc(1, struct cache_entry);
|
||||
e->key = strdup(key);
|
||||
e->value = c->getter(c->user_data, key, err);
|
||||
if (*err) {
|
||||
free(e->key);
|
||||
free(e);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HASH_ADD_STR(c->entries, key, e);
|
||||
return e->value;
|
||||
}
|
||||
|
||||
static inline void cache_invalidate_impl(struct cache *c, struct cache_entry *e) {
|
||||
if (c->free) {
|
||||
c->free(c->user_data, e->value);
|
||||
}
|
||||
static inline void
|
||||
cache_invalidate_impl(struct cache *c, struct cache_handle *e, cache_free_t free_fn) {
|
||||
free(e->key);
|
||||
HASH_DEL(c->entries, e);
|
||||
free(e);
|
||||
}
|
||||
|
||||
void cache_invalidate(struct cache *c, const char *key) {
|
||||
struct cache_entry *e;
|
||||
HASH_FIND_STR(c->entries, key, e);
|
||||
|
||||
if (e) {
|
||||
cache_invalidate_impl(c, e);
|
||||
if (free_fn) {
|
||||
free_fn(c, e);
|
||||
}
|
||||
}
|
||||
|
||||
void cache_invalidate_all(struct cache *c) {
|
||||
struct cache_entry *e, *tmpe;
|
||||
void cache_invalidate_all(struct cache *c, cache_free_t free_fn) {
|
||||
struct cache_handle *e, *tmpe;
|
||||
HASH_ITER(hh, c->entries, e, tmpe) {
|
||||
cache_invalidate_impl(c, e);
|
||||
cache_invalidate_impl(c, e, free_fn);
|
||||
}
|
||||
}
|
||||
|
||||
void *cache_free(struct cache *c) {
|
||||
void *ret = c->user_data;
|
||||
cache_invalidate_all(c);
|
||||
free(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct cache *new_cache(void *ud, cache_getter_t getter, cache_free_t f) {
|
||||
auto c = ccalloc(1, struct cache);
|
||||
c->user_data = ud;
|
||||
c->getter = getter;
|
||||
c->free = f;
|
||||
return c;
|
||||
}
|
||||
|
||||
49
src/cache.h
49
src/cache.h
@@ -1,30 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <uthash.h>
|
||||
#include "utils.h"
|
||||
|
||||
#define cache_entry(ptr, type, member) container_of(ptr, type, member)
|
||||
|
||||
struct cache;
|
||||
struct cache_handle;
|
||||
|
||||
typedef void *(*cache_getter_t)(void *user_data, const char *key, int *err);
|
||||
typedef void (*cache_free_t)(void *user_data, void *data);
|
||||
/// User-provided function to fetch a value for the cache, when it's not present.
|
||||
/// Should return 0 if the value is fetched successfully, and a negative number if the
|
||||
/// value cannot be fetched. Getter doesn't need to initialize fields of `struct
|
||||
/// cache_handle`.
|
||||
typedef int (*cache_getter_t)(struct cache *, const char *key,
|
||||
struct cache_handle **value, void *user_data);
|
||||
typedef void (*cache_free_t)(struct cache *, struct cache_handle *value);
|
||||
|
||||
/// Create a cache with `getter`, and a free function `f` which is used to free the cache
|
||||
/// value when they are invalidated.
|
||||
///
|
||||
/// `user_data` will be passed to `getter` and `f` when they are called.
|
||||
struct cache *new_cache(void *user_data, cache_getter_t getter, cache_free_t f);
|
||||
struct cache {
|
||||
struct cache_handle *entries;
|
||||
};
|
||||
|
||||
static const struct cache CACHE_INIT = {NULL};
|
||||
|
||||
struct cache_handle {
|
||||
char *key;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
/// Get a value from the cache. If the value doesn't present in the cache yet, the
|
||||
/// getter will be called, and the returned value will be stored into the cache.
|
||||
void *cache_get_or_fetch(struct cache *, const char *key, int *err);
|
||||
/// Returns 0 if the value is already present in the cache, 1 if the value is fetched
|
||||
/// successfully, and a negative number if the value cannot be fetched.
|
||||
int cache_get_or_fetch(struct cache *, const char *key, struct cache_handle **value,
|
||||
void *user_data, cache_getter_t getter);
|
||||
|
||||
/// Get a value from the cache. If the value doesn't present in the cache, NULL will be
|
||||
/// returned.
|
||||
void *cache_get(struct cache *, const char *key);
|
||||
struct cache_handle *cache_get(struct cache *, const char *key);
|
||||
|
||||
/// Invalidate a value in the cache.
|
||||
void cache_invalidate(struct cache *, const char *key);
|
||||
|
||||
/// Invalidate all values in the cache.
|
||||
void cache_invalidate_all(struct cache *);
|
||||
|
||||
/// Invalidate all values in the cache and free it. Returns the user data passed to
|
||||
/// `new_cache`
|
||||
void *cache_free(struct cache *);
|
||||
/// Invalidate all values in the cache. After this call, `struct cache` holds no allocated
|
||||
/// memory, and can be discarded.
|
||||
void cache_invalidate_all(struct cache *, cache_free_t free_fn);
|
||||
|
||||
13
src/common.h
13
src/common.h
@@ -143,6 +143,11 @@ typedef struct session {
|
||||
ev_timer unredir_timer;
|
||||
/// Timer for fading
|
||||
ev_timer fade_timer;
|
||||
/// Timer for animations
|
||||
ev_timer animation_timer;
|
||||
/// Use an ev_idle callback for drawing
|
||||
/// So we only start drawing when events are processed
|
||||
ev_idle draw_idle;
|
||||
/// Use an ev_timer callback for drawing
|
||||
ev_timer draw_timer;
|
||||
/// Called every time we have timeouts or new data on socket,
|
||||
@@ -175,7 +180,6 @@ typedef struct session {
|
||||
bool server_grabbed;
|
||||
/// Width of root window.
|
||||
int root_width;
|
||||
/// Height of root window.
|
||||
int root_height;
|
||||
/// X Composite overlay window.
|
||||
xcb_window_t overlay;
|
||||
@@ -264,6 +268,13 @@ typedef struct session {
|
||||
xcb_render_picture_t *alpha_picts;
|
||||
/// Time of last fading. In milliseconds.
|
||||
long long fade_time;
|
||||
/// Time of last window animation step. In milliseconds.
|
||||
long animation_time; // TODO(dccsillag) turn into `long long`, like fade_time
|
||||
/// Head pointer of the error ignore linked list.
|
||||
pending_reply_t *pending_reply_head;
|
||||
/// Pointer to the <code>next</code> member of tail element of the error
|
||||
/// ignore linked list.
|
||||
pending_reply_t **pending_reply_tail;
|
||||
// Cached blur convolution kernels.
|
||||
struct x_convolution_kernel **blur_kerns_cache;
|
||||
/// If we should quit
|
||||
|
||||
53
src/config.c
53
src/config.c
@@ -796,6 +796,10 @@ void set_default_winopts(options_t *opt, win_option_mask_t *mask, bool shadow_en
|
||||
// opacity logic is complicated, and needs an "unset" state
|
||||
opt->wintype_option[i].opacity = NAN;
|
||||
}
|
||||
if (!mask[i].animation) {
|
||||
mask[i].animation = OPEN_WINDOW_ANIMATION_INVALID;
|
||||
opt->wintype_option[i].animation = OPEN_WINDOW_ANIMATION_INVALID;
|
||||
}
|
||||
if (!mask[i].clip_shadow_above) {
|
||||
mask[i].clip_shadow_above = true;
|
||||
opt->wintype_option[i].clip_shadow_above = false;
|
||||
@@ -803,6 +807,40 @@ void set_default_winopts(options_t *opt, win_option_mask_t *mask, bool shadow_en
|
||||
}
|
||||
}
|
||||
|
||||
enum open_window_animation parse_open_window_animation(const char *src) {
|
||||
if (strcmp(src, "none") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_NONE;
|
||||
} else if (strcmp(src, "fly-in") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_FLYIN;
|
||||
} else if (strcmp(src, "zoom") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_ZOOM;
|
||||
} else if (strcmp(src, "slide-up") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_UP;
|
||||
} else if (strcmp(src, "slide-down") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_DOWN;
|
||||
} else if (strcmp(src, "slide-left") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_LEFT;
|
||||
} else if (strcmp(src, "slide-right") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_RIGHT;
|
||||
} else if (strcmp(src, "slide-out") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_OUT;
|
||||
} else if (strcmp(src, "slide-in") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_IN;
|
||||
} else if (strcmp(src, "slide-out-center") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER;
|
||||
} else if (strcmp(src, "slide-in-center") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SLIDE_IN_CENTER;
|
||||
} else if (strcmp(src, "minimize") == 0 || strcmp(src, "maximize") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_MINIMIZE;
|
||||
} else if (strcmp(src, "squeeze") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SQUEEZE;
|
||||
} else if (strcmp(src, "squeeze-bottom") == 0) {
|
||||
return OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM;
|
||||
}
|
||||
|
||||
return OPEN_WINDOW_ANIMATION_INVALID;
|
||||
}
|
||||
|
||||
char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask) {
|
||||
// clang-format off
|
||||
@@ -848,6 +886,18 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||
.no_fading_destroyed_argb = false,
|
||||
.fade_blacklist = NULL,
|
||||
|
||||
.animations = false,
|
||||
.animation_for_open_window = OPEN_WINDOW_ANIMATION_NONE,
|
||||
.animation_for_transient_window = OPEN_WINDOW_ANIMATION_NONE,
|
||||
.animation_for_unmap_window = OPEN_WINDOW_ANIMATION_NONE,
|
||||
.animation_for_next_tag = OPEN_WINDOW_ANIMATION_NONE,
|
||||
.animation_for_prev_tag = OPEN_WINDOW_ANIMATION_NONE,
|
||||
.animation_stiffness = 200.0,
|
||||
.animation_stiffness_tag_change = 200.0,
|
||||
.animation_window_mass = 1.0,
|
||||
.animation_dampening = 25,
|
||||
.animation_clamping = true,
|
||||
|
||||
.inactive_opacity = 1.0,
|
||||
.inactive_opacity_override = false,
|
||||
.active_opacity = 1.0,
|
||||
@@ -879,7 +929,8 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||
|
||||
.track_leader = false,
|
||||
|
||||
.rounded_corners_blacklist = NULL
|
||||
.rounded_corners_blacklist = NULL,
|
||||
.animation_blacklist = NULL
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
55
src/config.h
55
src/config.h
@@ -40,6 +40,24 @@ enum backend {
|
||||
NUM_BKEND,
|
||||
};
|
||||
|
||||
enum open_window_animation {
|
||||
OPEN_WINDOW_ANIMATION_NONE = 0,
|
||||
OPEN_WINDOW_ANIMATION_FLYIN,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_UP,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_DOWN,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_LEFT,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_RIGHT,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_IN,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_OUT,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_IN_CENTER,
|
||||
OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER,
|
||||
OPEN_WINDOW_ANIMATION_ZOOM,
|
||||
OPEN_WINDOW_ANIMATION_MINIMIZE,
|
||||
OPEN_WINDOW_ANIMATION_SQUEEZE,
|
||||
OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM,
|
||||
OPEN_WINDOW_ANIMATION_INVALID,
|
||||
};
|
||||
|
||||
typedef struct win_option_mask {
|
||||
bool shadow : 1;
|
||||
bool fade : 1;
|
||||
@@ -49,6 +67,7 @@ typedef struct win_option_mask {
|
||||
bool redir_ignore : 1;
|
||||
bool opacity : 1;
|
||||
bool clip_shadow_above : 1;
|
||||
enum open_window_animation animation;
|
||||
} win_option_mask_t;
|
||||
|
||||
typedef struct win_option {
|
||||
@@ -60,6 +79,7 @@ typedef struct win_option {
|
||||
bool redir_ignore;
|
||||
double opacity;
|
||||
bool clip_shadow_above;
|
||||
enum open_window_animation animation;
|
||||
} win_option_t;
|
||||
|
||||
enum blur_method {
|
||||
@@ -195,6 +215,33 @@ typedef struct options {
|
||||
/// Fading blacklist. A linked list of conditions.
|
||||
c2_lptr_t *fade_blacklist;
|
||||
|
||||
// === Animations ===
|
||||
/// Whether to do window animations
|
||||
bool animations;
|
||||
/// Which animation to run when opening a window
|
||||
enum open_window_animation animation_for_open_window;
|
||||
/// Which animation to run when opening a transient window
|
||||
enum open_window_animation animation_for_transient_window;
|
||||
/// Which animation to run when unmapping a window
|
||||
enum open_window_animation animation_for_unmap_window;
|
||||
/// Which animation to run when swapping to new tag
|
||||
enum open_window_animation animation_for_next_tag;
|
||||
/// Which animation to run for old tag
|
||||
enum open_window_animation animation_for_prev_tag;
|
||||
/// Spring stiffness for animation
|
||||
double animation_stiffness;
|
||||
/// Spring stiffness for current tag animation
|
||||
double animation_stiffness_tag_change;
|
||||
/// Window mass for animation
|
||||
double animation_window_mass;
|
||||
/// Animation dampening
|
||||
double animation_dampening;
|
||||
/// Whether to clamp animations
|
||||
bool animation_clamping;
|
||||
/// Animation blacklist. A linked list of conditions.
|
||||
c2_lptr_t *animation_blacklist;
|
||||
/// TODO: open/close animations
|
||||
|
||||
// === Opacity ===
|
||||
/// Default opacity for inactive windows.
|
||||
/// 32-bit integer with the format of _NET_WM_WINDOW_OPACITY.
|
||||
@@ -278,6 +325,13 @@ typedef struct options {
|
||||
// Make transparent windows clip other windows, instead of blending on top of
|
||||
// them
|
||||
bool transparent_clipping;
|
||||
|
||||
// Enable fading for next tag
|
||||
bool enable_fading_next_tag;
|
||||
|
||||
// Enable fading for prev tag
|
||||
bool enable_fading_prev_tag;
|
||||
|
||||
/// A list of conditions of windows to which transparent clipping
|
||||
/// should not apply
|
||||
c2_lptr_t *transparent_clipping_blacklist;
|
||||
@@ -298,6 +352,7 @@ bool must_use parse_rule_window_shader(c2_lptr_t **, const char *, const char *)
|
||||
char *must_use locate_auxiliary_file(const char *scope, const char *path,
|
||||
const char *include_dir);
|
||||
enum blur_method must_use parse_blur_method(const char *src);
|
||||
enum open_window_animation must_use parse_open_window_animation(const char *src);
|
||||
|
||||
/**
|
||||
* Add a pattern to a condition linked list.
|
||||
|
||||
@@ -264,6 +264,15 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_
|
||||
o->clip_shadow_above = ival;
|
||||
mask->clip_shadow_above = true;
|
||||
}
|
||||
const char *sval = NULL;
|
||||
if (config_setting_lookup_string(setting, "animation", &sval)) {
|
||||
enum open_window_animation animation = parse_open_window_animation(sval);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID)
|
||||
animation = OPEN_WINDOW_ANIMATION_NONE;
|
||||
|
||||
o->animation = animation;
|
||||
mask->animation = animation;
|
||||
}
|
||||
|
||||
double fval;
|
||||
if (config_setting_lookup_float(setting, "opacity", &fval)) {
|
||||
@@ -514,6 +523,70 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||
parse_cfg_condlst(&cfg, &opt->shadow_clip_list, "clip-shadow-above");
|
||||
// --fade-exclude
|
||||
parse_cfg_condlst(&cfg, &opt->fade_blacklist, "fade-exclude");
|
||||
// --animations
|
||||
lcfg_lookup_bool(&cfg, "animations", &opt->animations);
|
||||
// --animation-for-open-window
|
||||
if (config_lookup_string(&cfg, "animation-for-open-window", &sval)) {
|
||||
enum open_window_animation animation = parse_open_window_animation(sval);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
|
||||
log_fatal("Invalid open-window animation %s", sval);
|
||||
goto err;
|
||||
}
|
||||
opt->animation_for_open_window = animation;
|
||||
}
|
||||
// --animation-for-transient-window
|
||||
if (config_lookup_string(&cfg, "animation-for-transient-window", &sval)) {
|
||||
enum open_window_animation animation = parse_open_window_animation(sval);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
|
||||
log_fatal("Invalid open-window animation %s", sval);
|
||||
goto err;
|
||||
}
|
||||
opt->animation_for_transient_window = animation;
|
||||
}
|
||||
// --animation-for-unmap-window
|
||||
if (config_lookup_string(&cfg, "animation-for-unmap-window", &sval)) {
|
||||
enum open_window_animation animation = parse_open_window_animation(sval);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
|
||||
log_fatal("Invalid unmap-window animation %s", sval);
|
||||
goto err;
|
||||
}
|
||||
opt->animation_for_unmap_window = animation;
|
||||
}
|
||||
// --animation-for-next-tag
|
||||
if (config_lookup_string(&cfg, "animation-for-next-tag", &sval)) {
|
||||
enum open_window_animation animation = parse_open_window_animation(sval);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
|
||||
log_fatal("Invalid next-tag animation %s", sval);
|
||||
goto err;
|
||||
}
|
||||
opt->animation_for_next_tag = animation;
|
||||
}
|
||||
// --animation-for-prev-tag
|
||||
if (config_lookup_string(&cfg, "animation-for-prev-tag", &sval)) {
|
||||
enum open_window_animation animation = parse_open_window_animation(sval);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
|
||||
log_fatal("Invalid prev-tag animation %s", sval);
|
||||
goto err;
|
||||
}
|
||||
opt->animation_for_prev_tag = animation;
|
||||
}
|
||||
// --animations-exclude
|
||||
parse_cfg_condlst(&cfg, &opt->animation_blacklist, "animation-exclude");
|
||||
|
||||
// --animation-stiffness
|
||||
config_lookup_float(&cfg, "animation-stiffness-in-tag", &opt->animation_stiffness);
|
||||
// --animation-stiffness-tag-change
|
||||
config_lookup_float(&cfg, "animation-stiffness-tag-change", &opt->animation_stiffness_tag_change);
|
||||
// --enable-fading-next-tag
|
||||
lcfg_lookup_bool(&cfg, "enable-fading-next-tag", &opt->enable_fading_next_tag);
|
||||
// --enable-fading-next-tag
|
||||
lcfg_lookup_bool(&cfg, "enable-fading-prev-tag", &opt->enable_fading_prev_tag);
|
||||
// --animation-window-mass
|
||||
config_lookup_float(&cfg, "animation-window-mass", &opt->animation_window_mass);
|
||||
// --animation-dampening
|
||||
config_lookup_float(&cfg, "animation-dampening", &opt->animation_dampening);
|
||||
// --animation-clamping
|
||||
lcfg_lookup_bool(&cfg, "animation-clamping", &opt->animation_clamping);
|
||||
// --focus-exclude
|
||||
parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude");
|
||||
// --invert-color-include
|
||||
|
||||
13
src/list.h
13
src/list.h
@@ -2,18 +2,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const __typeof__(((type *)0)->member) *__mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
#include "utils.h"
|
||||
|
||||
struct list_node {
|
||||
struct list_node *next, *prev;
|
||||
|
||||
@@ -184,6 +184,15 @@ static const struct picom_option picom_options[] = {
|
||||
"you want to attach a debugger to picom"},
|
||||
{"no-ewmh-fullscreen" , no_argument , 803, NULL , "Do not use EWMH to detect fullscreen windows. Reverts to checking if a "
|
||||
"window is fullscreen based only on its size and coordinates."},
|
||||
{"animations" ,no_argument , 804, NULL , "Enable/disable animations."},
|
||||
{"animation-stiffness-in-tag" , required_argument, 805, NULL , "Animation speed in current tag (float)."},
|
||||
{"animation-stiffness-tag-change", required_argument, 806, NULL , "Animation speed when tag changes (change to a new desktop)."},
|
||||
{"animation-dampening" , required_argument, 807, NULL , "Animation dampening ratio (spring dampening as an example)."},
|
||||
{"animation-window-mass" , required_argument, 808, NULL , "Animation window mass (lower mass makes animations bumpy)."},
|
||||
{"animation-clamping" , no_argument , 809, NULL , "Enable/disable animation clamping. Disabling increases performance"},
|
||||
{"animation-for-open-window" , required_argument, 810, NULL , "Set animation for opening window (Check sample.conf for options)."},
|
||||
{"animation-for-transient-window", required_argument, 811, NULL , "Set animation for transient (child) windows."},
|
||||
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -751,6 +760,52 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
break;
|
||||
P_CASEBOOL(802, debug_mode);
|
||||
P_CASEBOOL(803, no_ewmh_fullscreen);
|
||||
P_CASEBOOL(804, animations);
|
||||
case 805:
|
||||
// --animation-stiffness
|
||||
opt->animation_stiffness = atof(optarg);
|
||||
break;
|
||||
case 806:
|
||||
// --animation-stiffness-for-tags
|
||||
opt->animation_stiffness_tag_change = atof(optarg);
|
||||
break;
|
||||
case 807:
|
||||
// --animation-dampening
|
||||
opt->animation_dampening = atof(optarg);
|
||||
break;
|
||||
case 808:
|
||||
// --animation-window-masss
|
||||
opt->animation_window_mass = atof(optarg);
|
||||
break;
|
||||
case 809:
|
||||
// --animation-clamping
|
||||
opt->animation_clamping = true;
|
||||
break;
|
||||
case 810: {
|
||||
// --animation-for-open-window
|
||||
enum open_window_animation animation = parse_open_window_animation(optarg);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
|
||||
log_warn("Invalid open-window animation %s, ignoring.", optarg);
|
||||
} else {
|
||||
opt->animation_for_open_window = animation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 811: {
|
||||
// --animation-for-transient-window
|
||||
enum open_window_animation animation = parse_open_window_animation(optarg);
|
||||
if (animation >= OPEN_WINDOW_ANIMATION_INVALID) {
|
||||
log_warn("Invalid transient-window animation %s, ignoring.", optarg);
|
||||
} else {
|
||||
opt->animation_for_transient_window = animation;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 812: {
|
||||
// --animation-exclude
|
||||
condlst_add(&opt->animation_blacklist, optarg);
|
||||
break;
|
||||
}
|
||||
default: usage(argv[0], 1); break;
|
||||
#undef P_CASEBOOL
|
||||
}
|
||||
|
||||
306
src/picom.c
306
src/picom.c
@@ -888,85 +888,260 @@ static void handle_root_flags(session_t *ps) {
|
||||
}
|
||||
}
|
||||
|
||||
static struct managed_win *
|
||||
paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
|
||||
/**
|
||||
* Go through the window stack and calculate some parameters for rendering.
|
||||
*
|
||||
* @return whether the operation succeeded
|
||||
*/
|
||||
static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
|
||||
struct managed_win **out_bottom) {
|
||||
// XXX need better, more general name for `fade_running`. It really
|
||||
// means if fade is still ongoing after the current frame is rendered
|
||||
// means if fade is still ongoing after the current frame is rendered.
|
||||
// Same goes for `animation_running`.
|
||||
struct managed_win *bottom = NULL;
|
||||
*fade_running = false;
|
||||
*animation = false;
|
||||
*out_bottom = NULL;
|
||||
*animation_running = false;
|
||||
auto now = get_time_ms();
|
||||
|
||||
// Fading step calculation
|
||||
long long steps = 0L;
|
||||
auto now = get_time_ms();
|
||||
if (ps->fade_time) {
|
||||
assert(now >= ps->fade_time);
|
||||
steps = (now - ps->fade_time) / ps->o.fade_delta;
|
||||
} else {
|
||||
// Reset fade_time if unset
|
||||
ps->fade_time = get_time_ms();
|
||||
ps->fade_time = now;
|
||||
steps = 0L;
|
||||
}
|
||||
ps->fade_time += steps * ps->o.fade_delta;
|
||||
|
||||
// First, let's process fading, and animated shaders
|
||||
// TODO(yshui) check if a window is fully obscured, and if we don't need to
|
||||
// process fading or animation for it.
|
||||
if (ps->o.animations && !ps->animation_time)
|
||||
ps->animation_time = now;
|
||||
|
||||
double delta_secs = (double)(now - ps->animation_time) / 1000;
|
||||
|
||||
// First, let's process fading
|
||||
win_stack_foreach_managed_safe(w, &ps->window_stack) {
|
||||
const winmode_t mode_old = w->mode;
|
||||
const bool was_painted = w->to_paint;
|
||||
const double opacity_old = w->opacity;
|
||||
|
||||
// IMPORTANT: These window animation steps must happen before any other
|
||||
// [pre]processing. This is because it changes the window's geometry.
|
||||
if (ps->o.animations &&
|
||||
!isnan(w->animation_progress) && w->animation_progress != 1.0 &&
|
||||
ps->o.wintype_option[w->window_type].animation != 0 &&
|
||||
win_is_mapped_in_x(w))
|
||||
{
|
||||
double neg_displacement_x =
|
||||
w->animation_dest_center_x - w->animation_center_x;
|
||||
double neg_displacement_y =
|
||||
w->animation_dest_center_y - w->animation_center_y;
|
||||
double neg_displacement_w = w->animation_dest_w - w->animation_w;
|
||||
double neg_displacement_h = w->animation_dest_h - w->animation_h;
|
||||
double animation_stiffness = ps->o.animation_stiffness;
|
||||
if (!(w->animation_is_tag & ANIM_IN_TAG)) {
|
||||
if (w->animation_is_tag & ANIM_SLOW)
|
||||
animation_stiffness = ps->o.animation_stiffness_tag_change;
|
||||
else if (w->animation_is_tag & ANIM_FAST)
|
||||
animation_stiffness = ps->o.animation_stiffness_tag_change * 1.5;
|
||||
}
|
||||
if (w->state == WSTATE_FADING && !(w->animation_is_tag & ANIM_FADE))
|
||||
w->opacity_target = win_calc_opacity_target(ps, w);
|
||||
double acceleration_x =
|
||||
(animation_stiffness * neg_displacement_x -
|
||||
ps->o.animation_dampening * w->animation_velocity_x) /
|
||||
ps->o.animation_window_mass;
|
||||
double acceleration_y =
|
||||
(animation_stiffness * neg_displacement_y -
|
||||
ps->o.animation_dampening * w->animation_velocity_y) /
|
||||
ps->o.animation_window_mass;
|
||||
double acceleration_w =
|
||||
(animation_stiffness * neg_displacement_w -
|
||||
ps->o.animation_dampening * w->animation_velocity_w) /
|
||||
ps->o.animation_window_mass;
|
||||
double acceleration_h =
|
||||
(animation_stiffness * neg_displacement_h -
|
||||
ps->o.animation_dampening * w->animation_velocity_h) /
|
||||
ps->o.animation_window_mass;
|
||||
w->animation_velocity_x += acceleration_x * delta_secs;
|
||||
w->animation_velocity_y += acceleration_y * delta_secs;
|
||||
w->animation_velocity_w += acceleration_w * delta_secs;
|
||||
w->animation_velocity_h += acceleration_h * delta_secs;
|
||||
|
||||
// Animate window geometry
|
||||
double new_animation_x =
|
||||
w->animation_center_x + w->animation_velocity_x * delta_secs;
|
||||
double new_animation_y =
|
||||
w->animation_center_y + w->animation_velocity_y * delta_secs;
|
||||
double new_animation_w =
|
||||
w->animation_w + w->animation_velocity_w * delta_secs;
|
||||
double new_animation_h =
|
||||
w->animation_h + w->animation_velocity_h * delta_secs;
|
||||
|
||||
// Negative new width/height causes segfault and it can happen
|
||||
// when clamping disabled and shading a window
|
||||
if (new_animation_h < 0)
|
||||
new_animation_h = 0;
|
||||
|
||||
if (new_animation_w < 0)
|
||||
new_animation_w = 0;
|
||||
|
||||
if (ps->o.animation_clamping) {
|
||||
w->animation_center_x = clamp(
|
||||
new_animation_x,
|
||||
min2(w->animation_center_x, w->animation_dest_center_x),
|
||||
max2(w->animation_center_x, w->animation_dest_center_x));
|
||||
w->animation_center_y = clamp(
|
||||
new_animation_y,
|
||||
min2(w->animation_center_y, w->animation_dest_center_y),
|
||||
max2(w->animation_center_y, w->animation_dest_center_y));
|
||||
w->animation_w =
|
||||
clamp(new_animation_w,
|
||||
min2(w->animation_w, w->animation_dest_w),
|
||||
max2(w->animation_w, w->animation_dest_w));
|
||||
w->animation_h =
|
||||
clamp(new_animation_h,
|
||||
min2(w->animation_h, w->animation_dest_h),
|
||||
max2(w->animation_h, w->animation_dest_h));
|
||||
} else {
|
||||
w->animation_center_x = new_animation_x;
|
||||
w->animation_center_y = new_animation_y;
|
||||
w->animation_w = new_animation_w;
|
||||
w->animation_h = new_animation_h;
|
||||
}
|
||||
|
||||
// Now we are done doing the math; we just need to submit our
|
||||
// changes (if there are any).
|
||||
|
||||
struct win_geometry old_g = w->g;
|
||||
double old_animation_progress = w->animation_progress;
|
||||
new_animation_x = round(w->animation_center_x - w->animation_w * 0.5);
|
||||
new_animation_y = round(w->animation_center_y - w->animation_h * 0.5);
|
||||
new_animation_w = round(w->animation_w);
|
||||
new_animation_h = round(w->animation_h);
|
||||
|
||||
bool position_changed =
|
||||
new_animation_x != old_g.x || new_animation_y != old_g.y;
|
||||
bool size_changed =
|
||||
new_animation_w != old_g.width || new_animation_h != old_g.height;
|
||||
bool geometry_changed = position_changed || size_changed;
|
||||
|
||||
// Mark past window region with damage
|
||||
if (was_painted && geometry_changed)
|
||||
add_damage_from_win(ps, w);
|
||||
|
||||
double x_dist = w->animation_dest_center_x - w->animation_center_x;
|
||||
double y_dist = w->animation_dest_center_y - w->animation_center_y;
|
||||
double w_dist = w->animation_dest_w - w->animation_w;
|
||||
double h_dist = w->animation_dest_h - w->animation_h;
|
||||
w->animation_progress =
|
||||
1.0 - w->animation_inv_og_distance *
|
||||
sqrt(x_dist * x_dist + y_dist * y_dist +
|
||||
w_dist * w_dist + h_dist * h_dist);
|
||||
|
||||
// When clamping disabled we don't want the overlayed image to
|
||||
// fade in again because process is moving to negative value
|
||||
if (w->animation_progress < old_animation_progress)
|
||||
w->animation_progress = old_animation_progress;
|
||||
|
||||
w->g.x = (int16_t)new_animation_x;
|
||||
w->g.y = (int16_t)new_animation_y;
|
||||
w->g.width = (uint16_t)new_animation_w;
|
||||
w->g.height = (uint16_t)new_animation_h;
|
||||
|
||||
|
||||
if (w->animation_is_tag > ANIM_IN_TAG && (((w->animation_is_tag & ANIM_FADE) && w->opacity_target == w->opacity) || ((w->g.width == 0 || w->g.height == 0) && (w->animation_dest_w == 0 || w->animation_dest_h == 0)))) {
|
||||
w->g.x = w->pending_g.x;
|
||||
w->g.y = w->pending_g.y;
|
||||
if (ps->o.animation_for_next_tag < OPEN_WINDOW_ANIMATION_ZOOM) {
|
||||
w->g.width = w->pending_g.width;
|
||||
w->g.height = w->pending_g.height;
|
||||
} else {
|
||||
w->g.width = 0;
|
||||
w->g.height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Submit window size change
|
||||
if (size_changed) {
|
||||
win_on_win_size_change(ps, w);
|
||||
|
||||
pixman_region32_clear(&w->bounding_shape);
|
||||
pixman_region32_fini(&w->bounding_shape);
|
||||
pixman_region32_init_rect(&w->bounding_shape, 0, 0,
|
||||
(uint)w->widthb, (uint)w->heightb);
|
||||
|
||||
if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING && w->state != WSTATE_UNMAPPING) {
|
||||
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
||||
win_process_image_flags(ps, w);
|
||||
}
|
||||
}
|
||||
// Mark new window region with damage
|
||||
if (was_painted && geometry_changed) {
|
||||
add_damage_from_win(ps, w);
|
||||
w->reg_ignore_valid = false;
|
||||
}
|
||||
|
||||
// We can't check for 1 here as sometimes 1 = 0.999999999999999
|
||||
// in case of floating numbers
|
||||
if (w->animation_progress >= 0.999999999) {
|
||||
w->animation_progress = 1;
|
||||
w->animation_velocity_x = 0.0;
|
||||
w->animation_velocity_y = 0.0;
|
||||
w->animation_velocity_w = 0.0;
|
||||
w->animation_velocity_h = 0.0;
|
||||
w->opacity = win_calc_opacity_target(ps, w);
|
||||
}
|
||||
*animation_running = true;
|
||||
}
|
||||
|
||||
if (win_should_dim(ps, w) != w->dim) {
|
||||
w->dim = win_should_dim(ps, w);
|
||||
add_damage_from_win(ps, w);
|
||||
}
|
||||
|
||||
if (w->fg_shader && (w->fg_shader->attributes & SHADER_ATTRIBUTE_ANIMATED)) {
|
||||
add_damage_from_win(ps, w);
|
||||
*animation = true;
|
||||
}
|
||||
|
||||
// Run fading
|
||||
if (run_fade(ps, &w, steps)) {
|
||||
*fade_running = true;
|
||||
}
|
||||
if (w->opacity != w->opacity_target) {
|
||||
// Run fading
|
||||
if (run_fade(ps, &w, steps)) {
|
||||
*fade_running = true;
|
||||
}
|
||||
|
||||
// Add window to damaged area if its opacity changes
|
||||
// If was_painted == false, and to_paint is also false, we don't care
|
||||
// If was_painted == false, but to_paint is true, damage will be added in
|
||||
// the loop below
|
||||
if (was_painted && w->opacity != opacity_old) {
|
||||
add_damage_from_win(ps, w);
|
||||
}
|
||||
// Add window to damaged area if its opacity changes
|
||||
// If was_painted == false, and to_paint is also false, we don't care
|
||||
// If was_painted == false, but to_paint is true, damage will be added in
|
||||
// the loop below
|
||||
if (was_painted && w->opacity != opacity_old) {
|
||||
add_damage_from_win(ps, w);
|
||||
}
|
||||
|
||||
if (win_check_fade_finished(ps, w)) {
|
||||
// the window has been destroyed because fading finished
|
||||
continue;
|
||||
}
|
||||
|
||||
if (win_has_frame(w)) {
|
||||
w->frame_opacity = ps->o.frame_opacity;
|
||||
} else {
|
||||
w->frame_opacity = 1.0;
|
||||
}
|
||||
if (win_check_fade_finished(ps, w)) {
|
||||
// the window has been destroyed because fading finished
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update window mode
|
||||
w->mode = win_calc_mode(w);
|
||||
if (win_has_frame(w)) {
|
||||
w->frame_opacity = ps->o.frame_opacity;
|
||||
} else {
|
||||
w->frame_opacity = 1.0;
|
||||
}
|
||||
|
||||
// Destroy all reg_ignore above when frame opaque state changes on
|
||||
// SOLID mode
|
||||
if (was_painted && w->mode != mode_old) {
|
||||
w->reg_ignore_valid = false;
|
||||
// Update window mode
|
||||
w->mode = win_calc_mode(w);
|
||||
|
||||
// Destroy all reg_ignore above when frame opaque state changes on
|
||||
// SOLID mode
|
||||
if (was_painted && w->mode != mode_old) {
|
||||
w->reg_ignore_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animation_running)
|
||||
ps->animation_time = now;
|
||||
|
||||
// Opacity will not change, from now on.
|
||||
rc_region_t *last_reg_ignore = rc_region_new();
|
||||
|
||||
@@ -1151,8 +1326,7 @@ static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
|
||||
}
|
||||
}
|
||||
|
||||
*out_bottom = bottom;
|
||||
return true;
|
||||
return bottom;
|
||||
}
|
||||
|
||||
void root_damaged(session_t *ps) {
|
||||
@@ -1346,7 +1520,7 @@ static int register_cm(session_t *ps) {
|
||||
log_fatal("Failed to allocate memory");
|
||||
return -1;
|
||||
}
|
||||
atom = get_atom(ps->atoms, buf);
|
||||
atom = get_atom(ps->atoms, buf, ps->c.c);
|
||||
free(buf);
|
||||
|
||||
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(
|
||||
@@ -1708,6 +1882,11 @@ static void fade_timer_callback(EV_P attr_unused, ev_timer *w, int revents attr_
|
||||
queue_redraw(ps);
|
||||
}
|
||||
|
||||
static void animation_timer_callback(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
|
||||
session_t *ps = session_ptr(w, animation_timer);
|
||||
queue_redraw(ps);
|
||||
}
|
||||
|
||||
static void handle_pending_updates(EV_P_ struct session *ps) {
|
||||
if (ps->pending_updates) {
|
||||
log_debug("Delayed handling of events, entering critical section");
|
||||
@@ -1813,14 +1992,9 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
||||
* screen is not redirected. its sole purpose should be to decide whether the
|
||||
* screen should be redirected. */
|
||||
bool fade_running = false;
|
||||
bool animation = false;
|
||||
bool animation_running = false;
|
||||
bool was_redirected = ps->redirected;
|
||||
struct managed_win *bottom = NULL;
|
||||
if (!paint_preprocess(ps, &fade_running, &animation, &bottom)) {
|
||||
log_fatal("Pre-render preparation has failed, exiting...");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto bottom = paint_preprocess(ps, &fade_running, &animation_running);
|
||||
ps->tmout_unredir_hit = false;
|
||||
|
||||
if (!was_redirected && ps->redirected) {
|
||||
@@ -1842,6 +2016,13 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
||||
ev_timer_set(&ps->fade_timer, fade_timeout(ps), 0);
|
||||
ev_timer_start(EV_A_ & ps->fade_timer);
|
||||
}
|
||||
// Start/stop animation timer depends on whether windows are animating
|
||||
if (!animation_running && ev_is_active(&ps->animation_timer)) {
|
||||
ev_timer_stop(EV_A_ & ps->animation_timer);
|
||||
} else if (animation_running && !ev_is_active(&ps->animation_timer)) {
|
||||
ev_timer_set(&ps->animation_timer, 0, 0);
|
||||
ev_timer_start(EV_A_ & ps->animation_timer);
|
||||
}
|
||||
|
||||
int64_t after_preprocess_us;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
@@ -1882,23 +2063,15 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
||||
if (!fade_running) {
|
||||
ps->fade_time = 0L;
|
||||
}
|
||||
if (!animation_running) {
|
||||
ps->animation_time = 0L;
|
||||
}
|
||||
|
||||
ps->render_queued = false;
|
||||
|
||||
// TODO(yshui) Investigate how big the X critical section needs to be. There are
|
||||
// suggestions that rendering should be in the critical section as well.
|
||||
|
||||
// Queue redraw if animation is running. This should be picked up by next present
|
||||
// event.
|
||||
if (animation) {
|
||||
queue_redraw(ps);
|
||||
}
|
||||
if (ps->vblank_scheduler) {
|
||||
// Even if we might not want to render during next vblank, we want to keep
|
||||
// `backend_busy` up to date, so when the next render comes, we can
|
||||
// immediately know if we can render.
|
||||
vblank_scheduler_schedule(ps->vblank_scheduler, check_render_finish, ps);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_callback(EV_P_ ev_timer *w, int revents) {
|
||||
@@ -2029,6 +2202,9 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||
.redirected = false,
|
||||
.alpha_picts = NULL,
|
||||
.fade_time = 0L,
|
||||
.animation_time = 0L,
|
||||
.pending_reply_head = NULL,
|
||||
.pending_reply_tail = NULL,
|
||||
.quit = false,
|
||||
|
||||
.expose_rects = NULL,
|
||||
@@ -2277,8 +2453,9 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||
c2_list_postprocess(ps, ps->o.window_shader_fg_rules) &&
|
||||
c2_list_postprocess(ps, ps->o.opacity_rules) &&
|
||||
c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
|
||||
c2_list_postprocess(ps, ps->o.focus_blacklist) &&
|
||||
c2_list_postprocess(ps, ps->o.corner_radius_rules) &&
|
||||
c2_list_postprocess(ps, ps->o.focus_blacklist))) {
|
||||
c2_list_postprocess(ps, ps->o.animation_blacklist))) {
|
||||
log_error("Post-processing of conditionals failed, some of your rules "
|
||||
"might not work");
|
||||
}
|
||||
@@ -2474,7 +2651,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||
|
||||
// Monitor screen changes if vsync_sw is enabled and we are using
|
||||
// an auto-detected refresh rate, or when X RandR features are enabled
|
||||
if (ps->randr_exists && ps->o.crop_shadow_to_monitor) {
|
||||
if (ps->randr_exists) {
|
||||
xcb_randr_select_input(ps->c.c, ps->c.screen_info->root,
|
||||
XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
|
||||
x_update_monitors(&ps->c, &ps->monitors);
|
||||
@@ -2503,6 +2680,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||
ev_init(&ps->draw_timer, draw_callback);
|
||||
|
||||
ev_init(&ps->fade_timer, fade_timer_callback);
|
||||
ev_init(&ps->animation_timer, animation_timer_callback);
|
||||
|
||||
|
||||
// Set up SIGUSR1 signal handler to reset program
|
||||
ev_signal_init(&ps->usr1_signal, reset_enable, SIGUSR1);
|
||||
@@ -2808,6 +2987,7 @@ static void session_destroy(session_t *ps) {
|
||||
// Stop libev event handlers
|
||||
ev_timer_stop(ps->loop, &ps->unredir_timer);
|
||||
ev_timer_stop(ps->loop, &ps->fade_timer);
|
||||
ev_timer_stop(ps->loop, &ps->animation_timer);
|
||||
ev_timer_stop(ps->loop, &ps->draw_timer);
|
||||
ev_prepare_stop(ps->loop, &ps->event_check);
|
||||
ev_signal_stop(ps->loop, &ps->usr1_signal);
|
||||
|
||||
41
src/utils.h
41
src/utils.h
@@ -21,6 +21,7 @@
|
||||
#include "types.h"
|
||||
|
||||
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||
#define CLEAR_MASK(x) x = 0;
|
||||
|
||||
#ifdef __FAST_MATH__
|
||||
#warning Use of -ffast-math can cause rendering error or artifacts, \
|
||||
@@ -58,11 +59,11 @@ safe_isnan(double a) {
|
||||
/// being always true or false.
|
||||
#define ASSERT_IN_RANGE(var, lower, upper) \
|
||||
do { \
|
||||
auto __tmp attr_unused = (var); \
|
||||
auto __assert_in_range_tmp attr_unused = (var); \
|
||||
_Pragma("GCC diagnostic push"); \
|
||||
_Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
|
||||
assert(__tmp >= lower); \
|
||||
assert(__tmp <= upper); \
|
||||
assert(__assert_in_range_tmp >= lower); \
|
||||
assert(__assert_in_range_tmp <= upper); \
|
||||
_Pragma("GCC diagnostic pop"); \
|
||||
} while (0)
|
||||
|
||||
@@ -112,11 +113,26 @@ safe_isnan(double a) {
|
||||
#define to_u32_checked(val) \
|
||||
({ \
|
||||
auto __to_tmp = (val); \
|
||||
int64_t max attr_unused = UINT32_MAX; /* silence clang tautological \
|
||||
comparison warning*/ \
|
||||
ASSERT_IN_RANGE(__to_tmp, 0, max); \
|
||||
int64_t __to_u32_max attr_unused = UINT32_MAX; /* silence clang \
|
||||
tautological \
|
||||
comparison warning */ \
|
||||
ASSERT_IN_RANGE(__to_tmp, 0, __to_u32_max); \
|
||||
(uint32_t) __to_tmp; \
|
||||
})
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
const __typeof__(((type *)0)->member) *__mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); \
|
||||
})
|
||||
|
||||
/**
|
||||
* Normalize an int value to a specific range.
|
||||
*
|
||||
@@ -135,6 +151,19 @@ static inline int attr_const attr_unused normalize_i_range(int i, int min, int m
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linearly interpolate from a range into another.
|
||||
*
|
||||
* @param a,b first range
|
||||
* @param c,d second range
|
||||
* @param value value to interpolate, should be in range [a,b]
|
||||
* @return interpolated value in range [c,d]
|
||||
*/
|
||||
static inline int attr_const lerp_range(int a, int b, int c, int d, int value) {
|
||||
ASSERT_IN_RANGE(value, a, b);
|
||||
return (d-c)*(value-a)/(b-a) + c;
|
||||
}
|
||||
|
||||
/// Generic integer abs()
|
||||
#define iabs(val) \
|
||||
({ \
|
||||
|
||||
364
src/win.c
364
src/win.c
@@ -307,6 +307,13 @@ static inline void win_release_pixmap(backend_t *base, struct managed_win *w) {
|
||||
w->flags |= WIN_FLAGS_PIXMAP_NONE;
|
||||
}
|
||||
}
|
||||
static inline void win_release_oldpixmap(backend_t *base, struct managed_win *w) {
|
||||
log_debug("Releasing old_pixmap of window %#010x (%s)", w->base.id, w->name);
|
||||
if (w->old_win_image) {
|
||||
base->ops->release_image(base, w->old_win_image);
|
||||
w->old_win_image = NULL;
|
||||
}
|
||||
}
|
||||
static inline void win_release_shadow(backend_t *base, struct managed_win *w) {
|
||||
log_debug("Releasing shadow of window %#010x (%s)", w->base.id, w->name);
|
||||
assert(w->shadow_image);
|
||||
@@ -403,6 +410,7 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) {
|
||||
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
||||
assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
|
||||
win_release_pixmap(backend, w);
|
||||
win_release_oldpixmap(backend, w);
|
||||
}
|
||||
|
||||
if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) {
|
||||
@@ -476,6 +484,173 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
|
||||
win_clear_all_properties_stale(w);
|
||||
}
|
||||
|
||||
static void init_animation(session_t *ps, struct managed_win *w) {
|
||||
CLEAR_MASK(w->animation_is_tag)
|
||||
static int32_t randr_mon_center_x, randr_mon_center_y;
|
||||
if (w->randr_monitor != -1) {
|
||||
auto e = pixman_region32_extents(&ps->monitors.regions[w->randr_monitor]);
|
||||
randr_mon_center_x = (e->x2 + e->x1) / 2, randr_mon_center_y = (e->y2 + e->y1) / 2;
|
||||
} else {
|
||||
randr_mon_center_x = ps->root_width / 2, randr_mon_center_y = ps->root_height / 2;
|
||||
}
|
||||
static double *anim_x, *anim_y, *anim_w, *anim_h;
|
||||
enum open_window_animation animation;
|
||||
|
||||
|
||||
animation = ps->o.animation_for_open_window;
|
||||
|
||||
if (w->window_type != WINTYPE_TOOLTIP &&
|
||||
wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR)) {
|
||||
animation = ps->o.animation_for_transient_window;
|
||||
}
|
||||
|
||||
if (ps->o.wintype_option[w->window_type].animation != OPEN_WINDOW_ANIMATION_INVALID
|
||||
&& !w->dwm_mask) {
|
||||
animation = ps->o.wintype_option[w->window_type].animation;
|
||||
}
|
||||
|
||||
anim_x = &w->animation_center_x, anim_y = &w->animation_center_y;
|
||||
anim_w = &w->animation_w, anim_h = &w->animation_h;
|
||||
|
||||
if (w->dwm_mask & ANIM_PREV_TAG) {
|
||||
animation = ps->o.animation_for_prev_tag;
|
||||
|
||||
if (ps->o.enable_fading_prev_tag) {
|
||||
w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old);
|
||||
w->state = WSTATE_FADING;
|
||||
w->animation_is_tag |= ANIM_FADE;
|
||||
}
|
||||
if (ps->o.animation_for_prev_tag >= OPEN_WINDOW_ANIMATION_ZOOM) {
|
||||
w->animation_is_tag |= ANIM_FAST;
|
||||
w->dwm_mask |= ANIM_SPECIAL_MINIMIZE;
|
||||
goto revert;
|
||||
}
|
||||
w->animation_is_tag |= ANIM_SLOW;
|
||||
} else if (w->dwm_mask & ANIM_NEXT_TAG) {
|
||||
animation = ps->o.animation_for_next_tag;
|
||||
w->animation_is_tag |= animation >= OPEN_WINDOW_ANIMATION_ZOOM ? ANIM_FAST : ANIM_SLOW;
|
||||
if (ps->o.enable_fading_next_tag) {
|
||||
w->opacity = 0.0;
|
||||
w->state = WSTATE_FADING;
|
||||
}
|
||||
} else if (w->dwm_mask & ANIM_UNMAP) {
|
||||
animation = ps->o.animation_for_unmap_window;
|
||||
revert:
|
||||
anim_x = &w->animation_dest_center_x, anim_y = &w->animation_dest_center_y;
|
||||
anim_w = &w->animation_dest_w, anim_h = &w->animation_dest_h;
|
||||
}
|
||||
|
||||
double angle;
|
||||
switch (animation) {
|
||||
case OPEN_WINDOW_ANIMATION_NONE: // No animation
|
||||
w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
w->animation_center_y = w->pending_g.y + w->pending_g.height * 0.5;
|
||||
w->animation_w = w->pending_g.width;
|
||||
w->animation_h = w->pending_g.height;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_FLYIN: // Fly-in from a random point outside the screen
|
||||
// Compute random point off screen
|
||||
angle = 2 * M_PI * ((double)rand() / RAND_MAX);
|
||||
const double radius =
|
||||
sqrt(ps->root_width * ps->root_width + ps->root_height * ps->root_height);
|
||||
|
||||
// Set animation
|
||||
*anim_x = randr_mon_center_x + radius * cos(angle);
|
||||
*anim_y = randr_mon_center_y + radius * sin(angle);
|
||||
*anim_w = 0;
|
||||
*anim_h = 0;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_UP: // Slide up the image, without changing its location
|
||||
*anim_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
*anim_y = w->pending_g.y + w->pending_g.height;
|
||||
*anim_w = w->pending_g.width;
|
||||
*anim_h = 0;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_DOWN: // Slide down the image, without changing its location
|
||||
*anim_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
*anim_y = w->pending_g.y;
|
||||
*anim_w = w->pending_g.width;
|
||||
*anim_h = 0;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_LEFT: // Slide left the image, without changing its location
|
||||
*anim_x = w->pending_g.x + w->pending_g.width;
|
||||
*anim_y = w->pending_g.y + w->pending_g.height * 0.5;
|
||||
*anim_w = 0;
|
||||
*anim_h = w->pending_g.height;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_RIGHT: // Slide right the image, without changing its location
|
||||
*anim_x = w->pending_g.x;
|
||||
*anim_y = w->pending_g.y + w->pending_g.height * 0.5;
|
||||
*anim_w = 0;
|
||||
*anim_h = w->pending_g.height;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_IN:
|
||||
*anim_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
*anim_y = w->pending_g.y + w->pending_g.height * 0.5;
|
||||
*anim_w = w->pending_g.width;
|
||||
*anim_h = w->pending_g.height;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_IN_CENTER:
|
||||
*anim_x = randr_mon_center_x;
|
||||
*anim_y = w->g.y + w->pending_g.height * 0.5;
|
||||
*anim_w = w->pending_g.width;
|
||||
*anim_h = w->pending_g.height;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_OUT:
|
||||
w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
w->animation_dest_center_y = w->pending_g.y;
|
||||
w->animation_dest_w = w->pending_g.width;
|
||||
w->animation_dest_h = w->pending_g.height;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SLIDE_OUT_CENTER:
|
||||
w->animation_dest_center_x = randr_mon_center_x;
|
||||
w->animation_dest_center_y = w->pending_g.y;
|
||||
w->animation_dest_w = w->pending_g.width;
|
||||
w->animation_dest_h = w->pending_g.height;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_ZOOM: // Zoom-in the image, without changing its location
|
||||
if (w->dwm_mask & ANIM_SPECIAL_MINIMIZE) {
|
||||
*anim_x = w->g.x + w->g.width * 0.5;
|
||||
*anim_y = w->g.y + w->g.height * 0.5;
|
||||
} else {
|
||||
*anim_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
*anim_y = w->pending_g.y + w->pending_g.height * 0.5;
|
||||
}
|
||||
*anim_w = 0;
|
||||
*anim_h = 0;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_MINIMIZE:
|
||||
*anim_x = randr_mon_center_x;
|
||||
*anim_y = randr_mon_center_y;
|
||||
*anim_w = 0;
|
||||
*anim_h = 0;
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SQUEEZE:
|
||||
if (w->dwm_mask & ANIM_PREV_TAG) {
|
||||
*anim_h = 0;
|
||||
} else {
|
||||
*anim_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
*anim_y = w->pending_g.y + w->pending_g.height * 0.5;
|
||||
*anim_w = w->pending_g.width;
|
||||
*anim_h = 0;
|
||||
}
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_SQUEEZE_BOTTOM:
|
||||
if (w->dwm_mask & ANIM_PREV_TAG) {
|
||||
*anim_y = w->g.y + w->g.height;
|
||||
*anim_h = 0;
|
||||
} else {
|
||||
w->animation_center_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
w->animation_center_y = w->pending_g.y + w->pending_g.height;
|
||||
w->animation_w = w->pending_g.width;
|
||||
*anim_h = 0;
|
||||
*anim_y = w->pending_g.y + w->pending_g.height;
|
||||
}
|
||||
break;
|
||||
case OPEN_WINDOW_ANIMATION_INVALID: assert(false); break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle non-image flags. This phase might set IMAGES_STALE flags
|
||||
void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
// Whether the window was visible before we process the mapped flag. i.e.
|
||||
@@ -517,8 +692,79 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
add_damage_from_win(ps, w);
|
||||
}
|
||||
|
||||
// Update window geometry
|
||||
w->g = w->pending_g;
|
||||
|
||||
|
||||
// Determine if a window should animate
|
||||
if (win_should_animate(ps, w)) {
|
||||
if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height)
|
||||
w->dwm_mask = ANIM_PREV_TAG;
|
||||
else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height)
|
||||
w->dwm_mask = ANIM_NEXT_TAG;
|
||||
|
||||
if (!was_visible || w->dwm_mask) {
|
||||
|
||||
// Set window-open animation
|
||||
init_animation(ps, w);
|
||||
if (!(w->dwm_mask & ANIM_SPECIAL_MINIMIZE)) {
|
||||
w->animation_dest_center_x = w->pending_g.x + w->pending_g.width * 0.5;
|
||||
w->animation_dest_center_y = w->pending_g.y + w->pending_g.height * 0.5;
|
||||
w->animation_dest_w = w->pending_g.width;
|
||||
w->animation_dest_h = w->pending_g.height;
|
||||
w->g.x = (int16_t)round(w->animation_center_x -
|
||||
w->animation_w * 0.5);
|
||||
w->g.y = (int16_t)round(w->animation_center_y -
|
||||
w->animation_h * 0.5);
|
||||
w->g.width = (uint16_t)round(w->animation_w);
|
||||
w->g.height = (uint16_t)round(w->animation_h);
|
||||
}
|
||||
|
||||
} else {
|
||||
w->animation_is_tag = ANIM_IN_TAG;
|
||||
w->animation_dest_center_x =
|
||||
w->pending_g.x + w->pending_g.width * 0.5;
|
||||
w->animation_dest_center_y =
|
||||
w->pending_g.y + w->pending_g.height * 0.5;
|
||||
w->animation_dest_w = w->pending_g.width;
|
||||
w->animation_dest_h = w->pending_g.height;
|
||||
}
|
||||
|
||||
CLEAR_MASK(w->dwm_mask)
|
||||
w->g.border_width = w->pending_g.border_width;
|
||||
double x_dist = w->animation_dest_center_x - w->animation_center_x;
|
||||
double y_dist = w->animation_dest_center_y - w->animation_center_y;
|
||||
double w_dist = w->animation_dest_w - w->animation_w;
|
||||
double h_dist = w->animation_dest_h - w->animation_h;
|
||||
w->animation_inv_og_distance =
|
||||
1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
|
||||
w_dist * w_dist + h_dist * h_dist);
|
||||
|
||||
if (isinf(w->animation_inv_og_distance))
|
||||
w->animation_inv_og_distance = 0;
|
||||
|
||||
// We only grab images if w->reg_ignore_valid is true as
|
||||
// there's an ev_shape_notify() event fired quickly on new windows
|
||||
// for e.g. in case of Firefox main menu and ev_shape_notify()
|
||||
// sets the win_set_flags(w, WIN_FLAGS_SIZE_STALE); which
|
||||
// brakes the new image captured and because this same event
|
||||
// also sets w->reg_ignore_valid = false; too we check for it
|
||||
if (w->reg_ignore_valid) {
|
||||
if (w->old_win_image) {
|
||||
ps->backend_data->ops->release_image(ps->backend_data,
|
||||
w->old_win_image);
|
||||
w->old_win_image = NULL;
|
||||
}
|
||||
|
||||
// We only grab
|
||||
if (w->win_image) {
|
||||
w->old_win_image = ps->backend_data->ops->clone_image(
|
||||
ps->backend_data, w->win_image, &w->bounding_shape);
|
||||
}
|
||||
}
|
||||
|
||||
w->animation_progress = 0.0;
|
||||
} else {
|
||||
w->g = w->pending_g;
|
||||
}
|
||||
|
||||
// Whether a window is fullscreen changes based on its geometry
|
||||
win_update_is_fullscreen(ps, w);
|
||||
@@ -658,12 +904,14 @@ int win_update_name(session_t *ps, struct managed_win *w) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(wid_get_text_prop(ps, w->client_win, ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
|
||||
if (!(wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
|
||||
ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
|
||||
log_debug("(%#010x): _NET_WM_NAME unset, falling back to "
|
||||
"WM_NAME.",
|
||||
w->client_win);
|
||||
|
||||
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_NAME, &strlst, &nstr)) {
|
||||
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
|
||||
ps->atoms->aWM_NAME, &strlst, &nstr)) {
|
||||
log_debug("Unsetting window name for %#010x", w->client_win);
|
||||
free(w->name);
|
||||
w->name = NULL;
|
||||
@@ -690,7 +938,8 @@ static int win_update_role(session_t *ps, struct managed_win *w) {
|
||||
char **strlst = NULL;
|
||||
int nstr = 0;
|
||||
|
||||
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
|
||||
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
|
||||
ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -833,6 +1082,10 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
|
||||
if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
|
||||
return 0;
|
||||
}
|
||||
if ((w->state == WSTATE_FADING && (w->animation_is_tag & ANIM_FADE))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try obeying opacity property and window type opacity firstly
|
||||
if (w->has_opacity_prop) {
|
||||
opacity = ((double)w->opacity_prop) / OPAQUE;
|
||||
@@ -897,6 +1150,24 @@ bool win_should_fade(session_t *ps, const struct managed_win *w) {
|
||||
return ps->o.wintype_option[w->window_type].fade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a window should animate.
|
||||
*/
|
||||
bool win_should_animate(session_t *ps, const struct managed_win *w) {
|
||||
if (!ps->o.animations) {
|
||||
return false;
|
||||
}
|
||||
if (ps->o.wintype_option[w->window_type].animation == 0) {
|
||||
log_debug("Animation disabled by window_type");
|
||||
return false;
|
||||
}
|
||||
if (c2_match(ps, w, ps->o.animation_blacklist, NULL)) {
|
||||
log_debug("Animation disabled by animation_exclude");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reread _COMPTON_SHADOW property from a window.
|
||||
*
|
||||
@@ -1310,8 +1581,12 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w) {
|
||||
w->state != WSTATE_UNMAPPING);
|
||||
|
||||
// Invalidate the shadow we built
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
win_release_mask(ps->backend_data, w);
|
||||
// Do not set flags if window is unmapping and animation is running
|
||||
if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
|
||||
w->state != WSTATE_UNMAPPING) {
|
||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||
win_release_mask(ps->backend_data, w);
|
||||
}
|
||||
ps->pending_updates = true;
|
||||
free_paint(ps, &w->shadow_paint);
|
||||
}
|
||||
@@ -1570,14 +1845,19 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
.blur_background = false,
|
||||
.reg_ignore = NULL,
|
||||
// The following ones are updated for other reasons
|
||||
.pixmap_damaged = false, // updated by damage events
|
||||
.state = WSTATE_UNMAPPED, // updated by window state changes
|
||||
.in_openclose = true, // set to false after first map is done,
|
||||
// true here because window is just created
|
||||
.reg_ignore_valid = false, // set to true when damaged
|
||||
.flags = WIN_FLAGS_IMAGES_NONE, // updated by
|
||||
// property/attributes/etc
|
||||
// change
|
||||
.pixmap_damaged = false, // updated by damage events
|
||||
.state = WSTATE_UNMAPPED, // updated by window state changes
|
||||
.in_openclose = true, // set to false after first map is done,
|
||||
// true here because window is just created
|
||||
.animation_velocity_x = 0.0, // updated by window geometry changes
|
||||
.animation_velocity_y = 0.0, // updated by window geometry changes
|
||||
.animation_velocity_w = 0.0, // updated by window geometry changes
|
||||
.animation_velocity_h = 0.0, // updated by window geometry changes
|
||||
.animation_progress = 1.0, // updated by window geometry changes
|
||||
.animation_inv_og_distance = NAN, // updated by window geometry changes
|
||||
.reg_ignore_valid = false, // set to true when damaged
|
||||
.flags = WIN_FLAGS_IMAGES_NONE, // updated by property/attributes/etc
|
||||
// change
|
||||
.stale_props = NULL,
|
||||
.stale_props_capacity = 0,
|
||||
|
||||
@@ -1603,6 +1883,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
// have no meaning or have no use until the window
|
||||
// is mapped
|
||||
.win_image = NULL,
|
||||
.old_win_image = NULL,
|
||||
.shadow_image = NULL,
|
||||
.mask_image = NULL,
|
||||
.prev_trans = NULL,
|
||||
@@ -1876,7 +2157,8 @@ bool win_update_class(session_t *ps, struct managed_win *w) {
|
||||
w->class_general = NULL;
|
||||
|
||||
// Retrieve the property string list
|
||||
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_CLASS, &strlst, &nstr)) {
|
||||
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win, ps->atoms->aWM_CLASS,
|
||||
&strlst, &nstr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2167,17 +2449,31 @@ static void unmap_win_finish(session_t *ps, struct managed_win *w) {
|
||||
// Shadow image can be preserved.
|
||||
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
||||
win_release_pixmap(ps->backend_data, w);
|
||||
win_release_oldpixmap(ps->backend_data, w);
|
||||
}
|
||||
} else {
|
||||
assert(!w->win_image);
|
||||
assert(!w->old_win_image);
|
||||
assert(!w->shadow_image);
|
||||
}
|
||||
|
||||
// Force animation to completed position
|
||||
w->animation_velocity_x = 0;
|
||||
w->animation_velocity_y = 0;
|
||||
w->animation_velocity_w = 0;
|
||||
w->animation_velocity_h = 0;
|
||||
w->animation_progress = 1.0;
|
||||
|
||||
free_paint(ps, &w->paint);
|
||||
free_paint(ps, &w->shadow_paint);
|
||||
|
||||
// Try again at binding images when the window is mapped next time
|
||||
win_clear_flags(w, WIN_FLAGS_IMAGE_ERROR);
|
||||
|
||||
// Flag window so that it gets animated when it reapears
|
||||
// in case it wasn't destroyed
|
||||
win_set_flags(w, WIN_FLAGS_POSITION_STALE);
|
||||
win_set_flags(w, WIN_FLAGS_SIZE_STALE);
|
||||
}
|
||||
|
||||
/// Finish the destruction of a window (e.g. after fading has finished).
|
||||
@@ -2453,6 +2749,30 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
|
||||
w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old);
|
||||
w->opacity_target = win_calc_opacity_target(ps, w);
|
||||
|
||||
if (ps->o.animations && ps->o.animation_for_unmap_window != OPEN_WINDOW_ANIMATION_NONE && ps->o.wintype_option[w->window_type].animation) {
|
||||
w->dwm_mask = ANIM_UNMAP;
|
||||
init_animation(ps, w);
|
||||
|
||||
double x_dist = w->animation_dest_center_x - w->animation_center_x;
|
||||
double y_dist = w->animation_dest_center_y - w->animation_center_y;
|
||||
double w_dist = w->animation_dest_w - w->animation_w;
|
||||
double h_dist = w->animation_dest_h - w->animation_h;
|
||||
w->animation_inv_og_distance =
|
||||
1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
|
||||
w_dist * w_dist + h_dist * h_dist);
|
||||
|
||||
if (isinf(w->animation_inv_og_distance))
|
||||
w->animation_inv_og_distance = 0;
|
||||
|
||||
w->animation_progress = 0.0;
|
||||
|
||||
if (w->old_win_image) {
|
||||
ps->backend_data->ops->release_image(ps->backend_data,
|
||||
w->old_win_image);
|
||||
w->old_win_image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DBUS
|
||||
// Send D-Bus signal
|
||||
if (ps->o.dbus) {
|
||||
@@ -2498,7 +2818,7 @@ bool win_check_fade_finished(session_t *ps, struct managed_win *w) {
|
||||
///
|
||||
/// @return whether the window is destroyed and freed
|
||||
bool win_skip_fading(session_t *ps, struct managed_win *w) {
|
||||
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
|
||||
if ((w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED)) {
|
||||
assert(w->opacity_target == w->opacity);
|
||||
return false;
|
||||
}
|
||||
@@ -2510,11 +2830,12 @@ bool win_skip_fading(session_t *ps, struct managed_win *w) {
|
||||
// TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to
|
||||
// the x.c.
|
||||
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) {
|
||||
mw->randr_monitor = -1;
|
||||
for (int i = 0; i < monitors->count; i++) {
|
||||
auto e = pixman_region32_extents(&monitors->regions[i]);
|
||||
if (e->x1 <= mw->g.x && e->y1 <= mw->g.y &&
|
||||
e->x2 >= mw->g.x + mw->widthb && e->y2 >= mw->g.y + mw->heightb) {
|
||||
if (((e->x1 <= mw->g.x || e->x1 <= mw->pending_g.x) &&
|
||||
(e->x2 >= mw->g.x + mw->widthb || e->x2 >= mw->pending_g.x + mw->widthb)) &&
|
||||
(e->y1 <= mw->g.y || e->y1 <= mw->pending_g.y) &&
|
||||
(e->y2 >= mw->g.y + mw->heightb || e->y2 >= mw->pending_g.y + mw->heightb)) {
|
||||
mw->randr_monitor = i;
|
||||
log_debug("Window %#010x (%s), %dx%d+%dx%d, is entirely on the "
|
||||
"monitor %d (%dx%d+%dx%d)",
|
||||
@@ -2523,6 +2844,7 @@ void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mw->randr_monitor = -1;
|
||||
log_debug("Window %#010x (%s), %dx%d+%dx%d, is not entirely on any monitor",
|
||||
mw->base.id, mw->name, mw->g.x, mw->g.y, mw->widthb, mw->heightb);
|
||||
}
|
||||
@@ -2889,5 +3211,5 @@ win_stack_find_next_managed(const session_t *ps, const struct list_node *w) {
|
||||
/// Return whether this window is mapped on the X server side
|
||||
bool win_is_mapped_in_x(const struct managed_win *w) {
|
||||
return w->state == WSTATE_MAPPING || w->state == WSTATE_FADING ||
|
||||
w->state == WSTATE_MAPPED || (w->flags & WIN_FLAGS_MAPPED);
|
||||
w->state == WSTATE_MAPPED || w->state == WSTATE_DESTROYING || (w->flags & WIN_FLAGS_MAPPED);
|
||||
}
|
||||
|
||||
37
src/win.h
37
src/win.h
@@ -96,11 +96,25 @@ struct win_geometry {
|
||||
uint16_t border_width;
|
||||
};
|
||||
|
||||
enum {
|
||||
// dwm_mask
|
||||
ANIM_PREV_TAG = 1,
|
||||
ANIM_NEXT_TAG = (1 << 1),
|
||||
ANIM_UNMAP = (1 << 2),
|
||||
ANIM_SPECIAL_MINIMIZE = (1 << 3),
|
||||
// animation_is_tag
|
||||
ANIM_IN_TAG = 1,
|
||||
ANIM_SLOW = (1 << 1),
|
||||
ANIM_FAST = (1 << 2),
|
||||
ANIM_FADE = (1 << 3),
|
||||
};
|
||||
|
||||
struct managed_win {
|
||||
struct win base;
|
||||
/// backend data attached to this window. Only available when
|
||||
/// `state` is not UNMAPPED
|
||||
image_handle win_image;
|
||||
image_handle old_win_image; // Old window image for interpolating window contents during animations
|
||||
image_handle shadow_image;
|
||||
image_handle mask_image;
|
||||
/// Pointer to the next higher window to paint.
|
||||
@@ -152,6 +166,8 @@ struct managed_win {
|
||||
/// 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.
|
||||
/// 1 = tag prev , 2 = tag next, 4 = unmap
|
||||
uint32_t dwm_mask;
|
||||
rc_region_t *reg_ignore;
|
||||
/// Whether the reg_ignore of all windows beneath this window are valid
|
||||
bool reg_ignore_valid;
|
||||
@@ -169,6 +185,24 @@ struct managed_win {
|
||||
bool unredir_if_possible_excluded;
|
||||
/// Whether this window is in open/close state.
|
||||
bool in_openclose;
|
||||
/// Current position and destination, for animation
|
||||
double animation_center_x, animation_center_y;
|
||||
double animation_dest_center_x, animation_dest_center_y;
|
||||
double animation_w, animation_h;
|
||||
double animation_dest_w, animation_dest_h;
|
||||
/// Spring animation velocity
|
||||
double animation_velocity_x, animation_velocity_y;
|
||||
double animation_velocity_w, animation_velocity_h;
|
||||
/// Track animation progress; goes from 0 to 1
|
||||
double animation_progress;
|
||||
/// Inverse of the window distance at the start of animation, for
|
||||
/// tracking animation progress
|
||||
double animation_inv_og_distance;
|
||||
/// Animation info if it is a tag change & check if its changing window sizes
|
||||
/// 0: no tag change
|
||||
/// 1: normal tag change animation
|
||||
/// 2: tag change animation that effects window size
|
||||
uint16_t animation_is_tag;
|
||||
|
||||
// Client window related members
|
||||
/// ID of the top-level client window of the window.
|
||||
@@ -462,6 +496,9 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags);
|
||||
/// Mark properties as stale for a window
|
||||
void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *prop, int nprops);
|
||||
|
||||
/// Determine if a window should animate
|
||||
bool attr_pure win_should_animate(session_t *ps, const struct managed_win *w);
|
||||
|
||||
static inline attr_unused void win_set_property_stale(struct managed_win *w, xcb_atom_t prop) {
|
||||
return win_set_properties_stale(w, (xcb_atom_t[]){prop}, 1);
|
||||
}
|
||||
|
||||
12
src/x.c
12
src/x.c
@@ -182,10 +182,9 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
|
||||
/**
|
||||
* 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) {
|
||||
assert(ps->server_grabbed);
|
||||
auto prop_info = x_get_prop_info(&ps->c, wid, prop);
|
||||
bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
|
||||
xcb_atom_t prop, char ***pstrlst, int *pnstr) {
|
||||
auto prop_info = x_get_prop_info(c, wid, prop);
|
||||
auto type = prop_info.type;
|
||||
auto format = prop_info.format;
|
||||
auto length = prop_info.length;
|
||||
@@ -194,8 +193,7 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type != XCB_ATOM_STRING && type != ps->atoms->aUTF8_STRING &&
|
||||
type != ps->atoms->aC_STRING) {
|
||||
if (type != XCB_ATOM_STRING && type != atoms->aUTF8_STRING && type != atoms->aC_STRING) {
|
||||
log_warn("Text property %d of window %#010x has unsupported type: %d",
|
||||
prop, wid, type);
|
||||
return false;
|
||||
@@ -210,7 +208,7 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
|
||||
xcb_generic_error_t *e = NULL;
|
||||
auto word_count = (length + 4 - 1) / 4;
|
||||
auto r = xcb_get_property_reply(
|
||||
ps->c.c, xcb_get_property(ps->c.c, 0, wid, prop, type, 0, word_count), &e);
|
||||
c->c, xcb_get_property(c->c, 0, wid, prop, type, 0, word_count), &e);
|
||||
if (!r) {
|
||||
log_debug_x_error(e, "Failed to get window property for %#010x", wid);
|
||||
free(e);
|
||||
|
||||
4
src/x.h
4
src/x.h
@@ -261,8 +261,8 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
|
||||
* array
|
||||
* @param[out] pnstr Number of strings in the array
|
||||
*/
|
||||
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(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
|
||||
xcb_atom_t prop, char ***pstrlst, int *pnstr);
|
||||
|
||||
const xcb_render_pictforminfo_t *
|
||||
x_get_pictform_for_visual(struct x_connection *, xcb_visualid_t);
|
||||
|
||||
Reference in New Issue
Block a user