97 Commits

Author SHA1 Message Date
Arda Atci
e9834a5e35 fix on desktop switch
Some checks failed
coding-style / check (push) Has been cancelled
2023-04-25 03:47:31 +03:00
Arda Atci
fb99c9e472 added support for other wm 2023-04-22 13:46:16 +03:00
Arda Atci
dc9d1fe248 Merge branch 'yshui:next' into next 2023-04-01 23:12:29 +03:00
Arda Atci
1c7466c07c pos fix 2023-02-15 17:29:55 +03:00
Arda Atci
44cfa0b0ef randr_mon position fix 2023-02-14 23:24:51 +03:00
Arda Atci
bb2b4801f7 randr anim fix 2023-02-06 23:12:45 +03:00
Arda Atci
36e6b73cde randr extension 2023-02-04 23:10:18 +03:00
Arda Atci
90f57e9b1a shadow fix 2023-02-04 15:40:21 +03:00
Arda Atci
157ecd2077 shadow fix 2023-02-04 07:11:23 +03:00
Arda Atci
3a791ab559 Merge branch 'next' into nextt
Some checks failed
coding-style / check (push) Has been cancelled
2023-02-04 07:10:45 +03:00
Arda Atci
f5e8351507 shadow fix 2023-02-04 07:09:20 +03:00
Arda Atci
9f14c43989 Merge pull request #18 from yshui/next
merge yshui/next
2023-02-04 05:49:58 +03:00
Arda Atci
97d9c960e0 animations added, picom upstreamed yshui/next 2023-01-23 07:36:50 +03:00
Arda Atci
d41a291fd6 fix lerping on shadows, closes #4 2023-01-23 07:34:35 +03:00
Arda Atci
c3a7cf29ab AUR pkg, closes #15, thanks fxzzi 2023-01-23 07:34:35 +03:00
Arda Atci
3b1930d2c6 fixed open window type anim, closes #12 2023-01-23 07:34:35 +03:00
Yuxuan Shui
92a32808ae doc: list cases that trigger unredirect
unredir-if-possible doesn't just happen for fullscreen windows, be more
accurate.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Maxim Solovyov
d3c467f4c0 opengl: fix segfault in ensure_glx_context 2023-01-23 07:34:35 +03:00
Maxim Solovyov
4be96a92c7 backend: egl: fix creating eglpixmap
according to the specification of the EGL_KHR_image_pixmap extension,
if target is EGL_NATIVE_PIXMAP_KHR then ctx must be EGL_NO_CONTEXT,
otherwise, the EGL_BAD_PARAMETER error is generated. source:
https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_image_pixmap.txt

fixes #981
2023-01-23 07:34:35 +03:00
Maxim Solovyov
ab766b6386 picom: fix xcb_request_check memory leaks 2023-01-23 07:34:35 +03:00
Maxim Solovyov
7e975cdc5d picom: free root image properly
fixes #982
2023-01-23 07:34:35 +03:00
Maxim Solovyov
349f2f37db backend: egl: simplify getting a framebuffer configuration
we're using the first available framebuffer configuration that
meets our needs so there is no need to ask for more than one
2023-01-23 07:34:35 +03:00
Maxim Solovyov
7846b17c54 use _checked functions with xcb_request_check 2023-01-23 07:34:35 +03:00
Maxim Solovyov
a667886959 xrender: make corner-radius respect inactive-dim
for the new xrender backend it's enough to clip with a rounded rectangle after dimming, not before

partially fixes #867
2023-01-23 07:34:35 +03:00
Maxim Solovyov
6c08650f3c xrender: fix leak in release_rounded_corner_cache
calling wrong free function did nothing and produced ton of x errors

fixes at least #892
2023-01-23 07:34:35 +03:00
Maxim Solovyov
7f533c0b23 xrender: fix leak in deinit
and check should we actually free something

