mirror of https://github.com/yshui/picom.git
picom upto date sync with yshui, full anim support
small changes small changes picom upto date sync with yshui, full anim support small changes small changes rounded corner delay fix fixed open window type anim, closes #12 AUR pkg, closes #15, thanks fxzzi fix lerping on shadows, closes #4 picom upto date sync with yshui, full anim support animations added, picom upstreamed yshui/next shadow fix shadow fix randr extension randr anim fix randr_mon position fix pos fix multi monitor desktop switch fixed fix for vertical stacked monitors closes #26 revert opacity rule index on next: 5a8c61da revert opacity rule Add running section to README.md Properly animates depending on wintype animation property in config fixed window unmap crash fixes animations on destroying/unmapping windows xrender fix
This commit is contained in:
parent
f7e538fe88
commit
b37989b9ba
|
@ -16,6 +16,7 @@ compton
|
||||||
build/
|
build/
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
build.ninja
|
build.ninja
|
||||||
|
make.sh
|
||||||
|
|
||||||
# language servers
|
# language servers
|
||||||
.ccls-cache
|
.ccls-cache
|
||||||
|
|
11
README.md
11
README.md
|
@ -85,12 +85,23 @@ $ ninja -C build
|
||||||
|
|
||||||
### To install
|
### To install
|
||||||
|
|
||||||
|
#### AUR (arch)
|
||||||
|
- picom-ftlabs-git
|
||||||
|
Thanks to @Fxzzi for maintaining the package.
|
||||||
|
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
$ ninja -C build install
|
$ ninja -C build install
|
||||||
```
|
```
|
||||||
|
|
||||||
Default install prefix is `/usr/local`, you can change it with `meson configure -Dprefix=<path> build`
|
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
|
## How to Contribute
|
||||||
|
|
||||||
All contributions are welcome!
|
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 #
|
# Shadows #
|
||||||
#################################
|
#################################
|
||||||
|
|
||||||
|
|
||||||
# Enabled client-side shadows on windows. Note desktop windows
|
# Enabled client-side shadows on windows. Note desktop windows
|
||||||
# (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow,
|
# (windows with '_NET_WM_WINDOW_TYPE_DESKTOP') never get shadow,
|
||||||
# unless explicitly requested using the wintypes option.
|
# unless explicitly requested using the wintypes option.
|
||||||
#
|
#
|
||||||
# shadow = false
|
# shadow = false
|
||||||
shadow = true;
|
shadow = false;
|
||||||
|
|
||||||
# The blur radius for shadows, in pixels. (defaults to 12)
|
# The blur radius for shadows, in pixels. (defaults to 12)
|
||||||
# shadow-radius = 12
|
# shadow-radius = 12
|
||||||
shadow-radius = 7;
|
shadow-radius = 60;
|
||||||
|
|
||||||
# The opacity of shadows. (0.0 - 1.0, defaults to 0.75)
|
# The opacity of shadows. (0.0 - 1.0, defaults to 0.75)
|
||||||
# shadow-opacity = .75
|
# shadow-opacity = .75
|
||||||
|
|
||||||
# The left offset for shadows, in pixels. (defaults to -15)
|
# The left offset for shadows, in pixels. (defaults to -15)
|
||||||
# shadow-offset-x = -15
|
# shadow-offset-x = -15
|
||||||
shadow-offset-x = -7;
|
shadow-offset-x = -20;
|
||||||
|
|
||||||
# The top offset for shadows, in pixels. (defaults to -15)
|
# The top offset for shadows, in pixels. (defaults to -15)
|
||||||
# shadow-offset-y = -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).
|
# Red color value of shadow (0.0 - 1.0, defaults to 0).
|
||||||
# shadow-red = 0
|
# shadow-red = 0
|
||||||
|
@ -69,19 +116,18 @@ shadow-exclude = [
|
||||||
|
|
||||||
# Fade windows in/out when opening/closing and when opacity changes,
|
# Fade windows in/out when opening/closing and when opacity changes,
|
||||||
# unless no-fading-openclose is used.
|
# unless no-fading-openclose is used.
|
||||||
# fading = false
|
|
||||||
fading = true;
|
fading = true;
|
||||||
|
|
||||||
# Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028)
|
# Opacity change between steps while fading in. (0.01 - 1.0, defaults to 0.028)
|
||||||
# fade-in-step = 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)
|
# 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.03;
|
fade-out-step = 0.035;
|
||||||
|
|
||||||
# The time between steps in fade step, in milliseconds. (> 0, defaults to 10)
|
# 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.
|
# Specify a list of conditions of windows that should not be faded.
|
||||||
# fade-exclude = []
|
# fade-exclude = []
|
||||||
|
@ -100,15 +146,13 @@ fade-out-step = 0.03;
|
||||||
|
|
||||||
# Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
|
# Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
|
||||||
# inactive-opacity = 1
|
# inactive-opacity = 1
|
||||||
inactive-opacity = 0.8;
|
|
||||||
|
|
||||||
# Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default)
|
# Opacity of window titlebars and borders. (0.1 - 1.0, disabled by default)
|
||||||
# frame-opacity = 1.0
|
# frame-opacity = 1.0
|
||||||
frame-opacity = 0.7;
|
|
||||||
|
|
||||||
# Let inactive opacity set by -i override the '_NET_WM_WINDOW_OPACITY' values of windows.
|
# Let inactive opacity set by -i override the '_NET_WM_WINDOW_OPACITY' values of windows.
|
||||||
# inactive-opacity-override = true
|
# 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)
|
# Default opacity for active windows. (0.0 - 1.0, defaults to 1.0)
|
||||||
# active-opacity = 1.0
|
# active-opacity = 1.0
|
||||||
|
@ -118,21 +162,13 @@ inactive-opacity-override = false;
|
||||||
|
|
||||||
# Specify a list of conditions of windows that should never be considered focused.
|
# Specify a list of conditions of windows that should never be considered focused.
|
||||||
# focus-exclude = []
|
# 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.
|
# Use fixed inactive dim value, instead of adjusting according to window opacity.
|
||||||
# inactive-dim-fixed = 1.0
|
# 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 #
|
# Corners #
|
||||||
#################################
|
#################################
|
||||||
|
@ -140,52 +176,21 @@ focus-exclude = [ "class_g = 'Cairo-clock'" ];
|
||||||
# Sets the radius of rounded window corners. When > 0, the compositor will
|
# Sets the radius of rounded window corners. When > 0, the compositor will
|
||||||
# round the corners of windows. Does not interact well with
|
# round the corners of windows. Does not interact well with
|
||||||
# `transparent-clipping`.
|
# `transparent-clipping`.
|
||||||
corner-radius = 0
|
corner-radius = 11;
|
||||||
|
|
||||||
# Exclude conditions for rounded corners.
|
# Exclude conditions for rounded corners.
|
||||||
rounded-corners-exclude = [
|
#rounded-corners-exclude = [
|
||||||
"window_type = 'dock'",
|
# "window_type = 'dock'",
|
||||||
"window_type = 'desktop'"
|
# "window_type = 'desktop'"
|
||||||
];
|
#];
|
||||||
|
|
||||||
|
blur: {
|
||||||
#################################
|
method = "dual_kawase";
|
||||||
# Background-Blurring #
|
strength = 9;
|
||||||
#################################
|
background = true;
|
||||||
|
background-frame = false;
|
||||||
|
background-fixed = false;
|
||||||
# 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";
|
|
||||||
|
|
||||||
|
|
||||||
# Exclude conditions for background blur.
|
# Exclude conditions for background blur.
|
||||||
|
@ -200,17 +205,13 @@ blur-background-exclude = [
|
||||||
# General Settings #
|
# 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.
|
# Daemonize process. Fork to background after initialization. Causes issues with certain (badly-written) drivers.
|
||||||
# daemon = false
|
# daemon = false
|
||||||
|
|
||||||
# Specify the backend to use: `xrender`, `glx`, `egl` or `xr_glx_hybrid`.
|
# Specify the backend to use: `xrender`, `glx`, `egl` or `xr_glx_hybrid`.
|
||||||
# `xrender` is the default one.
|
# `xrender` is the default one.
|
||||||
#
|
#
|
||||||
# backend = "glx"
|
backend = "glx"
|
||||||
backend = "xrender";
|
|
||||||
|
|
||||||
# Use higher precision during rendering, and apply dither when presenting the
|
# Use higher precision during rendering, and apply dither when presenting the
|
||||||
# rendered screen. Reduces banding artifacts, but might cause performance
|
# rendered screen. Reduces banding artifacts, but might cause performance
|
||||||
|
@ -218,8 +219,7 @@ backend = "xrender";
|
||||||
dithered-present = false;
|
dithered-present = false;
|
||||||
|
|
||||||
# Enable/disable VSync.
|
# Enable/disable VSync.
|
||||||
# vsync = false
|
# vsync = true
|
||||||
vsync = true;
|
|
||||||
|
|
||||||
# Try to detect WM windows (a non-override-redirect window with no
|
# Try to detect WM windows (a non-override-redirect window with no
|
||||||
# child that has 'WM_STATE') and mark them as active.
|
# child that has 'WM_STATE') and mark them as active.
|
||||||
|
@ -235,25 +235,25 @@ mark-ovredir-focused = true;
|
||||||
# shaped windows. The accuracy is not very high, unfortunately.
|
# shaped windows. The accuracy is not very high, unfortunately.
|
||||||
#
|
#
|
||||||
# detect-rounded-corners = false
|
# detect-rounded-corners = false
|
||||||
detect-rounded-corners = true;
|
detect-rounded-corners = false;
|
||||||
|
|
||||||
# Detect '_NET_WM_WINDOW_OPACITY' on client windows, useful for window managers
|
# Detect '_NET_WM_WINDOW_OPACITY' on client windows, useful for window managers
|
||||||
# not passing '_NET_WM_WINDOW_OPACITY' of client windows to frame windows.
|
# not passing '_NET_WM_WINDOW_OPACITY' of client windows to frame windows.
|
||||||
#
|
#
|
||||||
# detect-client-opacity = false
|
# detect-client-opacity = false
|
||||||
detect-client-opacity = true;
|
detect-client-opacity = false;
|
||||||
|
|
||||||
# Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window,
|
# Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window,
|
||||||
# rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy,
|
# rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy,
|
||||||
# provided that the WM supports it.
|
# 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,
|
# Unredirect all windows if a full-screen opaque window is detected,
|
||||||
# to maximize performance for full-screen windows. Known to cause flickering
|
# to maximize performance for full-screen windows. Known to cause flickering
|
||||||
# when redirecting/unredirecting windows.
|
# when redirecting/unredirecting windows.
|
||||||
#
|
#
|
||||||
# unredir-if-possible = false
|
unredir-if-possible = false;
|
||||||
|
|
||||||
# Delay before unredirecting the window, in milliseconds. Defaults to 0.
|
# Delay before unredirecting the window, in milliseconds. Defaults to 0.
|
||||||
# unredir-if-possible-delay = 0
|
# unredir-if-possible-delay = 0
|
||||||
|
@ -297,7 +297,7 @@ detect-transient = true;
|
||||||
# practically happened) and may not work with blur-background.
|
# practically happened) and may not work with blur-background.
|
||||||
# My tests show a 15% performance boost. Recommended.
|
# My tests show a 15% performance boost. Recommended.
|
||||||
#
|
#
|
||||||
# glx-no-stencil = false
|
glx-no-stencil = true;
|
||||||
|
|
||||||
# GLX backend: Avoid rebinding pixmap on window damage.
|
# GLX backend: Avoid rebinding pixmap on window damage.
|
||||||
# Probably could improve performance on rapid window content changes,
|
# Probably could improve performance on rapid window content changes,
|
||||||
|
@ -318,24 +318,18 @@ use-damage = true;
|
||||||
# calls are finished before picom starts drawing. Needed on nvidia-drivers
|
# calls are finished before picom starts drawing. Needed on nvidia-drivers
|
||||||
# with GLX backend for some users.
|
# with GLX backend for some users.
|
||||||
#
|
#
|
||||||
# xrender-sync-fence = false
|
xrender-sync-fence = true;
|
||||||
|
|
||||||
# GLX backend: Use specified GLSL fragment shader for rendering window
|
# GLX backend: Use specified GLSL fragment shader for rendering window contents.
|
||||||
# contents. Read the man page for a detailed explanation of the interface.
|
# See `compton-default-fshader-win.glsl` and `compton-fake-transparency-fshader-win.glsl`
|
||||||
|
# in the source tree for examples.
|
||||||
#
|
#
|
||||||
# window-shader-fg = "default"
|
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'"
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# Force all windows to be painted with blending. Useful if you
|
# Force all windows to be painted with blending. Useful if you
|
||||||
# have a glx-fshader-win that could turn opaque pixels transparent.
|
# 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.
|
# Do not use EWMH to detect fullscreen windows.
|
||||||
# Reverts to checking if a window is fullscreen based only on its size and coordinates.
|
# Reverts to checking if a window is fullscreen based only on its size and coordinates.
|
||||||
|
@ -352,7 +346,7 @@ use-damage = true;
|
||||||
# Make transparent windows clip other windows like non-transparent windows do,
|
# Make transparent windows clip other windows like non-transparent windows do,
|
||||||
# instead of blending on top of them.
|
# 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
|
# 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
|
# clipping applied. Useful for screenshot tools, where you need to be able to
|
||||||
|
@ -425,3 +419,9 @@ wintypes:
|
||||||
popup_menu = { opacity = 0.8; }
|
popup_menu = { opacity = 0.8; }
|
||||||
dropdown_menu = { opacity = 0.8; }
|
dropdown_menu = { opacity = 0.8; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
opacity-rule = [
|
||||||
|
"100:class_g = 'St' && focused",
|
||||||
|
"50:class_g = 'St' && !focused",
|
||||||
|
"100:fullscreen",
|
||||||
|
];
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
_NET_WM_WINDOW_TYPE, \
|
_NET_WM_WINDOW_TYPE, \
|
||||||
_XROOTPMAP_ID, \
|
_XROOTPMAP_ID, \
|
||||||
ESETROOT_PMAP_ID, \
|
ESETROOT_PMAP_ID, \
|
||||||
_XSETROOT_ID
|
_XSETROOT_ID \
|
||||||
|
_NET_WM_WINDOW_TYPE,
|
||||||
|
|
||||||
#define ATOM_LIST2 \
|
#define ATOM_LIST2 \
|
||||||
_NET_WM_WINDOW_TYPE_DESKTOP, \
|
_NET_WM_WINDOW_TYPE_DESKTOP, \
|
||||||
|
|
|
@ -56,6 +56,58 @@ region_t get_damage(session_t *ps, bool all_damage) {
|
||||||
return region;
|
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) {
|
void handle_device_reset(session_t *ps) {
|
||||||
log_error("Device reset detected");
|
log_error("Device reset detected");
|
||||||
// Wait for reset to complete
|
// 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) {
|
if (ps->root_image) {
|
||||||
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
|
ps->backend_data->ops->compose(ps->backend_data, ps->root_image,
|
||||||
(coord_t){0}, NULL, (coord_t){0},
|
(coord_t){0}, NULL, (coord_t){.x = ps->root_width, .y = ps->root_height},
|
||||||
®_paint, ®_visible);
|
®_paint, ®_visible, true);
|
||||||
} else {
|
} else {
|
||||||
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
|
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
|
||||||
®_paint);
|
®_paint);
|
||||||
|
@ -262,6 +314,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||||
* option */
|
* option */
|
||||||
auto real_win_mode = w->mode;
|
auto real_win_mode = w->mode;
|
||||||
coord_t window_coord = {.x = w->g.x, .y = w->g.y};
|
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 &&
|
if (w->blur_background &&
|
||||||
(ps->o.force_win_blend || real_win_mode == WMODE_TRANS ||
|
(ps->o.force_win_blend || real_win_mode == WMODE_TRANS ||
|
||||||
|
@ -399,7 +452,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||||
}
|
}
|
||||||
ps->backend_data->ops->compose(
|
ps->backend_data->ops->compose(
|
||||||
ps->backend_data, w->shadow_image, shadow_coord,
|
ps->backend_data, w->shadow_image, shadow_coord,
|
||||||
inverted_mask, window_coord, ®_shadow, ®_visible);
|
inverted_mask, window_coord, ®_shadow, ®_visible, false);
|
||||||
if (inverted_mask) {
|
if (inverted_mask) {
|
||||||
ps->backend_data->ops->set_image_property(
|
ps->backend_data->ops->set_image_property(
|
||||||
ps->backend_data, IMAGE_PROPERTY_INVERTED,
|
ps->backend_data, IMAGE_PROPERTY_INVERTED,
|
||||||
|
@ -443,6 +496,17 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||||
ps->backend_data->ops->set_image_property(
|
ps->backend_data->ops->set_image_property(
|
||||||
ps->backend_data, IMAGE_PROPERTY_BORDER_WIDTH,
|
ps->backend_data, IMAGE_PROPERTY_BORDER_WIDTH,
|
||||||
w->win_image, &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(
|
ps->backend_data->ops->set_image_property(
|
||||||
|
@ -465,53 +529,43 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw window on target
|
// 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,
|
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
|
||||||
window_coord, NULL, window_coord,
|
window_coord, NULL, dest_coord,
|
||||||
®_paint_in_bound, ®_visible);
|
®_paint_in_bound, ®_visible, true);
|
||||||
} else {
|
} else {
|
||||||
// For window image processing, we don't have to limit the process
|
if (is_animating && w->old_win_image) {
|
||||||
// region to damage for correctness. (see <damager-note> for
|
bool is_focused = win_is_focused_raw(ps, w);
|
||||||
// details)
|
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
|
bool resizing =
|
||||||
// don't limit process region to damage, we provide that info in
|
w->g.width != w->pending_g.width ||
|
||||||
// reg_visible as a hint. Since window image data outside of the
|
w->g.height != w->pending_g.height;
|
||||||
// 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);
|
|
||||||
|
|
||||||
pixman_region32_init(®_visible_local);
|
// Only animate opacity here if we are resizing
|
||||||
pixman_region32_intersect(®_visible_local,
|
// a transparent window
|
||||||
®_visible, ®_paint);
|
process_window_for_painting(ps, w, w->win_image,
|
||||||
pixman_region32_translate(®_visible_local, -w->g.x,
|
is_focused ? 1.0 : w->opacity >= 1 ? 1.0 : w->animation_progress,
|
||||||
-w->g.y);
|
®_bound, ®_visible,
|
||||||
// Data outside of the bounding shape won't be visible,
|
®_paint, ®_paint_in_bound);
|
||||||
// but it is not necessary to limit the image operations
|
|
||||||
// to the bounding shape yet. So pass that as the visible
|
// Only do this if size changes as otherwise moving
|
||||||
// region, not the clip region.
|
// transparent windows will flicker and if you just
|
||||||
pixman_region32_intersect(
|
// move so slightly they will keep flickering
|
||||||
®_visible_local, ®_visible_local, ®_bound_local);
|
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:
|
skip:
|
||||||
pixman_region32_fini(®_bound);
|
pixman_region32_fini(®_bound);
|
||||||
|
|
|
@ -180,7 +180,12 @@ struct backend_operations {
|
||||||
*/
|
*/
|
||||||
void (*compose)(backend_t *backend_data, image_handle image, coord_t image_dst,
|
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,
|
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);
|
||||||
|
|
||||||
|
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.
|
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
|
||||||
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
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,
|
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,
|
image_handle mask attr_unused, coord_t mask_dst attr_unused,
|
||||||
const region_t *reg_paint 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;
|
auto dummy attr_unused = (struct dummy_data *)base;
|
||||||
dummy_check_image(base, image);
|
dummy_check_image(base, image);
|
||||||
assert(mask == NULL || (struct backend_image *)mask == &dummy->mask);
|
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);
|
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);
|
(float)mask_offset.y);
|
||||||
if (mask != NULL) {
|
if (mask != NULL) {
|
||||||
glUniform1i(win_shader->uniform_mask_inverted, mask->color_inverted);
|
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 = root_height - image_dst.y;
|
||||||
image_dst.y -= extent_height;
|
image_dst.y -= extent_height;
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < nrects; i++) {
|
for (int i = 0; i < nrects; i++) {
|
||||||
// Y-flip. Note after this, crect.y1 > crect.y2
|
// Y-flip. Note after this, crect.y1 > crect.y2
|
||||||
rect_t crect = rects[i];
|
rect_t crect = rects[i];
|
||||||
|
@ -553,7 +554,7 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
|
||||||
// TODO(yshui) make use of reg_visible
|
// TODO(yshui) make use of reg_visible
|
||||||
void gl_compose(backend_t *base, image_handle image, coord_t image_dst,
|
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,
|
image_handle 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;
|
auto gd = (struct gl_data *)base;
|
||||||
auto img = (struct backend_image *)image;
|
auto img = (struct backend_image *)image;
|
||||||
auto mask = (struct backend_image *)mask_;
|
auto mask = (struct backend_image *)mask_;
|
||||||
|
@ -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};
|
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,
|
x_rect_to_coords(nrects, rects, image_dst, inner->height, inner->height,
|
||||||
gd->height, inner->y_inverted, coord, indices);
|
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);
|
_gl_compose(base, img, gd->back_fbo, mask, mask_offset, coord, indices, nrects);
|
||||||
|
|
||||||
free(indices);
|
free(indices);
|
||||||
|
|
|
@ -151,7 +151,7 @@ bool gl_last_render_time(backend_t *backend_data, struct timespec *time);
|
||||||
* @brief Render a region with texture data.
|
* @brief Render a region with texture data.
|
||||||
*/
|
*/
|
||||||
void gl_compose(backend_t *, image_handle image, coord_t image_dst, image_handle mask,
|
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);
|
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible, bool lerp);
|
||||||
|
|
||||||
void gl_root_change(backend_t *base, session_t *);
|
void gl_root_change(backend_t *base, session_t *);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "backend/backend.h"
|
#include "backend/backend.h"
|
||||||
#include "backend/backend_common.h"
|
#include "backend/backend_common.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "compiler.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -362,7 +363,7 @@ static void xrender_compose_impl(struct xrender_data *xd, struct xrender_image *
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xrender_compose(backend_t *base, image_handle image_, coord_t dst, image_handle mask_,
|
xrender_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, bool lerp attr_unused) {
|
||||||
auto xd = (struct xrender_data *)base;
|
auto xd = (struct xrender_data *)base;
|
||||||
auto image = (struct xrender_image *)image_;
|
auto image = (struct xrender_image *)image_;
|
||||||
auto mask = (struct xrender_image *)mask_;
|
auto mask = (struct xrender_image *)mask_;
|
||||||
|
|
|
@ -143,6 +143,8 @@ typedef struct session {
|
||||||
ev_timer unredir_timer;
|
ev_timer unredir_timer;
|
||||||
/// Timer for fading
|
/// Timer for fading
|
||||||
ev_timer fade_timer;
|
ev_timer fade_timer;
|
||||||
|
/// Timer for animations
|
||||||
|
ev_timer animation_timer;
|
||||||
/// Use an ev_timer callback for drawing
|
/// Use an ev_timer callback for drawing
|
||||||
ev_timer draw_timer;
|
ev_timer draw_timer;
|
||||||
/// Called every time we have timeouts or new data on socket,
|
/// Called every time we have timeouts or new data on socket,
|
||||||
|
@ -177,6 +179,8 @@ typedef struct session {
|
||||||
int root_width;
|
int root_width;
|
||||||
/// Height of root window.
|
/// Height of root window.
|
||||||
int root_height;
|
int root_height;
|
||||||
|
int selmon_center_x;
|
||||||
|
int selmon_center_y;
|
||||||
/// X Composite overlay window.
|
/// X Composite overlay window.
|
||||||
xcb_window_t overlay;
|
xcb_window_t overlay;
|
||||||
/// The target window for debug mode
|
/// The target window for debug mode
|
||||||
|
@ -266,6 +270,8 @@ typedef struct session {
|
||||||
xcb_render_picture_t *alpha_picts;
|
xcb_render_picture_t *alpha_picts;
|
||||||
/// Time of last fading. In milliseconds.
|
/// Time of last fading. In milliseconds.
|
||||||
long long fade_time;
|
long long fade_time;
|
||||||
|
/// Time of last window animation step. In milliseconds.
|
||||||
|
long animation_time; // TODO(dccsillag) turn into `long long`, like fade_time
|
||||||
// Cached blur convolution kernels.
|
// Cached blur convolution kernels.
|
||||||
struct x_convolution_kernel **blur_kerns_cache;
|
struct x_convolution_kernel **blur_kerns_cache;
|
||||||
/// If we should quit
|
/// 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
|
// opacity logic is complicated, and needs an "unset" state
|
||||||
opt->wintype_option[i].opacity = NAN;
|
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) {
|
if (!mask[i].clip_shadow_above) {
|
||||||
mask[i].clip_shadow_above = true;
|
mask[i].clip_shadow_above = true;
|
||||||
opt->wintype_option[i].clip_shadow_above = false;
|
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,
|
char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||||
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask) {
|
bool *fading_enable, bool *hasneg, win_option_mask_t *winopt_mask) {
|
||||||
// clang-format off
|
// 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,
|
.no_fading_destroyed_argb = false,
|
||||||
.fade_blacklist = NULL,
|
.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 = 1.0,
|
||||||
.inactive_opacity_override = false,
|
.inactive_opacity_override = false,
|
||||||
.active_opacity = 1.0,
|
.active_opacity = 1.0,
|
||||||
|
@ -879,7 +929,8 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||||
|
|
||||||
.track_leader = false,
|
.track_leader = false,
|
||||||
|
|
||||||
.rounded_corners_blacklist = NULL
|
.rounded_corners_blacklist = NULL,
|
||||||
|
.animation_blacklist = NULL
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
54
src/config.h
54
src/config.h
|
@ -40,6 +40,24 @@ enum backend {
|
||||||
NUM_BKEND,
|
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 {
|
typedef struct win_option_mask {
|
||||||
bool shadow : 1;
|
bool shadow : 1;
|
||||||
bool fade : 1;
|
bool fade : 1;
|
||||||
|
@ -49,6 +67,7 @@ typedef struct win_option_mask {
|
||||||
bool redir_ignore : 1;
|
bool redir_ignore : 1;
|
||||||
bool opacity : 1;
|
bool opacity : 1;
|
||||||
bool clip_shadow_above : 1;
|
bool clip_shadow_above : 1;
|
||||||
|
enum open_window_animation animation;
|
||||||
} win_option_mask_t;
|
} win_option_mask_t;
|
||||||
|
|
||||||
typedef struct win_option {
|
typedef struct win_option {
|
||||||
|
@ -60,6 +79,7 @@ typedef struct win_option {
|
||||||
bool redir_ignore;
|
bool redir_ignore;
|
||||||
double opacity;
|
double opacity;
|
||||||
bool clip_shadow_above;
|
bool clip_shadow_above;
|
||||||
|
enum open_window_animation animation;
|
||||||
} win_option_t;
|
} win_option_t;
|
||||||
|
|
||||||
enum blur_method {
|
enum blur_method {
|
||||||
|
@ -197,6 +217,33 @@ typedef struct options {
|
||||||
/// Fading blacklist. A linked list of conditions.
|
/// Fading blacklist. A linked list of conditions.
|
||||||
c2_lptr_t *fade_blacklist;
|
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 ===
|
// === Opacity ===
|
||||||
/// Default opacity for inactive windows.
|
/// Default opacity for inactive windows.
|
||||||
/// 32-bit integer with the format of _NET_WM_WINDOW_OPACITY.
|
/// 32-bit integer with the format of _NET_WM_WINDOW_OPACITY.
|
||||||
|
@ -280,6 +327,12 @@ typedef struct options {
|
||||||
// Make transparent windows clip other windows, instead of blending on top of
|
// Make transparent windows clip other windows, instead of blending on top of
|
||||||
// them
|
// them
|
||||||
bool transparent_clipping;
|
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
|
/// A list of conditions of windows to which transparent clipping
|
||||||
/// should not apply
|
/// should not apply
|
||||||
c2_lptr_t *transparent_clipping_blacklist;
|
c2_lptr_t *transparent_clipping_blacklist;
|
||||||
|
@ -300,6 +353,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,
|
char *must_use locate_auxiliary_file(const char *scope, const char *path,
|
||||||
const char *include_dir);
|
const char *include_dir);
|
||||||
enum blur_method must_use parse_blur_method(const char *src);
|
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.
|
* 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;
|
o->clip_shadow_above = ival;
|
||||||
mask->clip_shadow_above = true;
|
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;
|
double fval;
|
||||||
if (config_setting_lookup_float(setting, "opacity", &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");
|
parse_cfg_condlst(&cfg, &opt->shadow_clip_list, "clip-shadow-above");
|
||||||
// --fade-exclude
|
// --fade-exclude
|
||||||
parse_cfg_condlst(&cfg, &opt->fade_blacklist, "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
|
// --focus-exclude
|
||||||
parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude");
|
parse_cfg_condlst(&cfg, &opt->focus_blacklist, "focus-exclude");
|
||||||
// --invert-color-include
|
// --invert-color-include
|
||||||
|
|
|
@ -187,6 +187,19 @@ static const struct picom_option picom_options[] = {
|
||||||
"window is fullscreen based only on its size and coordinates."},
|
"window is fullscreen based only on its size and coordinates."},
|
||||||
{"realtime" , no_argument , 804, NULL , "Enable realtime scheduling. This might reduce latency, but might also cause "
|
{"realtime" , no_argument , 804, NULL , "Enable realtime scheduling. This might reduce latency, but might also cause "
|
||||||
"other issues. Disable this if you see the compositor being killed."},
|
"other issues. Disable this if you see the compositor being killed."},
|
||||||
|
|
||||||
|
{"animation-stiffness-in-tag" , required_argument, 805, NULL , "Stiffness (a.k.a. tension) parameter for animation (default: 200.0)."},
|
||||||
|
{"animation-stiffness-tag-change" , required_argument, 806, NULL , "Stiffness (a.k.a. tension) parameter for animation (default: 200.0). ??"},
|
||||||
|
{"animation-dampening" , required_argument, 807, NULL , "Dampening (a.k.a. friction) parameter for animation (default: 25.0)."},
|
||||||
|
{"animation-window-mass" , required_argument, 808, NULL , "Mass parameter for animation (default: 1.0)."},
|
||||||
|
{"animation-clamping" , no_argument , 809, NULL , "Whether to clamp animations (default: true)."},
|
||||||
|
{"animation-for-open-window" , required_argument, 810, NULL , "Which animation to run when opening a window. Must be one of `none`, `fade`, "
|
||||||
|
"`zoom`, `slide-down`, `slide-up`, `slide-left`, `slide-right`. (default: none)."},
|
||||||
|
{"animation-for-transient-window" , required_argument, 811, NULL , "Which animation to run when opening a transient window. Must be one of `none`, "
|
||||||
|
"`fly-in`, `zoom`, `slide-down`, `slide-up`, `slide-left`, `slide-right`. "
|
||||||
|
"(default: none)."},
|
||||||
|
{"animation-exclude" , required_argument, 812, "COND" , "Exclude conditions for animation."},
|
||||||
|
{"animations" , no_argument , 813, NULL , "Run animations for window geometry changes (movement and scaling)."},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -376,6 +389,7 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||||
// Parse command line arguments. Range checking will be done later.
|
// Parse command line arguments. Range checking will be done later.
|
||||||
|
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
char *endptr = NULL;
|
||||||
const char *deprecation_message attr_unused =
|
const char *deprecation_message attr_unused =
|
||||||
"has been removed. If you encounter problems "
|
"has been removed. If you encounter problems "
|
||||||
"without this feature, please feel free to "
|
"without this feature, please feel free to "
|
||||||
|
@ -584,7 +598,6 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||||
P_CASEBOOL(298, glx_no_rebind_pixmap);
|
P_CASEBOOL(298, glx_no_rebind_pixmap);
|
||||||
case 299: {
|
case 299: {
|
||||||
// --glx-swap-method
|
// --glx-swap-method
|
||||||
char *endptr;
|
|
||||||
long tmpval = strtol(optarg, &endptr, 10);
|
long tmpval = strtol(optarg, &endptr, 10);
|
||||||
bool should_remove = true;
|
bool should_remove = true;
|
||||||
if (*endptr || !(*optarg)) {
|
if (*endptr || !(*optarg)) {
|
||||||
|
@ -753,6 +766,68 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||||
P_CASEBOOL(802, debug_mode);
|
P_CASEBOOL(802, debug_mode);
|
||||||
P_CASEBOOL(803, no_ewmh_fullscreen);
|
P_CASEBOOL(803, no_ewmh_fullscreen);
|
||||||
P_CASEBOOL(804, use_realtime_scheduling);
|
P_CASEBOOL(804, use_realtime_scheduling);
|
||||||
|
case 805:
|
||||||
|
// --animation-stiffness
|
||||||
|
opt->animation_stiffness = strtod(optarg, &endptr);
|
||||||
|
if (*endptr != '\0' || opt->animation_stiffness < 0) {
|
||||||
|
log_error("Invalid animation-stiffness value: %s", optarg);
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 806:
|
||||||
|
// --animation-stiffness-for-tags
|
||||||
|
opt->animation_stiffness_tag_change = strtod(optarg, &endptr);
|
||||||
|
if (*endptr != '\0' || opt->animation_stiffness_tag_change < 0) {
|
||||||
|
log_error("Invalid animation-stiffness-for-tags value: %s", optarg);
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 807:
|
||||||
|
// --animation-dampening
|
||||||
|
opt->animation_dampening = strtod(optarg, &endptr);
|
||||||
|
if (*endptr != '\0' || opt->animation_dampening < 0) {
|
||||||
|
log_error("Invalid animation-dampening value: %s", optarg);
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 808:
|
||||||
|
// --animation-window-masss
|
||||||
|
opt->animation_window_mass = strtod(optarg, &endptr);
|
||||||
|
if (*endptr != '\0' || opt->animation_window_mass < 0) {
|
||||||
|
log_error("Invalid animation-window-mass value: %s", optarg);
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
P_CASEBOOL(813, animations);
|
||||||
default: usage(argv[0], 1); break;
|
default: usage(argv[0], 1); break;
|
||||||
#undef P_CASEBOOL
|
#undef P_CASEBOOL
|
||||||
}
|
}
|
||||||
|
@ -912,7 +987,8 @@ void options_postprocess_c2_lists(struct c2_state *state, struct x_connection *c
|
||||||
c2_list_postprocess(state, c->c, option->opacity_rules) &&
|
c2_list_postprocess(state, c->c, option->opacity_rules) &&
|
||||||
c2_list_postprocess(state, c->c, option->rounded_corners_blacklist) &&
|
c2_list_postprocess(state, c->c, option->rounded_corners_blacklist) &&
|
||||||
c2_list_postprocess(state, c->c, option->corner_radius_rules) &&
|
c2_list_postprocess(state, c->c, option->corner_radius_rules) &&
|
||||||
c2_list_postprocess(state, c->c, option->focus_blacklist))) {
|
c2_list_postprocess(state, c->c, option->focus_blacklist) &&
|
||||||
|
c2_list_postprocess(state, c->c, option->animation_blacklist))) {
|
||||||
log_error("Post-processing of conditionals failed, some of your rules "
|
log_error("Post-processing of conditionals failed, some of your rules "
|
||||||
"might not work");
|
"might not work");
|
||||||
}
|
}
|
||||||
|
|
282
src/picom.c
282
src/picom.c
|
@ -906,80 +906,256 @@ static void handle_root_flags(session_t *ps) {
|
||||||
*
|
*
|
||||||
* @return whether the operation succeeded
|
* @return whether the operation succeeded
|
||||||
*/
|
*/
|
||||||
static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
|
static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running,
|
||||||
struct managed_win **out_bottom) {
|
struct managed_win **out_bottom) {
|
||||||
// XXX need better, more general name for `fade_running`. It really
|
// 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;
|
struct managed_win *bottom = NULL;
|
||||||
|
auto now = get_time_ms();
|
||||||
*fade_running = false;
|
*fade_running = false;
|
||||||
*animation = false;
|
*animation_running = false;
|
||||||
*out_bottom = NULL;
|
*out_bottom = NULL;
|
||||||
|
|
||||||
// Fading step calculation
|
// Fading step calculation
|
||||||
long long steps = 0L;
|
long long steps = 0L;
|
||||||
auto now = get_time_ms();
|
|
||||||
if (ps->fade_time) {
|
if (ps->fade_time) {
|
||||||
assert(now >= ps->fade_time);
|
assert(now >= ps->fade_time);
|
||||||
steps = (now - ps->fade_time) / ps->o.fade_delta;
|
steps = (now - ps->fade_time) / ps->o.fade_delta;
|
||||||
} else {
|
} else {
|
||||||
// Reset fade_time if unset
|
// Reset fade_time if unset
|
||||||
ps->fade_time = get_time_ms();
|
ps->fade_time = now;
|
||||||
steps = 0L;
|
steps = 0L;
|
||||||
}
|
}
|
||||||
ps->fade_time += steps * ps->o.fade_delta;
|
ps->fade_time += steps * ps->o.fade_delta;
|
||||||
|
|
||||||
// First, let's process fading, and animated shaders
|
if (ps->o.animations && !ps->animation_time)
|
||||||
// TODO(yshui) check if a window is fully obscured, and if we don't need to
|
ps->animation_time = now;
|
||||||
// process fading or animation for it.
|
|
||||||
|
double delta_secs = (double)(now - ps->animation_time) / 1000;
|
||||||
|
|
||||||
|
// First, let's process fading
|
||||||
win_stack_foreach_managed_safe(w, &ps->window_stack) {
|
win_stack_foreach_managed_safe(w, &ps->window_stack) {
|
||||||
const winmode_t mode_old = w->mode;
|
const winmode_t mode_old = w->mode;
|
||||||
const bool was_painted = w->to_paint;
|
const bool was_painted = w->to_paint;
|
||||||
const double opacity_old = w->opacity;
|
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) {
|
if (win_should_dim(ps, w) != w->dim) {
|
||||||
w->dim = win_should_dim(ps, w);
|
w->dim = win_should_dim(ps, w);
|
||||||
add_damage_from_win(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 (w->opacity != w->opacity_target) {
|
||||||
if (run_fade(ps, &w, steps)) {
|
// Run fading
|
||||||
*fade_running = true;
|
if (run_fade(ps, &w, steps)) {
|
||||||
}
|
*fade_running = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Add window to damaged area if its opacity changes
|
// 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, and to_paint is also false, we don't care
|
||||||
// If was_painted == false, but to_paint is true, damage will be added in
|
// If was_painted == false, but to_paint is true, damage will be added in
|
||||||
// the loop below
|
// the loop below
|
||||||
if (was_painted && w->opacity != opacity_old) {
|
if (was_painted && w->opacity != opacity_old) {
|
||||||
add_damage_from_win(ps, w);
|
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)) {
|
if (win_check_fade_finished(ps, w)) {
|
||||||
w->frame_opacity = ps->o.frame_opacity;
|
// the window has been destroyed because fading finished
|
||||||
} else {
|
continue;
|
||||||
w->frame_opacity = 1.0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update window mode
|
if (win_has_frame(w)) {
|
||||||
w->mode = win_calc_mode(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
|
// Update window mode
|
||||||
// SOLID mode
|
w->mode = win_calc_mode(w);
|
||||||
if (was_painted && w->mode != mode_old) {
|
|
||||||
w->reg_ignore_valid = false;
|
// 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.
|
// Opacity will not change, from now on.
|
||||||
rc_region_t *last_reg_ignore = rc_region_new();
|
rc_region_t *last_reg_ignore = rc_region_new();
|
||||||
|
|
||||||
|
@ -1722,6 +1898,11 @@ static void fade_timer_callback(EV_P attr_unused, ev_timer *w, int revents attr_
|
||||||
queue_redraw(ps);
|
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) {
|
static void handle_pending_updates(EV_P_ struct session *ps) {
|
||||||
if (ps->pending_updates) {
|
if (ps->pending_updates) {
|
||||||
log_debug("Delayed handling of events, entering critical section");
|
log_debug("Delayed handling of events, entering critical section");
|
||||||
|
@ -1827,10 +2008,10 @@ 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 is not redirected. its sole purpose should be to decide whether the
|
||||||
* screen should be redirected. */
|
* screen should be redirected. */
|
||||||
bool fade_running = false;
|
bool fade_running = false;
|
||||||
bool animation = false;
|
bool animation_running = false;
|
||||||
bool was_redirected = ps->redirected;
|
bool was_redirected = ps->redirected;
|
||||||
struct managed_win *bottom = NULL;
|
struct managed_win *bottom = NULL;
|
||||||
if (!paint_preprocess(ps, &fade_running, &animation, &bottom)) {
|
if (!paint_preprocess(ps, &fade_running, &animation_running, &bottom)) {
|
||||||
log_fatal("Pre-render preparation has failed, exiting...");
|
log_fatal("Pre-render preparation has failed, exiting...");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -1856,6 +2037,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_set(&ps->fade_timer, fade_timeout(ps), 0);
|
||||||
ev_timer_start(EV_A_ & ps->fade_timer);
|
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;
|
int64_t after_preprocess_us;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
@ -1896,15 +2084,21 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
||||||
if (!fade_running) {
|
if (!fade_running) {
|
||||||
ps->fade_time = 0L;
|
ps->fade_time = 0L;
|
||||||
}
|
}
|
||||||
|
if (!animation_running) {
|
||||||
|
ps->animation_time = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
ps->render_queued = false;
|
ps->render_queued = false;
|
||||||
|
if (!animation_running) {
|
||||||
|
ps->animation_time = 0L;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(yshui) Investigate how big the X critical section needs to be. There are
|
// 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.
|
// 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
|
// Queue redraw if animation is running. This should be picked up by next present
|
||||||
// event.
|
// event.
|
||||||
if (animation) {
|
if (animation_running) {
|
||||||
queue_redraw(ps);
|
queue_redraw(ps);
|
||||||
}
|
}
|
||||||
if (ps->vblank_scheduler) {
|
if (ps->vblank_scheduler) {
|
||||||
|
@ -2043,6 +2237,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||||
.redirected = false,
|
.redirected = false,
|
||||||
.alpha_picts = NULL,
|
.alpha_picts = NULL,
|
||||||
.fade_time = 0L,
|
.fade_time = 0L,
|
||||||
|
.animation_time = 0L,
|
||||||
.quit = false,
|
.quit = false,
|
||||||
|
|
||||||
.expose_rects = NULL,
|
.expose_rects = NULL,
|
||||||
|
@ -2454,7 +2649,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||||
|
|
||||||
// Monitor screen changes if vsync_sw is enabled and we are using
|
// Monitor screen changes if vsync_sw is enabled and we are using
|
||||||
// an auto-detected refresh rate, or when X RandR features are enabled
|
// 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_select_input(ps->c.c, ps->c.screen_info->root,
|
||||||
XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
|
XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
|
||||||
x_update_monitors(&ps->c, &ps->monitors);
|
x_update_monitors(&ps->c, &ps->monitors);
|
||||||
|
@ -2483,6 +2678,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||||
ev_init(&ps->draw_timer, draw_callback);
|
ev_init(&ps->draw_timer, draw_callback);
|
||||||
|
|
||||||
ev_init(&ps->fade_timer, fade_timer_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
|
// Set up SIGUSR1 signal handler to reset program
|
||||||
ev_signal_init(&ps->usr1_signal, reset_enable, SIGUSR1);
|
ev_signal_init(&ps->usr1_signal, reset_enable, SIGUSR1);
|
||||||
|
@ -2727,6 +2924,7 @@ static void session_destroy(session_t *ps) {
|
||||||
// Stop libev event handlers
|
// Stop libev event handlers
|
||||||
ev_timer_stop(ps->loop, &ps->unredir_timer);
|
ev_timer_stop(ps->loop, &ps->unredir_timer);
|
||||||
ev_timer_stop(ps->loop, &ps->fade_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_timer_stop(ps->loop, &ps->draw_timer);
|
||||||
ev_prepare_stop(ps->loop, &ps->event_check);
|
ev_prepare_stop(ps->loop, &ps->event_check);
|
||||||
ev_signal_stop(ps->loop, &ps->usr1_signal);
|
ev_signal_stop(ps->loop, &ps->usr1_signal);
|
||||||
|
|
14
src/utils.h
14
src/utils.h
|
@ -21,6 +21,7 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
|
#define CLEAR_MASK(x) x = 0;
|
||||||
|
|
||||||
#ifdef __FAST_MATH__
|
#ifdef __FAST_MATH__
|
||||||
#warning Use of -ffast-math can cause rendering error or artifacts, \
|
#warning Use of -ffast-math can cause rendering error or artifacts, \
|
||||||
|
@ -167,6 +168,19 @@ static inline int attr_const attr_unused normalize_i_range(int i, int min, int m
|
||||||
__auto_type __tmp = (val); \
|
__auto_type __tmp = (val); \
|
||||||
__tmp > 0 ? __tmp : -__tmp; \
|
__tmp > 0 ? __tmp : -__tmp; \
|
||||||
})
|
})
|
||||||
|
/**
|
||||||
|
* 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 min2(a, b) ((a) > (b) ? (b) : (a))
|
||||||
#define max2(a, b) ((a) > (b) ? (a) : (b))
|
#define max2(a, b) ((a) > (b) ? (a) : (b))
|
||||||
#define min3(a, b, c) min2(a, min2(b, c))
|
#define min3(a, b, c) min2(a, min2(b, c))
|
||||||
|
|
348
src/win.c
348
src/win.c
|
@ -324,6 +324,13 @@ static inline void win_release_pixmap(backend_t *base, struct managed_win *w) {
|
||||||
w->flags |= WIN_FLAGS_PIXMAP_NONE;
|
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) {
|
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);
|
log_debug("Releasing shadow of window %#010x (%s)", w->base.id, w->name);
|
||||||
if (w->shadow_image) {
|
if (w->shadow_image) {
|
||||||
|
@ -416,6 +423,7 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) {
|
||||||
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
||||||
assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
|
assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE));
|
||||||
win_release_pixmap(backend, w);
|
win_release_pixmap(backend, w);
|
||||||
|
win_release_oldpixmap(backend, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
win_release_shadow(backend, w);
|
win_release_shadow(backend, w);
|
||||||
|
@ -500,6 +508,173 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
|
||||||
win_clear_all_properties_stale(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
|
/// Handle non-image flags. This phase might set IMAGES_STALE flags
|
||||||
void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
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.
|
// Whether the window was visible before we process the mapped flag. i.e.
|
||||||
|
@ -545,11 +720,79 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||||
add_damage_from_win(ps, w);
|
add_damage_from_win(ps, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update window geometry
|
|
||||||
w->g = w->pending_g;
|
|
||||||
|
|
||||||
// Whether a window is fullscreen changes based on its geometry
|
|
||||||
win_update_is_fullscreen(ps, w);
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
|
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
|
||||||
win_on_win_size_change(w, ps->o.shadow_offset_x,
|
win_on_win_size_change(w, ps->o.shadow_offset_x,
|
||||||
|
@ -856,6 +1099,10 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
|
||||||
if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
|
if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if ((w->state == WSTATE_FADING && (w->animation_is_tag & ANIM_FADE))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Try obeying opacity property and window type opacity firstly
|
// Try obeying opacity property and window type opacity firstly
|
||||||
if (w->has_opacity_prop) {
|
if (w->has_opacity_prop) {
|
||||||
opacity = ((double)w->opacity_prop) / OPAQUE;
|
opacity = ((double)w->opacity_prop) / OPAQUE;
|
||||||
|
@ -920,6 +1167,24 @@ bool win_should_fade(session_t *ps, const struct managed_win *w) {
|
||||||
return ps->o.wintype_option[w->window_type].fade;
|
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.
|
* Reread _COMPTON_SHADOW property from a window.
|
||||||
*
|
*
|
||||||
|
@ -1285,6 +1550,9 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
|
||||||
w->transparent_clipping_excluded =
|
w->transparent_clipping_excluded =
|
||||||
c2_match(ps->c2_state, w, ps->o.transparent_clipping_blacklist, NULL);
|
c2_match(ps->c2_state, w, ps->o.transparent_clipping_blacklist, NULL);
|
||||||
|
|
||||||
|
w->transparent_clipping_excluded =
|
||||||
|
c2_match(ps, w, ps->o.transparent_clipping_blacklist, NULL);
|
||||||
|
|
||||||
win_update_opacity_target(ps, w);
|
win_update_opacity_target(ps, w);
|
||||||
|
|
||||||
w->reg_ignore_valid = false;
|
w->reg_ignore_valid = false;
|
||||||
|
@ -1558,14 +1826,19 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||||
.blur_background = false,
|
.blur_background = false,
|
||||||
.reg_ignore = NULL,
|
.reg_ignore = NULL,
|
||||||
// The following ones are updated for other reasons
|
// The following ones are updated for other reasons
|
||||||
.pixmap_damaged = false, // updated by damage events
|
.pixmap_damaged = false, // updated by damage events
|
||||||
.state = WSTATE_UNMAPPED, // updated by window state changes
|
.state = WSTATE_UNMAPPED, // updated by window state changes
|
||||||
.in_openclose = true, // set to false after first map is done,
|
.in_openclose = true, // set to false after first map is done,
|
||||||
// true here because window is just created
|
// true here because window is just created
|
||||||
.reg_ignore_valid = false, // set to true when damaged
|
.animation_velocity_x = 0.0, // updated by window geometry changes
|
||||||
.flags = WIN_FLAGS_PIXMAP_NONE, // updated by
|
.animation_velocity_y = 0.0, // updated by window geometry changes
|
||||||
// property/attributes/etc
|
.animation_velocity_w = 0.0, // updated by window geometry changes
|
||||||
// change
|
.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_PIXMAP_NONE, // updated by property/attributes/etc
|
||||||
|
// change
|
||||||
.stale_props = NULL,
|
.stale_props = NULL,
|
||||||
.stale_props_capacity = 0,
|
.stale_props_capacity = 0,
|
||||||
|
|
||||||
|
@ -1591,6 +1864,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||||
// have no meaning or have no use until the window
|
// have no meaning or have no use until the window
|
||||||
// is mapped
|
// is mapped
|
||||||
.win_image = NULL,
|
.win_image = NULL,
|
||||||
|
.old_win_image = NULL,
|
||||||
.shadow_image = NULL,
|
.shadow_image = NULL,
|
||||||
.mask_image = NULL,
|
.mask_image = NULL,
|
||||||
.prev_trans = NULL,
|
.prev_trans = NULL,
|
||||||
|
@ -2119,17 +2393,31 @@ static void unmap_win_finish(session_t *ps, struct managed_win *w) {
|
||||||
// Shadow image can be preserved.
|
// Shadow image can be preserved.
|
||||||
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) {
|
||||||
win_release_pixmap(ps->backend_data, w);
|
win_release_pixmap(ps->backend_data, w);
|
||||||
|
win_release_oldpixmap(ps->backend_data, w);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(!w->win_image);
|
assert(!w->win_image);
|
||||||
|
assert(!w->old_win_image);
|
||||||
assert(!w->shadow_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->paint);
|
||||||
free_paint(ps, &w->shadow_paint);
|
free_paint(ps, &w->shadow_paint);
|
||||||
|
|
||||||
// Try again at binding images when the window is mapped next time
|
// Try again at binding images when the window is mapped next time
|
||||||
win_clear_flags(w, WIN_FLAGS_IMAGE_ERROR);
|
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).
|
/// Finish the destruction of a window (e.g. after fading has finished).
|
||||||
|
@ -2401,6 +2689,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_old = fmax(w->opacity_target, w->opacity_target_old);
|
||||||
w->opacity_target = win_calc_opacity_target(ps, w);
|
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
|
#ifdef CONFIG_DBUS
|
||||||
// Send D-Bus signal
|
// Send D-Bus signal
|
||||||
if (ps->o.dbus) {
|
if (ps->o.dbus) {
|
||||||
|
@ -2446,7 +2758,7 @@ bool win_check_fade_finished(session_t *ps, struct managed_win *w) {
|
||||||
///
|
///
|
||||||
/// @return whether the window is destroyed and freed
|
/// @return whether the window is destroyed and freed
|
||||||
bool win_skip_fading(session_t *ps, struct managed_win *w) {
|
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);
|
assert(w->opacity_target == w->opacity);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2458,11 +2770,12 @@ bool win_skip_fading(session_t *ps, struct managed_win *w) {
|
||||||
// TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to
|
// TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to
|
||||||
// the x.c.
|
// the x.c.
|
||||||
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) {
|
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) {
|
||||||
mw->randr_monitor = -1;
|
|
||||||
for (int i = 0; i < monitors->count; i++) {
|
for (int i = 0; i < monitors->count; i++) {
|
||||||
auto e = pixman_region32_extents(&monitors->regions[i]);
|
auto e = pixman_region32_extents(&monitors->regions[i]);
|
||||||
if (e->x1 <= mw->g.x && e->y1 <= mw->g.y &&
|
if (((e->x1 <= mw->g.x || e->x1 <= mw->pending_g.x) &&
|
||||||
e->x2 >= mw->g.x + mw->widthb && e->y2 >= mw->g.y + mw->heightb) {
|
(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;
|
mw->randr_monitor = i;
|
||||||
log_debug("Window %#010x (%s), %dx%d+%dx%d, is entirely on the "
|
log_debug("Window %#010x (%s), %dx%d+%dx%d, is entirely on the "
|
||||||
"monitor %d (%dx%d+%dx%d)",
|
"monitor %d (%dx%d+%dx%d)",
|
||||||
|
@ -2471,6 +2784,7 @@ void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mw->randr_monitor = -1;
|
||||||
log_debug("Window %#010x (%s), %dx%d+%dx%d, is not entirely on any monitor",
|
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);
|
mw->base.id, mw->name, mw->g.x, mw->g.y, mw->widthb, mw->heightb);
|
||||||
}
|
}
|
||||||
|
@ -2837,5 +3151,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
|
/// Return whether this window is mapped on the X server side
|
||||||
bool win_is_mapped_in_x(const struct managed_win *w) {
|
bool win_is_mapped_in_x(const struct managed_win *w) {
|
||||||
return w->state == WSTATE_MAPPING || w->state == WSTATE_FADING ||
|
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);
|
||||||
}
|
}
|
||||||
|
|
36
src/win.h
36
src/win.h
|
@ -96,11 +96,25 @@ struct win_geometry {
|
||||||
uint16_t border_width;
|
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 managed_win {
|
||||||
struct win base;
|
struct win base;
|
||||||
/// backend data attached to this window. Only available when
|
/// backend data attached to this window. Only available when
|
||||||
/// `state` is not UNMAPPED
|
/// `state` is not UNMAPPED
|
||||||
image_handle win_image;
|
image_handle win_image;
|
||||||
|
image_handle old_win_image; // Old window image for interpolating window contents during animations
|
||||||
image_handle shadow_image;
|
image_handle shadow_image;
|
||||||
image_handle mask_image;
|
image_handle mask_image;
|
||||||
/// Pointer to the next higher window to paint.
|
/// Pointer to the next higher window to paint.
|
||||||
|
@ -152,6 +166,8 @@ struct managed_win {
|
||||||
/// opacity state, window geometry, window mapped/unmapped state,
|
/// opacity state, window geometry, window mapped/unmapped state,
|
||||||
/// window mode of the windows above. DOES NOT INCLUDE the body of THIS WINDOW.
|
/// 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.
|
/// 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;
|
rc_region_t *reg_ignore;
|
||||||
/// Whether the reg_ignore of all windows beneath this window are valid
|
/// Whether the reg_ignore of all windows beneath this window are valid
|
||||||
bool reg_ignore_valid;
|
bool reg_ignore_valid;
|
||||||
|
@ -169,6 +185,24 @@ struct managed_win {
|
||||||
bool unredir_if_possible_excluded;
|
bool unredir_if_possible_excluded;
|
||||||
/// Whether this window is in open/close state.
|
/// Whether this window is in open/close state.
|
||||||
bool in_openclose;
|
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
|
// Client window related members
|
||||||
/// ID of the top-level client window of the window.
|
/// ID of the top-level client window of the window.
|
||||||
|
@ -479,6 +513,8 @@ void win_update_bounding_shape(struct x_connection *c, struct managed_win *w,
|
||||||
bool shape_exists, bool detect_rounded_corners);
|
bool shape_exists, bool detect_rounded_corners);
|
||||||
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
|
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
|
||||||
struct managed_win *w);
|
struct managed_win *w);
|
||||||
|
/// 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) {
|
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);
|
return win_set_properties_stale(w, (xcb_atom_t[]){prop}, 1);
|
||||||
|
|
Loading…
Reference in New Issue