fixes at least #960
2023-01-23 07:34:35 +03:00
Maxim Solovyov
efa3d9c227 xrender: fix leak in bind_pixmap 2023-01-23 07:34:35 +03:00
Maxim Solovyov
0b965b2e5a x: fix leak in x_create_picture_with_pictfmt_and_pixmap 2023-01-23 07:34:35 +03:00
Yuxuan Shui
ba2e24af3e core: detect screen off
Use the DPMS extension to detect if screen is turned off, and unredirect
if it is. This also helps working around the problem where OpenGL
buffers lose data when screen is turned off, causing screen to flicker
later when it turns back on if use-damage is enabled.

Unfortunately the DPMS extension doesn't define an event, so we have to
periodically poll the screen state.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
0278a1fb0b backend: xrender: fix using of invalid picture when vsync is disabled
Fixes #974

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
931c1b8bf6 x: fix missing _checked
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
6906e6694a core: add set_cant_fail_cookie
Enables picom to abort when certain requests fail.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
f1bff49b1c core: expand X error handling
We used to have a list of X errors we should ignore in case they do
occur. This commit expands that functionality to also allow us aborting
on certain errors.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
fe3f53f3a4 x: fix CI build failure
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
57956fb219 general: fix compiler warning about unused results
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
c51020aef7 event: restore event sequence number after passing it to Xlib handlers
We set event sequence number to the last sequence xlib knows about to
silence its complaint about missing sequence numbers, but we forgot to
restore it back afterwards.

This used to break error ignoring mechanism in `should_ignore`. In the
last commit we updated it to use full_sequence which incidently fixed
this problem. But let's restore the sequence number anyway for good
measure.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
eec8bf79d2 core: print error with full_sequence
ev->sequence was just the lower 16 bits of the sequence number.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
227cb55ca5 backend: xrender: set update region for PresentPixmap request
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
e9ff18e1bd x: add x_{create,destroy}_region
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
43d8d3ed5d backend: add a comment
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
bf0832cd4c utils: add rolling_avg
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
9c3204cc72 utils: add rolling_max
For tracking rolling max of a stream of integers.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
bc1f99f2ae backend: gl: fix use-after-scope
'format' was pointing to an array with a shorter lifetime

suggested by @tryone144

Co-authored-by: Bernd Busse <bernd@busse-net.de>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
d21fb34e55 backend: gl: try different back buffer formats
Prefer RGB formats first, because they use less memory; but fallback to
RGBA formats, as they are formats required by OpenGL.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
7f18e74b8f backend: gl: don't add dither where it's not needed
If a pixel is perfectly representable as an 8-bit number, don't add
dither. Reduce artifacts where dither is unnecessary.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
2173654fbd options: add dithered-present option
See also 0a2cd0f14e

Related: #602

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
123ef51210 backend: gl: add dither
Add bayer ordered dithering when presenting to screen. Reduce banding
when using a strong blur. Also use 16-bit intermediary textures to
preserve precision in the rendering pipeline.

Related: #602

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
4ecc3e65a9 man: fix typo
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
4a7a9e8079 Update README.md
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Maxim Solovyov
dacadb9fc3 README: fix meson's warnings about setup commands
this fixes `WARNING: Running the setup command as `meson [options]` instead of `meson setup [options]` is ambiguous and deprecated.` warning while setting up the project
2023-01-23 07:34:35 +03:00
Jake
144e78fd5d Change dreaw -> draw 2023-01-23 07:34:35 +03:00
Yuxuan Shui
d647ccca16 README: update pcre requirements
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
9fe7e65e3e c2: replace pcre with pcre2
Because pcre has been deprecated.[1]

There are subtle changes from pcre to pcre2, so this could be a breaking
change.

Closes #895

[1]: https://www.pcre.org/original/changelog.txt

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Evgeniy Baskov
ad5a042803 win: consider border when creating the mask image
With rounded corners, X11 native border and blur enabled, left and
bottom 2*border_width pixels were not blurred, since mask did not
include border_width, only content width and height.

Create mask image with dimensions that include border width.

Signed-off-by: Evgeniy Baskov <j-basevgser@yandex.ru>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
0a82f460e0 backend: egl: fix warning 2023-01-23 07:34:35 +03:00
Yuxuan Shui
49490ab99f backend: egl: fix undefined symbols on old systems
Users with an old EGL version won't be able to use the egl backend. OTOH
we shouldn't prevent them from running picom because of a feature they
won't even use.

Don't assume the existence of EGL 1.5 symbols.

Fixes #945

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
h7x4
9bf39b8d1d picom.sample.conf: Add egl to backend option doc 2023-01-23 07:34:35 +03:00
Omar Polo
11a195747a backend: egl: don't assume glEGLImageTargetTexStorage exists
Use eglGetProcAddress instead.

Fixes #932
2023-01-23 07:34:35 +03:00
Yuxuan Shui
89690c9843 win: fix leaking of the mask image
destroy_win_finish doesn't call win_release_images to free the images,
so we need to add a release_mask call there.

Related: #892

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Omar Polo
935885d396 fix log_debug call 2023-01-23 07:34:35 +03:00
Yuxuan Shui
611f8b80c2 backend: gl: handle blur context creation failure
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
8f848c2b1d backend: gl: fix crash when shadow radius is 0
Fixes #927

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
a10a64f984 Fix typo
Fixes #922

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
1434881567 win: assert we won't clobber existing mask
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
fa21c44ee7 backend: gl: fix shadow from mask
The intermediate texture used for shadow from mask calculation did not
properly set the min/mag filter to linear, which is required by the blur
methods. Because they use texture interpolation to accelerate
the convolution calculation.

Fixes #916

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
dbb81b5116 doc: remove mention of raw string pattern
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Alp
f7596fd43a rounded corner delay fix 2023-01-23 07:34:35 +03:00
Yuxuan Shui
f7a950a638 Bump version number
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
fbe7ed5699 options: improve usage message printing
Laying the usage message out by hand is tedious, also error prone
because the option names are duplicated at 2 places and have to be
consistent.

Create a struct to hold the option names and help messages, and
do layout programmatically.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Yuxuan Shui
0004173ecd Rename COMPTON_VERSION to PICOM_VERSION
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:34:35 +03:00
Stefan Radziuk
1e5de4067b picom upto date sync with yshui, full anim support 2023-01-23 07:34:35 +03:00
Yuxuan Shui
04b80760d8 win: fix leak in win_bind_mask
Related: #905

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-01-23 07:33:56 +03:00
Arda Atci
a88393a7df fix lerping on shadows, closes #4 2023-01-23 06:00:50 +03:00
Arda Atci
8e8a62ae29 AUR pkg, closes #15, thanks fxzzi 2023-01-23 02:46:38 +03:00
Arda Atci
223533acec fixed open window type anim, closes #12 2023-01-23 02:31:51 +03:00
Arda Atci
bcc7d37546 Merge branch 'yshui:next' into next 2023-01-20 05:44:39 +03:00
Arda Atci
c9aee893d2 Merge pull request #14 from yshui/next
Merge yshui/next
2023-01-13 23:02:18 +03:00
Arda Atci
a1dcadf81b Merge pull request #13 from yshui/next
Merge yshui/next
2023-01-12 20:19:37 +03:00
Arda Atci
c86652a5c4 Merge remote-tracking branch 'yshui/next' into next 2022-12-25 21:37:18 +03:00
Arda Atci
157cb57ea7 Merge remote-tracking branch 'yshui/next' into next 2022-12-20 03:07:52 +03:00
Arda Atci
ad8feaad12 Merge branch 'yshui:next' into next 2022-12-05 21:07:53 +03:00
Arda Atci
f54315b51c Merge branch 'yshui:next' into next 2022-11-29 19:17:45 +03:00
Arda Atci
fd18476f4b Merge branch 'yshui:next' into next 2022-11-24 15:42:17 +03:00
Arda Atci
8e3ff3d63b Merge branch 'yshui:next' into next 2022-11-23 01:22:09 +03:00
Arda Atci
04b027d495 Merge pull request #9 from yshui/next
Merge yshui/next
2022-11-18 18:13:19 +03:00
Arda Atci
b93ad16e5a Merge remote-tracking branch 'yshui/next' into next 2022-11-11 20:18:17 +03:00
Arda Atci
68b48617c7 Merge pull request #8 from Alpcho/next
rounded corner delay fix
2022-10-31 22:26:41 +03:00
Alp
59d5b95483 rounded corner delay fix 2022-10-31 22:24:45 +03:00
Arda Atci
0a49da1b91 Merge remote-tracking branch 'yshui/next' into next 2022-10-30 02:27:37 +03:00
Arda Atci
260adcdc15 Merge branch 'yshui-next' into next 2022-10-25 01:31:35 +03:00
Arda Atci
4fbc9b33ad small changes 2022-10-25 01:30:51 +03:00
Arda Atci
9e397ef4e9 small changes 2022-10-25 01:30:51 +03:00
Arda Atci
70c729d389 picom upto date sync with yshui, full anim support 2022-10-25 01:30:50 +03:00
Arda Atci
23c151c8dc small changes 2022-10-25 01:24:44 +03:00
Arda Atci
b7434c7b76 small changes 2022-10-24 17:29:52 +03:00
Arda Atci
5a8da46952 Merge pull request #1 from yshui/next
win: fix leak in win_bind_mask
2022-10-10 20:01:50 +03:00
Arda Atci
f6b0b04f5b picom upto date sync with yshui, full anim support 2022-10-04 00:24:05 +03:00
21 changed files with 1125 additions and 189 deletions

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ compton
build/
compile_commands.json
build.ninja
make.sh
# language servers
.ccls-cache

View File

@@ -81,6 +81,11 @@ $ ninja -C build
### To install
#### AUR (arch)
- picom-ftlabs-git
Thanks to @Fxzzi for maintaining the package.
``` bash
$ ninja -C build install
```

View File

@@ -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
# Enable remote control via D-Bus. See the *D-BUS API* section below for more details.
# dbus = false
@@ -242,25 +250,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
@@ -304,7 +312,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,
@@ -325,24 +333,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.
@@ -359,7 +361,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
@@ -432,3 +434,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",
];

View File

@@ -23,7 +23,8 @@
WM_CLIENT_MACHINE, \
_NET_ACTIVE_WINDOW, \
_COMPTON_SHADOW, \
_NET_WM_WINDOW_TYPE
_NET_WM_DESKTOP, \
_NET_CURRENT_DESKTOP
#define ATOM_LIST2 \
_NET_WM_WINDOW_TYPE_DESKTOP, \
@@ -43,6 +44,7 @@
_NET_WM_STATE, \
_NET_WM_STATE_FULLSCREEN, \
_NET_WM_BYPASS_COMPOSITOR, \
_NET_WM_WINDOW_TYPE, \
UTF8_STRING, \
C_STRING
// clang-format on

View File

@@ -55,6 +55,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(&reg_bound_local);
pixman_region32_copy(&reg_bound_local, reg_bound);
pixman_region32_translate(&reg_bound_local, -w->g.x, -w->g.y);
pixman_region32_init(&reg_visible_local);
pixman_region32_intersect(&reg_visible_local, reg_visible, reg_paint);
pixman_region32_translate(&reg_visible_local, -w->g.x, -w->g.y);
// Data outside of the bounding shape won't be visible,
// but it is not necessary to limit the image operations
// to the bounding shape yet. So pass that as the visible
// region, not the clip region.
pixman_region32_intersect(&reg_visible_local, &reg_visible_local,
&reg_bound_local);
}
auto new_img = ps->backend_data->ops->clone_image(ps->backend_data, win_image,
&reg_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,
&reg_frame, &reg_visible_local,
(double[]){w->frame_opacity});
pixman_region32_fini(&reg_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(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
}
void handle_device_reset(session_t *ps) {
log_error("Device reset detected");
// Wait for reset to complete
@@ -187,8 +239,8 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
if (ps->root_image) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
(coord_t){0}, NULL, (coord_t){0},
&reg_paint, &reg_visible);
(coord_t){0}, NULL, (coord_t){.x = ps->root_width, .y = ps->root_height},
&reg_paint, &reg_visible, true);
} else {
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
&reg_paint);
@@ -239,6 +291,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
* 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 ||
@@ -367,7 +420,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
}
ps->backend_data->ops->compose(
ps->backend_data, w->shadow_image, shadow_coord,
inverted_mask, window_coord, &reg_shadow, &reg_visible);
inverted_mask, window_coord, &reg_shadow, &reg_visible, false);
if (inverted_mask) {
ps->backend_data->ops->set_image_property(
ps->backend_data, IMAGE_PROPERTY_INVERTED,
@@ -411,6 +464,17 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
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(
@@ -433,53 +497,43 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
}
// 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,
&reg_paint_in_bound, &reg_visible);
window_coord, NULL, dest_coord,
&reg_paint_in_bound, &reg_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(ps, 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(&reg_bound_local);
pixman_region32_copy(&reg_bound_local, &reg_bound);
pixman_region32_translate(&reg_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(&reg_visible_local);
pixman_region32_intersect(&reg_visible_local,
&reg_visible, &reg_paint);
pixman_region32_translate(&reg_visible_local, -w->g.x,
-w->g.y);
// Data outside of the bounding shape won't be visible,
// but it is not necessary to limit the image operations
// to the bounding shape yet. So pass that as the visible
// region, not the clip region.
pixman_region32_intersect(
&reg_visible_local, &reg_visible_local, &reg_bound_local);
// 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,
&reg_bound, &reg_visible,
&reg_paint, &reg_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,
&reg_bound, &reg_visible,
&reg_paint, &reg_paint_in_bound);
}
} else {
process_window_for_painting(
ps, w, w->win_image, 1.0, &reg_bound, &reg_visible,
&reg_paint, &reg_paint_in_bound);
}
auto new_img = ps->backend_data->ops->clone_image(
ps->backend_data, w->win_image, &reg_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, &reg_frame,
&reg_visible_local, (double[]){w->frame_opacity});
pixman_region32_fini(&reg_frame);
ps->backend_data->ops->compose(ps->backend_data, new_img,
window_coord, NULL, window_coord,
&reg_paint_in_bound, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local);
}
skip:
pixman_region32_fini(&reg_bound);

View File

@@ -181,7 +181,12 @@ struct backend_operations {
*/
void (*compose)(backend_t *backend_data, void *image_data, coord_t image_dst,
void *mask, coord_t mask_dst, const region_t *reg_paint,
const region_t *reg_visible);
const region_t *reg_visible, bool lerp);
void (*_compose)(backend_t *backend_data, void *image_data,
int dst_x1, int dst_y1, int dst_x2, int dst_y2,
const region_t *reg_paint, const region_t *reg_visible);
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);

View File

@@ -64,7 +64,7 @@ static void dummy_check_image(struct backend_base *base, const struct dummy_imag
void dummy_compose(struct backend_base *base, void *image, coord_t dst attr_unused,
void *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 || mask == &dummy->mask);

View File

@@ -412,7 +412,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);
@@ -494,7 +494,8 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
int extent_height, int texture_height, int root_height,
bool y_inverted, GLint *coord, GLuint *indices) {
image_dst.y = root_height - image_dst.y;
image_dst.y -= extent_height;
image_dst.y -= extent_height;
for (int i = 0; i < nrects; i++) {
// Y-flip. Note after this, crect.y1 > crect.y2
@@ -547,7 +548,7 @@ 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, void *image_data, coord_t image_dst, void *mask,
coord_t mask_dst, const region_t *reg_tgt,
const region_t *reg_visible attr_unused) {
const region_t *reg_visible attr_unused, bool lerp) {
auto gd = (struct gl_data *)base;
struct backend_image *img = image_data;
auto inner = (struct gl_texture *)img->inner;
@@ -573,6 +574,14 @@ void gl_compose(backend_t *base, void *image_data, coord_t image_dst, void *mask
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);

View File

@@ -147,7 +147,7 @@ bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
* @brief Render a region with texture data.
*/
void gl_compose(backend_t *, void *image_data, coord_t image_dst, void *mask,
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible);
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);

View File

@@ -350,7 +350,7 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
}
static void compose(backend_t *base, void *img_data, coord_t dst, void *mask, coord_t mask_dst,
const region_t *reg_paint, const region_t *reg_visible) {
const region_t *reg_paint, const region_t *reg_visible, bool lerp attr_unused) {
struct _xrender_data *xd = (void *)base;
return compose_impl(xd, img_data, dst, mask, mask_dst, reg_paint, reg_visible,
xd->back[2]);

View File

@@ -156,6 +156,8 @@ 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;
@@ -204,7 +206,6 @@ typedef struct session {
/// Width of root window.
int root_width;
// Damage of root window.
// Damage root_damage;
/// X Composite overlay window.
xcb_window_t overlay;
/// The target window for debug mode
@@ -266,6 +267,8 @@ 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
@@ -288,6 +291,8 @@ typedef struct session {
int size_expose;
/// Index of the next free slot in <code>expose_rects</code>.
int n_expose;
/// Current desktop of display
uint32_t cur_desktop;
// === Window related ===
/// A hash table of all windows.

View File

@@ -713,6 +713,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;
@@ -720,6 +724,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
@@ -764,6 +802,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,
@@ -795,7 +845,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

View File

@@ -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 {
@@ -172,6 +192,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.
@@ -255,6 +302,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;
@@ -273,6 +327,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.

View File

@@ -252,6 +252,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)) {
@@ -496,6 +505,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

View File

@@ -452,6 +452,28 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
}
if (ps->root == ev->window) {
if (ev->atom == ps->atoms->a_NET_CURRENT_DESKTOP) {
auto prop = x_get_prop(ps->c, ps->root, ps->atoms->a_NET_CURRENT_DESKTOP,
1L, XCB_ATOM_CARDINAL, 32);
if (prop.nitems) {
if (ps->cur_desktop != *prop.c32) {
win_stack_foreach_managed_safe(w, &ps->window_stack) {
if (w->a.override_redirect) {
continue;
}
if (w->cur_desktop & *prop.c32) {
w->dwm_mask = ANIM_NEXT_TAG;
} else if (w->cur_desktop & ps->cur_desktop) {
w->dwm_mask = ANIM_PREV_TAG;
}
}
ps->cur_desktop = *prop.c32;
}
}
}
if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) {
// to update focus
ps->pending_updates = true;

View File

@@ -183,6 +183,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
@@ -745,6 +754,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
}

View File

@@ -653,44 +653,210 @@ static void handle_root_flags(session_t *ps) {
}
static struct managed_win *
paint_preprocess(session_t *ps, bool *fade_running, bool *animation) {
paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
// 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;
*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 <= 0.999999999 &&
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);
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;
@@ -704,6 +870,7 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation) {
add_damage_from_win(ps, w);
}
if (win_check_fade_finished(ps, w)) {
// the window has been destroyed because fading finished
continue;
@@ -725,6 +892,9 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation) {
}
}
if (animation_running)
ps->animation_time = now;
// Opacity will not change, from now on.
rc_region_t *last_reg_ignore = rc_region_new();
@@ -1407,6 +1577,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");
@@ -1493,9 +1668,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;
auto bottom = paint_preprocess(ps, &fade_running, &animation);
auto bottom = paint_preprocess(ps, &fade_running, &animation_running);
ps->tmout_unredir_hit = false;
if (!was_redirected && ps->redirected) {
@@ -1517,6 +1692,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);
}
// If the screen is unredirected, free all_damage to stop painting
if (ps->redirected && ps->o.stoppaint_force != ON) {
@@ -1540,11 +1722,14 @@ 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;
}
// 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.
ps->redraw_needed = animation;
ps->redraw_needed = false;
}
static void draw_callback(EV_P_ ev_idle *w, int revents) {
@@ -1552,9 +1737,8 @@ static void draw_callback(EV_P_ ev_idle *w, int revents) {
draw_callback_impl(EV_A_ ps, revents);
// Don't do painting non-stop unless we are in benchmark mode, or if
// draw_callback_impl thinks we should continue painting.
if (!ps->o.benchmark && !ps->redraw_needed) {
// Don't do painting non-stop unless we are in benchmark mode
if (!ps->o.benchmark) {
ev_idle_stop(EV_A_ & ps->draw_idle);
}
}
@@ -1680,6 +1864,7 @@ 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,
@@ -1697,6 +1882,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
.white_picture = XCB_NONE,
.shadow_context = NULL,
.cur_desktop = 0,
#ifdef CONFIG_VSYNC_DRM
.drm_fd = -1,
#endif
@@ -1944,7 +2131,8 @@ 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.focus_blacklist) &&
c2_list_postprocess(ps, ps->o.animation_blacklist))) {
log_error("Post-processing of conditionals failed, some of your rules "
"might not work");
}
@@ -2255,6 +2443,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
write_pid(ps);
ev_init(&ps->animation_timer, animation_timer_callback);
if (fork && stderr_logger) {
// Remove the stderr logger if we will fork
log_remove_target_tls(stderr_logger);
@@ -2456,6 +2646,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->dpms_check_timer);
ev_idle_stop(ps->loop, &ps->draw_idle);
ev_prepare_stop(ps->loop, &ps->event_check);

View File

@@ -20,6 +20,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, \
@@ -132,6 +133,19 @@ static inline int attr_const normalize_i_range(int i, int min, int max) {
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;
}
#define min2(a, b) ((a) > (b) ? (b) : (a))
#define max2(a, b) ((a) > (b) ? (a) : (b))
#define min3(a, b, c) min2(a, min2(b, c))

388
src/win.c
View File

@@ -302,6 +302,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);
@@ -394,6 +401,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)) {
@@ -437,6 +445,10 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
}
}
if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_DESKTOP)) {
win_update_prop_desktop(ps, w);
}
if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLASS)) {
if (win_update_class(ps, w)) {
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
@@ -461,6 +473,178 @@ 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) {
win_update_monitor(ps->randr_nmonitors, ps->randr_monitor_regs, w);
if (w->randr_monitor != -1) {
auto e = pixman_region32_extents(&ps->randr_monitor_regs[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;
}
} else {
auto e = pixman_region32_extents(&ps->randr_monitor_regs[w->randr_monitor]);
randr_mon_center_x = (e->x2 + e->x1) / 2, randr_mon_center_y = (e->y2 + e->y1) / 2;
}
static double *anim_x, *anim_y, *anim_w, *anim_h;
enum open_window_animation animation;
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;
}
else
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;
}
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.
@@ -501,16 +685,87 @@ 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;
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
win_on_win_size_change(ps, w);
// Determine if a window should animate
if (win_should_animate(ps, w)) {
win_update_bounding_shape(ps, w);
damaged = true;
win_clear_flags(w, WIN_FLAGS_SIZE_STALE);
// 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;
}
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
win_on_win_size_change(ps, w);
win_update_bounding_shape(ps, w);
damaged = true;
win_clear_flags(w, WIN_FLAGS_SIZE_STALE);
}
if (win_check_flags_all(w, WIN_FLAGS_POSITION_STALE)) {
damaged = true;
win_clear_flags(w, WIN_FLAGS_POSITION_STALE);
@@ -814,6 +1069,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;
@@ -878,6 +1137,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.
*
@@ -896,6 +1173,17 @@ void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w) {
free_winprop(&prop);
}
void win_update_prop_desktop(session_t *ps, struct managed_win *w) {
winprop_t prop = x_get_prop(ps->c, w->base.id, ps->atoms->a_NET_WM_DESKTOP, 1,
XCB_ATOM_CARDINAL, 32);
if (!prop.nitems) {
w->cur_desktop = w->cur_desktop;
} else {
w->cur_desktop = *prop.c32;
}
free_winprop(&prop);
}
static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new) {
if (w->shadow == shadow_new) {
return;
@@ -1217,8 +1505,10 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
// focused state of the window
win_update_focused(ps, w);
win_determine_shadow(ps, w);
win_determine_clip_shadow_above(ps, w);
if (w->animation_progress > 0.99999999 || (w->animation_progress == 0.0 && ps->animation_time != 0L)) {
win_determine_shadow(ps, w);
win_determine_clip_shadow_above(ps, w);
}
win_determine_invert_color(ps, w);
win_determine_blur_background(ps, w);
win_determine_rounded_corners(ps, w);
@@ -1521,14 +1811,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 = 0.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,
@@ -1554,6 +1849,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,
@@ -1712,6 +2008,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS,
ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW,
ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR,
ps->atoms->a_NET_WM_DESKTOP
};
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
@@ -2111,17 +2408,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).
@@ -2390,6 +2701,32 @@ 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) {
@@ -2435,7 +2772,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;
}
@@ -2447,11 +2784,11 @@ 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(int nmons, region_t *mons, struct managed_win *mw) {
mw->randr_monitor = -1;
for (int i = 0; i < nmons; i++) {
auto e = pixman_region32_extents(&mons[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->y1 <= mw->g.y &&
// e->x2 >= mw->g.x + mw->widthb && e->y2 >= mw->g.y + mw->heightb) {
if (e->x1 <= mw->pending_g.x && e->x2 >= mw->pending_g.x + mw->widthb) {
mw->randr_monitor = i;
log_debug("Window %#010x (%s), %dx%d+%dx%d, is entirely on the "
"monitor %d (%dx%d+%dx%d)",
@@ -2460,6 +2797,7 @@ void win_update_monitor(int nmons, region_t *mons, 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);
}
@@ -2726,6 +3064,10 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo
void win_set_flags(struct managed_win *w, uint64_t flags) {
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
if (unlikely(w->state == WSTATE_DESTROYING)) {
if (w->animation_progress != 1.0) {
// Return because animation will trigger some of the flags
return;
}
log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name);
return;
}
@@ -2738,6 +3080,10 @@ void win_clear_flags(struct managed_win *w, uint64_t flags) {
log_debug("Clear flags %" PRIu64 " from window %#010x (%s)", flags, w->base.id,
w->name);
if (unlikely(w->state == WSTATE_DESTROYING)) {
if (w->animation_progress != 1.0) {
// Return because animation will trigger some of the flags
return;
}
log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
w->name);
return;
@@ -2858,5 +3204,5 @@ win_stack_find_next_managed(const session_t *ps, const struct list_node *i) {
/// 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);
}

View File

@@ -100,17 +100,32 @@ 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
void *win_image;
void *old_win_image; // Old window image for interpolating window contents during animations
void *shadow_image;
void *mask_image;
/// Pointer to the next higher window to paint.
struct managed_win *prev_trans;
/// Number of windows above this window
int stacking_rank;
uint32_t cur_desktop;
// TODO(yshui) rethink reg_ignore
// Core members
@@ -156,6 +171,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;
@@ -173,6 +190,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.
@@ -465,6 +500,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);
}
@@ -528,3 +566,5 @@ static inline bool attr_pure attr_unused win_has_frame(const struct managed_win
return w->g.border_width || w->frame_extents.top || w->frame_extents.left ||
w->frame_extents.right || w->frame_extents.bottom;
}
void win_update_prop_desktop(session_t *ps, struct managed_win *w);

View File

@@ -792,7 +792,7 @@ xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen) {
void x_update_randr_monitors(session_t *ps) {
x_free_randr_info(ps);
if (!ps->o.crop_shadow_to_monitor || !ps->randr_exists) {
if (!(ps->o.crop_shadow_to_monitor || ps->o.animations) || !ps->randr_exists) {
return;
}