Added New Backend Dual Kawase Blur + Rounded Corners - merged with Ibhagwan

This commit is contained in:
jon 2020-07-14 18:04:45 +02:00
parent d6bc68146b
commit 0375dad5d1
54 changed files with 4696 additions and 1620 deletions

View File

@ -1,13 +0,0 @@
name: coding-style
on: pull_request
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }}
- uses: yshui/git-clang-format-lint@v1.10
with:
base: ${{ github.event.pull_request.base.sha }}

View File

@ -1,14 +0,0 @@
name: coding-style
on: push
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 2
- uses: yshui/git-clang-format-lint@v1.10
with:
base: ${{ github.event.ref }}~1

2
.gitignore vendored
View File

@ -52,5 +52,3 @@ doxygen/
/src/backtrace-symbols.[ch] /src/backtrace-symbols.[ch]
/compton*.trace /compton*.trace
*.orig *.orig
/tests/log
/tests/testcases/__pycache__/

22
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,22 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"/usr/include",
"/usr/include/pixman-1",
"${workspaceFolder}/**"
],
"defines": [
"CONFIG_OPENGL",
"_POSIX_C_SOURCE 199309L"
],
"compilerPath": "/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64",
"compileCommands": "${workspaceFolder}/build/compile_commands.json"
}
],
"version": 4
}

26
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
"configurations": [
{
"targetArchitecture": "x64",
"name": "Debug with Meson",
"type": "cppdbg",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/build/src/picom",
"preLaunchTask": "build debug meson",
"stopAtEntry": false,
"launchCompleteCommand": "exec-run",
"linux": {
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb"
},
"osx": {
"MIMode": "lldb"
},
"windows": {
"MIMode": "gdb",
"miDebuggerPath": "C:\\MinGw\\bin\\gdb.exe"
}
}
]
}

25
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,25 @@
{
"tasks": [
{
"label": "build debug meson",
"type": "shell",
"command": "meson build --buildtype=debug && ninja -C build",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "build release meson",
"type": "shell",
"command": "meson build --buildtype=release && ninja -C build",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
],
"version": "2.0.0"
}

View File

@ -4,10 +4,7 @@ Open an issue or pull request if you don't want your name listed here.
Adam Jackson <ajax@nwnk.net> Adam Jackson <ajax@nwnk.net>
Alexander Kapshuna <kapsh@kap.sh> Alexander Kapshuna <kapsh@kap.sh>
Antonin Décimo <antonin.decimo@gmail.com> Antonin Décimo <antonin.decimo@gmail.com>
Antonio Vivace <dev@avivace.com>
Avi-D-coder <avi.the.coder@gmail.com> Avi-D-coder <avi.the.coder@gmail.com>
Ben Friesen <bfriesen95@gmail.com>
Bernd Busse <bernd@busse-net.de>
Brottweiler <tibell.christoffer@gmail.com> Brottweiler <tibell.christoffer@gmail.com>
Carl Worth <cworth@cworth.org> Carl Worth <cworth@cworth.org>
Christopher Jeffrey <chjjeffrey@gmail.com> Christopher Jeffrey <chjjeffrey@gmail.com>
@ -15,46 +12,36 @@ Corax26 <cor.ax26@gmail.com>
Dan Elkouby <streetwalkermc@gmail.com> Dan Elkouby <streetwalkermc@gmail.com>
Dana Jansens <danakj@orodu.net> Dana Jansens <danakj@orodu.net>
Dave Airlie <airlied@linux.ie> Dave Airlie <airlied@linux.ie>
David Schlachter
dolio dolio
Duncan <duncan.britton@outlook.com> Duncan <duncan.britton@outlook.com>
Dylan Araps <dylan.araps@gmail.com>
Einar Lielmanis <einars@gmail.com> Einar Lielmanis <einars@gmail.com>
Eric Anholt <anholt@freebsd.org> <eric@anholt.net> Eric Anholt <anholt@freebsd.org> <eric@anholt.net>
Greg Flynn Greg Flynn
hasufell <julian.ospald@googlemail.com> hasufell <julian.ospald@googlemail.com>
James Cloos <cloos@jhcloos.com> James Cloos <cloos@jhcloos.com>
Jamey Sharp <jamey@minilop.net> Jamey Sharp <jamey@minilop.net>
Jan Beich <jbeich@FreeBSD.org>
Jarrad <jarrad.whitaker@gmail.com> Jarrad <jarrad.whitaker@gmail.com>
Javeed Shaikh <syscrash2k@gmail.com> Javeed Shaikh <syscrash2k@gmail.com>
Jerónimo Navarro <jnavarro@ancasrl.com.ar>
Keith Packard <keithp@keithp.com> Keith Packard <keithp@keithp.com>
Kevin Kelley <kelleyk@kelleyk.net> Kevin Kelley <kelleyk@kelleyk.net>
mæp <m.aep@live.com> mæp <m.aep@live.com>
Mark Tiefenbruck <mark@fluxbox.org> Mark Tiefenbruck <mark@fluxbox.org>
Matthew Allum <breakfast@10.am> Matthew Allum <breakfast@10.am>
Maxim Solovyov <visleaf@protonmail.com>
Michael Reed <supertron421@gmail.com> Michael Reed <supertron421@gmail.com>
Michele Lambertucci <michele.lambertucci@mail.polimi.it>
Namkhai Bourquin <namkhai.n3@protonmail.com> Namkhai Bourquin <namkhai.n3@protonmail.com>
Nate Hart <nejthan@gmail.com> Nate Hart <nejthan@gmail.com>
notfoss <static.vortex@gmx.com> notfoss <static.vortex@gmx.com>
@Paradigm0001
Patrick Collins <Patricol@users.noreply.github.com> Patrick Collins <Patricol@users.noreply.github.com>
Peter Mattern <matternp@arcor.de> Peter Mattern <matternp@arcor.de>
Phil Blundell <pb@reciva.com> Phil Blundell <pb@reciva.com>
Que Quotion <quequotion@bugmenot.com> Que Quotion <quequotion@bugmenot.com>
Richard Grenville <pyxlcy@gmail.com> Richard Grenville <pyxlcy@gmail.com>
Rytis Karpuska <rytis.karpuska@gmail.com>
Scott Leggett <scott@sl.id.au> Scott Leggett <scott@sl.id.au>
Tasos Sahanidis <tasos@tasossah.com> Tasos Sahanidis <tasos@tasossah.com>
The Gitter Badger <badger@gitter.im>
Tilman Sauerbeck <tilman@code-monkey.de> Tilman Sauerbeck <tilman@code-monkey.de>
Tim van Dalen <Tim@timvdalen.nl> Tim van Dalen <Tim@timvdalen.nl>
Tomas Janousek <tomi@nomi.cz>
Uli Schlachter <psychon@znc.in> Uli Schlachter <psychon@znc.in>
Walter Lapchynski <wxl@ubuntu.com>
Will Dietz <w@wdtz.org> Will Dietz <w@wdtz.org>
XeCycle <XeCycle@Gmail.com> XeCycle <XeCycle@Gmail.com>
Yuxuan Shui <yshuiv7@gmail.com> Yuxuan Shui <yshuiv7@gmail.com>
ಠ_ಠ <easteregg@verfriemelt.org>

View File

@ -1,24 +1,21 @@
picom picom
======= =======
This is a forked version from Sandmark's picom branch, including Blackcapcoder's animation code inside. The animations here are further smoothed and time deltas reduced from 1ms to 40us for high refresh rates and buttery smooth transitions. ## Why another picom fork?
You'll need to run it with the experimental backend with: TL;DR: rounded corners and dual_kawase blur on all backends.
`picom --experimental-backend` This fork contains:
- Dual kawase blur method from [tryone144](https://github.com/tryone144/compton) as well as his new [feature/dual_kawase branch](https://github.com/tryone144/compton/tree/feature/dual_kawase) which implements the dual kawase blur method on the experimental glx backend.
- Rounded corners code from [sdhand](https://github.com/sdhand/picom) which is also ported to the experimetnal XRender backend.
- New code for rounded corners on the glx backend using GLSL frangment shader
For more information read [my reddit post](https://www.reddit.com/r/unixporn/comments/fs8trg/oc_comptonpicom_fork_with_both_tryone144s_dual/)
Your picom config can also now take advantage of some of the options that were previously implemented in blackcapcoder's compton version:
- [x] * `transition-length` length of animation in milliseconds (default: 300)
- [x] * `transition-pow-x` animation easing on the x-axis (default: 0.1)
- [x] * `transition-pow-y` animation easing on the y-axis (default: 0.1)
- [x] * `transition-pow-w` animation easing on the window width (default: 0.1)
- [x] * `transition-pow-h` animation easing on the window height (default: 0.1)
- [x] * `size-transition` whether to animate window size changes (default: true)
- [ ] * `spawn-center-screen` whether to animate new windows from the center of the screen (default: false)
- [ ] * `spawn-center` whether to animate new windows from their own center (default: true)
- [ ] * `no-scale-down` Whether to animate down scaling (some programs handle this poorly) (default: false)
----
**This is a development branch, bugs to be expected** **This is a development branch, bugs to be expected**
This is forked from the original Compton because it seems to have become unmaintained. This is forked from the original Compton because it seems to have become unmaintained.
@ -98,7 +95,6 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth
* xcb-image * xcb-image
* xcb-present * xcb-present
* xcb-xinerama * xcb-xinerama
* xcb-glx
* pixman * pixman
* libdbus (optional, disable with the `-Ddbus=false` meson configure flag) * libdbus (optional, disable with the `-Ddbus=false` meson configure flag)
* libconfig (optional, disable with the `-Dconfig_file=false` meson configure flag) * libconfig (optional, disable with the `-Dconfig_file=false` meson configure flag)
@ -110,7 +106,7 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth
On Debian based distributions (e.g. Ubuntu), the list of needed packages are On Debian based distributions (e.g. Ubuntu), the list of needed packages are
``` ```
libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl1-mesa-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-xinerama0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl1-mesa-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev
``` ```
To build the documents, you need `asciidoc` To build the documents, you need `asciidoc`

View File

@ -1,41 +1,38 @@
#!/bin/sh #!/bin/sh
# == Declare stderr function === # === Verify `compton --dbus` status ===
stderr() { if [ -z "`dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep compton`" ]; then
printf "\033[1;31m%s\n\033[0m" "$@" >&2 echo "compton DBus interface unavailable"
} if [ -n "`pgrep picom`" ]; then
echo "compton running without dbus interface"
# === Verify `picom --dbus` status === #killall compton & # Causes all windows to flicker away and come back ugly.
#compton --dbus & # Causes all windows to flicker away and come back beautiful
if [ -z "$(dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep compton)" ]; then
stderr "picom DBus interface unavailable"
if [ -n "$(pgrep picom)" ]; then
stderr "picom running without dbus interface"
#killall picom & # Causes all windows to flicker away and come back ugly.
#picom --dbus & # Causes all windows to flicker away and come back beautiful
else else
stderr "picom not running" echo "compton not running"
fi fi
exit 1 exit 1;
fi fi
# === Setup sed === # === Setup sed ===
SED="${SED:-$(command -v gsed || printf 'sed')}" if [ -z "$SED" ]; then
SED="sed"
command -v gsed > /dev/null && SED="gsed"
fi
# === Get connection parameters === # === Get connection parameters ===
dpy=$(printf "$DISPLAY" | tr -c '[:alnum:]' _) dpy=$(echo -n "$DISPLAY" | tr -c '[:alnum:]' _)
if [ -z "$dpy" ]; then if [ -z "$dpy" ]; then
stderr "Cannot find display." echo "Cannot find display."
exit 1 exit 1;
fi fi
service="com.github.chjj.compton.${dpy}" service="com.github.chjj.compton.${dpy}"
interface="com.github.chjj.compton" interface="com.github.chjj.compton"
picom_dbus="dbus-send --print-reply --dest="${service}" / "${interface}"." compton_dbus="dbus-send --print-reply --dest="${service}" / "${interface}"."
type_win='uint32' type_win='uint32'
type_enum='uint32' type_enum='uint32'
@ -46,7 +43,7 @@ if [ -z "$1" -o "$1" = "selected" ]; then
window=$(xwininfo -frame | sed -n 's/^xwininfo: Window id: \(0x[[:xdigit:]][[:xdigit:]]*\).*/\1/p') # Select window by mouse window=$(xwininfo -frame | sed -n 's/^xwininfo: Window id: \(0x[[:xdigit:]][[:xdigit:]]*\).*/\1/p') # Select window by mouse
elif [ "$1" = "focused" ]; then elif [ "$1" = "focused" ]; then
# Ensure we are tracking focus # Ensure we are tracking focus
window=$(${picom_dbus}find_win string:focused | $SED -n 's/^[[:space:]]*'${type_win}'[[:space:]]*\([[:digit:]]*\).*/\1/p') # Query picom for the active window window=$(${compton_dbus}find_win string:focused | $SED -n 's/^[[:space:]]*'${type_win}'[[:space:]]*\([[:digit:]]*\).*/\1/p') # Query compton for the active window
elif echo "$1" | grep -Eiq '^([[:digit:]][[:digit:]]*|0x[[:xdigit:]][[:xdigit:]]*)$'; then elif echo "$1" | grep -Eiq '^([[:digit:]][[:digit:]]*|0x[[:xdigit:]][[:xdigit:]]*)$'; then
window="$1" # Accept user-specified window-id if the format is correct window="$1" # Accept user-specified window-id if the format is correct
else else
@ -55,15 +52,15 @@ fi
# Color invert the selected or focused window # Color invert the selected or focused window
if [ -n "$window" ]; then if [ -n "$window" ]; then
invert_status="$(${picom_dbus}win_get "${type_win}:${window}" string:invert_color | $SED -n 's/^[[:space:]]*boolean[[:space:]]*\([[:alpha:]]*\).*/\1/p')" invert_status="$(${compton_dbus}win_get "${type_win}:${window}" string:invert_color | $SED -n 's/^[[:space:]]*boolean[[:space:]]*\([[:alpha:]]*\).*/\1/p')"
if [ "$invert_status" = true ]; then if [ "$invert_status" = true ]; then
invert=0 # Set the window to have normal color invert=0 # Set the window to have normal color
else else
invert=1 # Set the window to have inverted color invert=1 # Set the window to have inverted color
fi fi
${picom_dbus}win_set "${type_win}:${window}" string:invert_color_force "${type_enum}:${invert}" & ${compton_dbus}win_set "${type_win}:${window}" string:invert_color_force "${type_enum}:${invert}" &
else else
stderr "Cannot find $1 window." echo "Cannot find $1 window."
exit 1 exit 1;
fi fi
exit 0 exit 0;

View File

@ -7,7 +7,6 @@ if get_option('with_docs')
'picom-version='+version, 'picom-version='+version,
'--format', 'manpage', '@INPUT@', '-D', '--format', 'manpage', '@INPUT@', '-D',
meson.current_build_dir()], meson.current_build_dir()],
install: true, install: true, install_dir: 'share/man/man1/')
install_dir: join_paths(get_option('mandir'), 'man1'))
endforeach endforeach
endif endif

View File

@ -106,6 +106,18 @@ OPTIONS
*--inactive-dim* 'VALUE':: *--inactive-dim* 'VALUE'::
Dim inactive windows. (0.0 - 1.0, defaults to 0.0) Dim inactive windows. (0.0 - 1.0, defaults to 0.0)
*--corner-radius* 'VALUE'::
Round the corners of windows. (defaults to 0).
*--rounded-corners-exclude* 'CONDITION'::
Exclude conditions for rounded corners.
*--round-borders* 'VALUE'::
When rounding corners, Round the borders of windows. (defaults to 1).
*--round-borders-exclude* 'CONDITION'::
Exclude conditions for rounding borders.
*--mark-wmwin-focused*:: *--mark-wmwin-focused*::
Try to detect WM windows (a non-override-redirect window with no child that has 'WM_STATE') and mark them as active. Try to detect WM windows (a non-override-redirect window with no child that has 'WM_STATE') and mark them as active.
@ -140,7 +152,7 @@ OPTIONS
Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window, rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy, provided that the WM supports it. Use EWMH '_NET_ACTIVE_WINDOW' to determine currently focused window, rather than listening to 'FocusIn'/'FocusOut' event. Might have more accuracy, provided that the WM supports it.
*--unredir-if-possible*:: *--unredir-if-possible*::
Unredirect all windows if a full-screen opaque window is detected, to maximize performance for full-screen windows. Known to cause flickering when redirecting/unredirecting windows. Unredirect all windows if a full-screen opaque window is detected, to maximize performance for full-screen windows. Known to cause flickering when redirecting/unredirecting windows. *--paint-on-overlay* may make the flickering less obvious.
*--unredir-if-possible-delay* 'MILLISECONDS':: *--unredir-if-possible-delay* 'MILLISECONDS'::
Delay before unredirecting the window, in milliseconds. Defaults to 0. Delay before unredirecting the window, in milliseconds. Defaults to 0.
@ -166,7 +178,7 @@ OPTIONS
*--detect-client-leader*:: *--detect-client-leader*::
Use 'WM_CLIENT_LEADER' to group windows, and consider windows in the same group focused at the same time. 'WM_TRANSIENT_FOR' has higher priority if *--detect-transient* is enabled, too. Use 'WM_CLIENT_LEADER' to group windows, and consider windows in the same group focused at the same time. 'WM_TRANSIENT_FOR' has higher priority if *--detect-transient* is enabled, too.
*--blur-method*, *--blur-size*, *--blur-deviation*:: *--blur-method*, *--blur-size*, *--blur-deviation, *--blur-strength*::
Parameters for background blurring, see the *BLUR* section for more information. Parameters for background blurring, see the *BLUR* section for more information.
*--blur-background*:: *--blur-background*::
@ -407,6 +419,9 @@ Available options of the 'blur' section are: ::
*deviation*::: *deviation*:::
A floating point number. The standard deviation for the 'gaussian' blur method. Corresponds to the *--blur-deviation* command line option (default: 0.84089642). A floating point number. The standard deviation for the 'gaussian' blur method. Corresponds to the *--blur-deviation* command line option (default: 0.84089642).
*strength*:::
An integer in the range 1-20. The strength of the 'dual_kawase' blur method. Corresponds to the *--blur-strength* command line option. If not specified, the value requested by *--blur-size* is approximated.
*kernel*::: *kernel*:::
A string. The kernel to use for the 'kernel' blur method, specified in the same format as the *--blur-kerns* option. Corresponds to the *--blur-kerns* command line option. A string. The kernel to use for the 'kernel' blur method, specified in the same format as the *--blur-kerns* option. Corresponds to the *--blur-kerns* command line option.

View File

@ -1,4 +1,4 @@
project('picom', 'c', version: '8', project('picom', 'c', version: '7',
default_options: ['c_std=c11']) default_options: ['c_std=c11'])
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
@ -70,14 +70,11 @@ subdir('src')
subdir('man') subdir('man')
install_data('bin/picom-trans', install_dir: get_option('bindir')) install_data('bin/picom-trans', install_dir: get_option('bindir'))
install_data('compton.desktop', install_dir: 'share/applications')
install_data('picom.desktop', install_dir: 'share/applications') install_data('picom.desktop', install_dir: 'share/applications')
install_data('media/icons/48x48/compton.png',
install_dir: 'share/icons/hicolor/48x48/apps')
install_data('media/compton.svg',
install_dir: 'share/icons/hicolor/scalable/apps')
if get_option('compton') meson.add_install_script('meson/install.sh')
install_data('compton.desktop', install_dir: 'share/applications')
install_data('media/icons/48x48/compton.png',
install_dir: 'share/icons/hicolor/48x48/apps')
install_data('media/compton.svg',
install_dir: 'share/icons/hicolor/scalable/apps')
meson.add_install_script('meson/install.sh')
endif

View File

@ -9,8 +9,6 @@ option('dbus', type: 'boolean', value: true, description: 'Enable support for D-
option('xrescheck', type: 'boolean', value: false, description: 'Enable X resource leak checker (for debug only)') option('xrescheck', type: 'boolean', value: false, description: 'Enable X resource leak checker (for debug only)')
option('compton', type: 'boolean', value: true, description: 'Install backwards compat with compton')
option('with_docs', type: 'boolean', value: false, description: 'Build documentation and man pages') option('with_docs', type: 'boolean', value: false, description: 'Build documentation and man pages')
option('modularize', type: 'boolean', value: false, description: 'Build with clang\'s module system') option('modularize', type: 'boolean', value: false, description: 'Build with clang\'s module system')

View File

@ -1,3 +1,26 @@
#################################
# Corners #
#################################
# requires: https://github.com/sdhand/compton
corner-radius = 25.0;
rounded-corners-exclude = [
#"window_type = 'normal'",
"class_g = 'awesome'",
"class_g = 'URxvt'",
"class_g = 'XTerm'",
"class_g = 'kitty'",
"class_g = 'Alacritty'",
"class_g = 'Polybar'",
"class_g = 'code-oss'",
#"class_g = 'TelegramDesktop'",
"class_g = 'firefox'",
"class_g = 'Thunderbird'"
];
round-borders = 1;
round-borders-exclude = [
#"class_g = 'TelegramDesktop'",
];
################################# #################################
# Shadows # # Shadows #
################################# #################################
@ -8,7 +31,7 @@
# 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
@ -66,6 +89,8 @@ shadow-exclude = [
"class_g = 'Conky'", "class_g = 'Conky'",
"class_g ?= 'Notify-osd'", "class_g ?= 'Notify-osd'",
"class_g = 'Cairo-clock'", "class_g = 'Cairo-clock'",
"class_g = 'slop'",
"class_g = 'Polybar'",
"_GTK_FRAME_EXTENTS@:c" "_GTK_FRAME_EXTENTS@:c"
]; ];
@ -88,7 +113,7 @@ 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 = 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
@ -102,7 +127,10 @@ fade-out-step = 0.03;
# 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 = [] # don't need this, we disable fading for all normal windows with wintypes: {}
fade-exclude = [
"class_g = 'slop'" # maim
]
# Do not fade on window open/close. # Do not fade on window open/close.
# no-fading-openclose = false # no-fading-openclose = false
@ -132,14 +160,18 @@ frame-opacity = 0.7;
inactive-opacity-override = false; inactive-opacity-override = false;
# 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;
# Dim inactive windows. (0.0 - 1.0, defaults to 0.0) # Dim inactive windows. (0.0 - 1.0, defaults to 0.0)
# inactive-dim = 0.0 # inactive-dim = 0.0
# Specify a list of conditions of windows that should always be considered focused. # Specify a list of conditions of windows that should always be considered focused.
# focus-exclude = [] # focus-exclude = []
focus-exclude = [ "class_g = 'Cairo-clock'" ]; focus-exclude = [
"class_g = 'Cairo-clock'",
"class_g = 'Bar'", # lemonbar
"class_g = 'slop'" # maim
];
# 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
@ -152,6 +184,21 @@ focus-exclude = [ "class_g = 'Cairo-clock'" ];
# opacity-rule = [ "80:class_g = 'URxvt'" ]; # opacity-rule = [ "80:class_g = 'URxvt'" ];
# #
# opacity-rule = [] # opacity-rule = []
opacity-rule = [
"80:class_g = 'Bar'", # lemonbar
"100:class_g = 'slop'", # maim
"100:class_g = 'XTerm'",
"100:class_g = 'URxvt'",
"100:class_g = 'kitty'",
"100:class_g = 'Alacritty'",
"80:class_g = 'Polybar'",
"100:class_g = 'code-oss'",
"100:class_g = 'Meld'",
"70:class_g = 'TelegramDesktop'",
"90:class_g = 'Joplin'",
"100:class_g = 'firefox'",
"100:class_g = 'Thunderbird'"
];
################################# #################################
@ -169,18 +216,18 @@ focus-exclude = [ "class_g = 'Cairo-clock'" ];
# Bad in performance, with driver-dependent behavior. # Bad in performance, with driver-dependent behavior.
# The name of the switch may change without prior notifications. # The name of the switch may change without prior notifications.
# #
# blur-background = false # blur-background = true;
# Blur background of windows when the window frame is not opaque. # Blur background of windows when the window frame is not opaque.
# Implies: # Implies:
# blur-background # blur-background
# Bad in performance, with driver-dependent behavior. The name may change. # Bad in performance, with driver-dependent behavior. The name may change.
# #
# blur-background-frame = false # blur-background-frame = false;
# Use fixed blur strength rather than adjusting according to window opacity. # Use fixed blur strength rather than adjusting according to window opacity.
# blur-background-fixed = false # blur-background-fixed = false;
# Specify the blur convolution kernel, with the following format: # Specify the blur convolution kernel, with the following format:
@ -188,17 +235,35 @@ focus-exclude = [ "class_g = 'Cairo-clock'" ];
# 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 = "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 = ''
blur-kern = "3x3box"; # blur-kern = "3x3box";
blur: {
# requires: https://github.com/ibhagwan/picom
method = "kawase";
#method = "kernel";
strength = 7;
# deviation = 1.0;
# kernel = "11x11gaussian";
background = false;
background-frame = false;
background-fixed = false;
kern = "3x3box";
}
# Exclude conditions for background blur. # Exclude conditions for background blur.
# blur-background-exclude = []
blur-background-exclude = [ blur-background-exclude = [
"window_type = 'dock'", #"window_type = 'dock'",
"window_type = 'desktop'", #"window_type = 'desktop'",
#"class_g = 'URxvt'",
#
# prevents picom from blurring the background
# when taking selection screenshot with `main`
# https://github.com/naelstrof/maim/issues/130
"class_g = 'slop'",
"_GTK_FRAME_EXTENTS@:c" "_GTK_FRAME_EXTENTS@:c"
]; ];
################################# #################################
# General Settings # # General Settings #
################################# #################################
@ -209,8 +274,10 @@ blur-background-exclude = [
# Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`. # Specify the backend to use: `xrender`, `glx`, or `xr_glx_hybrid`.
# `xrender` is the default one. # `xrender` is the default one.
# #
# backend = 'glx' experimental-backends = true;
backend = "xrender"; backend = "glx";
#backend = "xrender";
# Enable/disable VSync. # Enable/disable VSync.
# vsync = false # vsync = false
@ -263,7 +330,7 @@ refresh-rate = 0
# 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. paint-on-overlay may make the flickering less obvious.
# #
# unredir-if-possible = false # unredir-if-possible = false
@ -367,7 +434,7 @@ use-damage = true
# using *--log-file*, since it can generate a huge stream of logs. # using *--log-file*, since it can generate a huge stream of logs.
# #
# log-level = "debug" # log-level = "debug"
log-level = "warn"; log-level = "info";
# Set the log file. # Set the log file.
# If *--log-file* is never specified, logs will be written to stderr. # If *--log-file* is never specified, logs will be written to stderr.
@ -415,6 +482,7 @@ log-level = "warn";
# #
wintypes: wintypes:
{ {
normal = { fade = false; shadow = false; }
tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; }; tooltip = { fade = true; shadow = true; opacity = 0.75; focus = true; full-shadow = false; };
dock = { shadow = false; } dock = { shadow = false; }
dnd = { shadow = false; } dnd = { shadow = false; }

View File

@ -55,7 +55,7 @@ region_t get_damage(session_t *ps, bool all_damage) {
/// paint all windows /// paint all windows
void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) { void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
if (ps->o.xrender_sync_fence) { if (ps->o.xrender_sync_fence || (ps->drivers & DRIVER_NVIDIA)) {
if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) { if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) {
log_error("x_fence_sync failed, xrender-sync-fence will be " log_error("x_fence_sync failed, xrender-sync-fence will be "
"disabled from now on."); "disabled from now on.");
@ -151,11 +151,8 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
} }
if (ps->root_image) { if (ps->root_image) {
ps->backend_data->ops->compose(ps->backend_data, ps->root_image, 0, 0, ps->backend_data->ops->compose(ps->backend_data, t, ps->root_image, 0, 0,
&reg_paint, &reg_visible); &reg_paint, &reg_visible);
} else {
ps->backend_data->ops->fill(ps->backend_data, (struct color){0, 0, 0, 1},
&reg_paint);
} }
// Windows are sorted from bottom to top // Windows are sorted from bottom to top
@ -171,7 +168,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
// The bounding shape of the window, in global/target coordinates // The bounding shape of the window, in global/target coordinates
// reminder: bounding shape contains the WM frame // reminder: bounding shape contains the WM frame
auto reg_bound = win_get_bounding_shape_global_by_val(w); auto reg_bound = win_get_bounding_shape_global_by_val(w, true);
// The clip region for the current window, in global/target coordinates // The clip region for the current window, in global/target coordinates
// reg_paint_in_bound \in reg_paint // reg_paint_in_bound \in reg_paint
@ -190,6 +187,17 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
&reg_paint_in_bound, &reg_visible); &reg_paint_in_bound, &reg_visible);
} }
// Store the window background for rounded corners
// If rounded corners backup the region first
if (w->corner_radius > 0) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
ps->backend_data->ops->store_back_texture(ps->backend_data, w,
ps->backend_round_context, &reg_bound, x, y, wid, hei);
}
// Blur window background // Blur window background
// TODO since the background might change the content of the window (e.g. // TODO since the background might change the content of the window (e.g.
// with shaders), we should consult the background whether the window // with shaders), we should consult the background whether the window
@ -204,36 +212,20 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
// itself is not opaque, only the frame is. // itself is not opaque, only the frame is.
double blur_opacity = 1; double blur_opacity = 1;
if (w->opacity < (1.0 / MAX_ALPHA)) { if (w->state == WSTATE_MAPPING) {
// Hide blur for fully transparent windows.
blur_opacity = 0;
} else if (w->state == WSTATE_MAPPING) {
// Gradually increase the blur intensity during // Gradually increase the blur intensity during
// fading in. // fading in.
assert(w->opacity <= w->opacity_target);
blur_opacity = w->opacity / w->opacity_target; blur_opacity = w->opacity / w->opacity_target;
} else if (w->state == WSTATE_UNMAPPING || } else if (w->state == WSTATE_UNMAPPING ||
w->state == WSTATE_DESTROYING) { w->state == WSTATE_DESTROYING) {
// Gradually decrease the blur intensity during // Gradually decrease the blur intensity during
// fading out. // fading out.
assert(w->opacity <= w->opacity_target_old); blur_opacity =
blur_opacity = w->opacity / w->opacity_target_old; w->opacity / win_calc_opacity_target(ps, w, true);
} else if (w->state == WSTATE_FADING) { } else if (!ps->o.blur_background_fixed) {
if (w->opacity < w->opacity_target && // Apply blur intensity depending on the window opacity.
w->opacity_target_old < (1.0 / MAX_ALPHA)) { blur_opacity = w->opacity;
// Gradually increase the blur intensity during
// fading in.
assert(w->opacity <= w->opacity_target);
blur_opacity = w->opacity / w->opacity_target;
} else if (w->opacity > w->opacity_target &&
w->opacity_target < (1.0 / MAX_ALPHA)) {
// Gradually decrease the blur intensity during
// fading out.
assert(w->opacity <= w->opacity_target_old);
blur_opacity = w->opacity / w->opacity_target_old;
}
} }
assert(blur_opacity >= 0 && blur_opacity <= 1);
if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) { if (real_win_mode == WMODE_TRANS || ps->o.force_win_blend) {
// We need to blur the bounding shape of the window // We need to blur the bounding shape of the window
@ -249,7 +241,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
assert(ps->o.blur_background_frame); assert(ps->o.blur_background_frame);
assert(real_win_mode == WMODE_FRAME_TRANS); assert(real_win_mode == WMODE_FRAME_TRANS);
auto reg_blur = win_get_region_frame_local_by_val(w); auto reg_blur = win_get_region_frame_local_by_val(w, true);
pixman_region32_translate(&reg_blur, w->g.x, w->g.y); pixman_region32_translate(&reg_blur, w->g.x, w->g.y);
// make sure reg_blur \in reg_paint // make sure reg_blur \in reg_paint
pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint); pixman_region32_intersect(&reg_blur, &reg_blur, &reg_paint);
@ -305,7 +297,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
assert(w->shadow_image); assert(w->shadow_image);
if (w->opacity == 1) { if (w->opacity == 1) {
ps->backend_data->ops->compose( ps->backend_data->ops->compose(
ps->backend_data, w->shadow_image, w->g.x + w->shadow_dx, ps->backend_data, w, w->shadow_image, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible); w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
} else { } else {
auto new_img = ps->backend_data->ops->copy( auto new_img = ps->backend_data->ops->copy(
@ -314,7 +306,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img, ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible, (double[]){w->opacity}); NULL, &reg_visible, (double[]){w->opacity});
ps->backend_data->ops->compose( ps->backend_data->ops->compose(
ps->backend_data, new_img, w->g.x + w->shadow_dx, ps->backend_data, w, new_img, w->g.x + w->shadow_dx,
w->g.y + w->shadow_dy, &reg_shadow, &reg_visible); w->g.y + w->shadow_dy, &reg_shadow, &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img); ps->backend_data->ops->release_image(ps->backend_data, new_img);
} }
@ -330,7 +322,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Draw window on target // Draw window on target
if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) { if (!w->invert_color && !w->dim && w->frame_opacity == 1 && w->opacity == 1) {
ps->backend_data->ops->compose(ps->backend_data, w->win_image, ps->backend_data->ops->compose(ps->backend_data, w, w->win_image,
w->g.x, w->g.y, w->g.x, w->g.y,
&reg_paint_in_bound, &reg_visible); &reg_paint_in_bound, &reg_visible);
} else if (w->opacity * MAX_ALPHA >= 1) { } else if (w->opacity * MAX_ALPHA >= 1) {
@ -379,7 +371,7 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
&reg_visible_local, (double[]){dim_opacity}); &reg_visible_local, (double[]){dim_opacity});
} }
if (w->frame_opacity != 1) { if (w->frame_opacity != 1) {
auto reg_frame = win_get_region_frame_local_by_val(w); auto reg_frame = win_get_region_frame_local_by_val(w, true);
ps->backend_data->ops->image_op( ps->backend_data->ops->image_op(
ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame, ps->backend_data, IMAGE_OP_APPLY_ALPHA, new_img, &reg_frame,
&reg_visible_local, (double[]){w->frame_opacity}); &reg_visible_local, (double[]){w->frame_opacity});
@ -390,13 +382,21 @@ void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img, ps->backend_data, IMAGE_OP_APPLY_ALPHA_ALL, new_img,
NULL, &reg_visible_local, (double[]){w->opacity}); NULL, &reg_visible_local, (double[]){w->opacity});
} }
ps->backend_data->ops->compose(ps->backend_data, new_img, w->g.x, ps->backend_data->ops->compose(ps->backend_data, w, new_img, w->g.x,
w->g.y, &reg_paint_in_bound, w->g.y, &reg_paint_in_bound,
&reg_visible); &reg_visible);
ps->backend_data->ops->release_image(ps->backend_data, new_img); ps->backend_data->ops->release_image(ps->backend_data, new_img);
pixman_region32_fini(&reg_visible_local); pixman_region32_fini(&reg_visible_local);
pixman_region32_fini(&reg_bound_local); pixman_region32_fini(&reg_bound_local);
} }
// Round the corners as last step after blur/shadow/dim/etc
if (w->corner_radius > 0.0) {
ps->backend_data->ops->round(ps->backend_data, w,
ps->backend_round_context, w->win_image,
&reg_bound, &reg_visible);
}
pixman_region32_fini(&reg_bound); pixman_region32_fini(&reg_bound);
pixman_region32_fini(&reg_paint_in_bound); pixman_region32_fini(&reg_paint_in_bound);
} }

View File

@ -28,6 +28,9 @@ typedef struct backend_base {
/// Whether the backend can accept new render request at the moment /// Whether the backend can accept new render request at the moment
bool busy; bool busy;
// ... // ...
// Session data
session_t *ps;
} backend_t; } backend_t;
typedef void (*backend_ready_callback_t)(void *); typedef void (*backend_ready_callback_t)(void *);
@ -56,6 +59,11 @@ struct gaussian_blur_args {
double deviation; double deviation;
}; };
struct dual_kawase_blur_args {
int size;
blur_strength_t strength;
};
struct box_blur_args { struct box_blur_args {
int size; int size;
}; };
@ -65,6 +73,11 @@ struct kernel_blur_args {
int kernel_count; int kernel_count;
}; };
struct round_corners_args {
int corner_radius;
bool round_borders;
};
struct backend_operations { struct backend_operations {
// =========== Initialization =========== // =========== Initialization ===========
@ -126,7 +139,7 @@ struct backend_operations {
* @param reg_paint the clip region, in target coordinates * @param reg_paint the clip region, in target coordinates
* @param reg_visible the visible region, in target coordinates * @param reg_visible the visible region, in target coordinates
*/ */
void (*compose)(backend_t *backend_data, void *image_data, int dst_x, int dst_y, void (*compose)(backend_t *backend_data, struct managed_win *const w, void *image_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible); 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.
@ -137,6 +150,11 @@ struct backend_operations {
const region_t *reg_blur, const region_t *reg_visible) const region_t *reg_blur, const region_t *reg_visible)
attr_nonnull(1, 3, 4, 5); attr_nonnull(1, 3, 4, 5);
/// Round a given region of the rendering buffer.
bool (*round)(backend_t *backend_data, struct managed_win *w, void *round_ctx,
void *image_data, const region_t *reg_round, const region_t *reg_visible)
attr_nonnull(1, 2, 3, 5, 6);
/// Update part of the back buffer with the rendering buffer, then present the /// Update part of the back buffer with the rendering buffer, then present the
/// back buffer onto the target window (if not back buffered, update part of the /// back buffer onto the target window (if not back buffered, update part of the
/// target window directly). /// target window directly).
@ -218,6 +236,15 @@ struct backend_operations {
/// Get how many pixels outside of the blur area is needed for blur /// Get how many pixels outside of the blur area is needed for blur
void (*get_blur_size)(void *blur_context, int *width, int *height); void (*get_blur_size)(void *blur_context, int *width, int *height);
/// Backup our current window background so we can use it for "erasing" corners
bool (*store_back_texture)(backend_t *base, struct managed_win *w, void *ctx_,
const region_t *reg_tgt, int x, int y, int width, int height);
/// Create a rounded corners context
void *(*create_round_context)(backend_t *base, void *args);
/// Destroy a rounded corners context
void (*destroy_round_context)(backend_t *base, void *ctx);
// =========== Hooks ============ // =========== Hooks ============
/// Let the backend hook into the event handling queue /// Let the backend hook into the event handling queue
/// Not implemented yet /// Not implemented yet

View File

@ -362,10 +362,106 @@ struct conv **generate_blur_kernel(enum blur_method method, void *args, int *ker
return NULL; return NULL;
} }
/// Generate kernel parameters for dual-kawase blur method. Falls back on approximating
/// standard gauss radius if strength is not supplied
struct dual_kawase_params *generate_dual_kawase_params(void *args) {
struct dual_kawase_blur_args *blur_args = args;
static const struct {
int iterations;
float offset;
} strength_levels[20] = {
{.iterations = 1, .offset = 1.25f}, // LVL 1 => radius 4
{.iterations = 1, .offset = 2.25f}, // LVL 2 => radius 7
{.iterations = 2, .offset = 2.00f}, // LVL 3 => radius 14
{.iterations = 2, .offset = 3.00f}, // LVL 4 => radius 20
{.iterations = 2, .offset = 4.25f}, // LVL 5 => radius 28
{.iterations = 3, .offset = 2.50f}, // LVL 6 => radius 35
{.iterations = 3, .offset = 3.25f}, // LVL 7 => radius 45
{.iterations = 3, .offset = 4.25f}, // LVL 8 => radius 57
{.iterations = 3, .offset = 5.50f}, // LVL 9 => radius 74
{.iterations = 4, .offset = 3.25f}, // LVL 10 => radius 91
{.iterations = 4, .offset = 4.00f}, // LVL 11 => radius 110
{.iterations = 4, .offset = 5.00f}, // LVL 12 => radius 135
{.iterations = 4, .offset = 6.00f}, // LVL 13 => radius 161
{.iterations = 4, .offset = 7.25f}, // LVL 14 => radius 195
{.iterations = 4, .offset = 8.25f}, // LVL 15 => radius 221
{.iterations = 5, .offset = 4.50f}, // LVL 16 => radius 250
{.iterations = 5, .offset = 5.25f}, // LVL 17 => radius 287
{.iterations = 5, .offset = 6.25f}, // LVL 18 => radius 330
{.iterations = 5, .offset = 7.25f}, // LVL 19 => radius 383
{.iterations = 5, .offset = 8.50f}, // LVL 20 => radius >450
};
auto params = ccalloc(1, struct dual_kawase_params);
params->iterations = 0;
params->offset = 1.0f;
if (blur_args->strength.strength <= 0 && blur_args->size) {
// approximate blur_strength with gaussian blur_radius
if (blur_args->size < 6) {
blur_args->strength.strength = 1;
} else if (blur_args->size < 11) {
blur_args->strength.strength = 2;
} else if (blur_args->size < 17) {
blur_args->strength.strength = 3;
} else if (blur_args->size < 24) {
blur_args->strength.strength = 4;
} else if (blur_args->size < 32) {
blur_args->strength.strength = 5;
} else if (blur_args->size < 40) {
blur_args->strength.strength = 6;
} else if (blur_args->size < 51) {
blur_args->strength.strength = 7;
} else if (blur_args->size < 67) {
blur_args->strength.strength = 8;
} else if (blur_args->size < 83) {
blur_args->strength.strength = 9;
} else if (blur_args->size < 101) {
blur_args->strength.strength = 10;
} else if (blur_args->size < 123) {
blur_args->strength.strength = 11;
} else if (blur_args->size < 148) {
blur_args->strength.strength = 12;
} else if (blur_args->size < 177) {
blur_args->strength.strength = 13;
} else if (blur_args->size < 208) {
blur_args->strength.strength = 14;
} else if (blur_args->size < 236) {
blur_args->strength.strength = 15;
} else if (blur_args->size < 269) {
blur_args->strength.strength = 16;
} else if (blur_args->size < 309) {
blur_args->strength.strength = 17;
} else if (blur_args->size < 357) {
blur_args->strength.strength = 18;
} else if (blur_args->size < 417) {
blur_args->strength.strength = 19;
} else {
blur_args->strength.strength = 20;
}
}
if (blur_args->strength.strength > 0) {
assert(blur_args->strength.strength <= 20);
params->iterations = strength_levels[blur_args->strength.strength - 1].iterations;
params->offset = strength_levels[blur_args->strength.strength - 1].offset;
}
params->expand = 2 * (int)exp2f((float)params->iterations) *
(256 - (int)(256.0f - params->offset)) +
1;
log_info("blur-strength: %d [.iter = %d, .offset = %f]",
blur_args->strength.strength, params->iterations, params->offset);
return params;
}
void init_backend_base(struct backend_base *base, session_t *ps) { void init_backend_base(struct backend_base *base, session_t *ps) {
base->c = ps->c; base->c = ps->c;
base->loop = ps->loop; base->loop = ps->loop;
base->root = ps->root; base->root = ps->root;
base->busy = false; base->busy = false;
base->ops = NULL; base->ops = NULL;
base->ps = ps;
} }

View File

@ -16,6 +16,15 @@ typedef struct conv conv;
typedef struct backend_base backend_t; typedef struct backend_base backend_t;
struct backend_operations; struct backend_operations;
typedef struct dual_kawase_params {
/// Number of downsample passes
int iterations;
/// Pixel offset for down- and upsample
float offset;
/// Save area around blur target (@ref resize_width, @ref resize_height)
int expand;
} dual_kawase_params_t;
bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width, bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width,
int height, const conv *kernel, xcb_render_picture_t shadow_pixel, int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
xcb_pixmap_t *pixmap, xcb_render_picture_t *pict); xcb_pixmap_t *pixmap, xcb_render_picture_t *pict);
@ -41,3 +50,5 @@ default_backend_render_shadow(backend_t *backend_data, int width, int height,
void init_backend_base(struct backend_base *base, session_t *ps); void init_backend_base(struct backend_base *base, session_t *ps);
struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count); struct conv **generate_blur_kernel(enum blur_method method, void *args, int *kernel_count);
struct dual_kawase_params *generate_dual_kawase_params(void *args);

View File

@ -12,14 +12,6 @@
#include "compiler.h" #include "compiler.h"
#include "log.h" #include "log.h"
/// Apply driver specified global workarounds. It's safe to call this multiple times.
void apply_driver_workarounds(struct session *ps, enum driver driver) {
if (driver & DRIVER_NVIDIA) {
setenv("__GL_YIELD", "usleep", true);
ps->o.xrender_sync_fence = true;
}
}
enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) { enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) {
enum driver ret = 0; enum driver ret = 0;
// First we try doing backend agnostic detection using RANDR // First we try doing backend agnostic detection using RANDR

View File

@ -32,9 +32,6 @@ enum driver {
/// Note, this is a best-effort test, so no guarantee all drivers will be detected. /// Note, this is a best-effort test, so no guarantee all drivers will be detected.
enum driver detect_driver(xcb_connection_t *, struct backend_base *, xcb_window_t); enum driver detect_driver(xcb_connection_t *, struct backend_base *, xcb_window_t);
/// Apply driver specified global workarounds. It's safe to call this multiple times.
void apply_driver_workarounds(struct session *ps, enum driver);
// Print driver names to stdout, for diagnostics // Print driver names to stdout, for diagnostics
static inline void print_drivers(enum driver drivers) { static inline void print_drivers(enum driver drivers) {
if (drivers & DRIVER_AMDGPU) { if (drivers & DRIVER_AMDGPU) {

View File

@ -56,7 +56,7 @@ static void dummy_check_image(struct backend_base *base, const struct dummy_imag
assert(*tmp->refcount > 0); assert(*tmp->refcount > 0);
} }
void dummy_compose(struct backend_base *base, void *image, int dst_x attr_unused, void dummy_compose(struct backend_base *base, struct managed_win *w attr_unused, void *image, int dst_x attr_unused,
int dst_y attr_unused, const region_t *reg_paint attr_unused, int dst_y attr_unused, const region_t *reg_paint attr_unused,
const region_t *reg_visible attr_unused) { const region_t *reg_visible attr_unused) {
dummy_check_image(base, image); dummy_check_image(base, image);
@ -72,6 +72,12 @@ bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity at
return true; return true;
} }
bool dummy_round(struct backend_base *backend_data attr_unused, struct managed_win *w attr_unused,
void *ctx_ attr_unused, void *image_data attr_unused, const region_t *reg_round attr_unused,
const region_t *reg_visible attr_unused) {
return true;
}
void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap, void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
struct xvisual_info fmt, bool owned attr_unused) { struct xvisual_info fmt, bool owned attr_unused) {
auto dummy = (struct dummy_data *)base; auto dummy = (struct dummy_data *)base;
@ -138,6 +144,14 @@ void *dummy_create_blur_context(struct backend_base *base attr_unused,
void dummy_destroy_blur_context(struct backend_base *base attr_unused, void *ctx attr_unused) { void dummy_destroy_blur_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
} }
void *dummy_create_round_context(struct backend_base *base attr_unused, void *args attr_unused) {
static int dummy_context;
return &dummy_context;
}
void dummy_destroy_round_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) { void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
// These numbers are arbitrary, to make sure the reisze_region code path is // These numbers are arbitrary, to make sure the reisze_region code path is
// covered. // covered.
@ -145,12 +159,18 @@ void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
*height = 5; *height = 5;
} }
bool dummy_store_back_texture(backend_t *backend_data attr_unused, struct managed_win *w attr_unused, void *ctx_ attr_unused,
const region_t *reg_tgt attr_unused, int x attr_unused, int y attr_unused, int width attr_unused, int height attr_unused) {
return true;
}
struct backend_operations dummy_ops = { struct backend_operations dummy_ops = {
.init = dummy_init, .init = dummy_init,
.deinit = dummy_deinit, .deinit = dummy_deinit,
.compose = dummy_compose, .compose = dummy_compose,
.fill = dummy_fill, .fill = dummy_fill,
.blur = dummy_blur, .blur = dummy_blur,
.round = dummy_round,
.bind_pixmap = dummy_bind_pixmap, .bind_pixmap = dummy_bind_pixmap,
.render_shadow = default_backend_render_shadow, .render_shadow = default_backend_render_shadow,
.release_image = dummy_release_image, .release_image = dummy_release_image,
@ -162,6 +182,9 @@ struct backend_operations dummy_ops = {
.copy = dummy_image_copy, .copy = dummy_image_copy,
.create_blur_context = dummy_create_blur_context, .create_blur_context = dummy_create_blur_context,
.destroy_blur_context = dummy_destroy_blur_context, .destroy_blur_context = dummy_destroy_blur_context,
.create_round_context = dummy_create_round_context,
.destroy_round_context = dummy_destroy_round_context,
.get_blur_size = dummy_get_blur_size, .get_blur_size = dummy_get_blur_size,
.store_back_texture = dummy_store_back_texture
}; };

File diff suppressed because it is too large Load Diff

View File

@ -33,10 +33,25 @@ typedef struct {
typedef struct { typedef struct {
GLuint prog; GLuint prog;
GLint unifm_opacity; GLint unifm_opacity;
GLint unifm_texture_size;
GLint unifm_halfpixel;
GLint orig_loc; GLint orig_loc;
GLint texorig_loc; GLint texorig_loc;
GLint projection_loc;
} gl_blur_shader_t; } gl_blur_shader_t;
typedef struct {
GLuint prog;
GLint projection_loc;
GLint unifm_radius;
GLint unifm_texcoord;
GLint unifm_texsize;
GLint unifm_borderw;
GLint unifm_resolution;
GLint unifm_tex_bg;
GLint unifm_tex_wnd;
} gl_round_shader_t;
typedef struct { typedef struct {
GLuint prog; GLuint prog;
GLint color_loc; GLint color_loc;
@ -68,7 +83,7 @@ struct gl_data {
backend_t base; backend_t base;
// If we are using proprietary NVIDIA driver // If we are using proprietary NVIDIA driver
bool is_nvidia; bool is_nvidia;
// Height and width of the root window // Height and width of the viewport
int height, width; int height, width;
gl_win_shader_t win_shader; gl_win_shader_t win_shader;
gl_brightness_shader_t brightness_shader; gl_brightness_shader_t brightness_shader;
@ -98,7 +113,7 @@ GLuint gl_create_program_from_str(const char *vert_shader_str, const char *frag_
/** /**
* @brief Render a region with texture data. * @brief Render a region with texture data.
*/ */
void gl_compose(backend_t *, void *ptex, int dst_x, int dst_y, const region_t *reg_tgt, void gl_compose(backend_t *, struct managed_win *, void *ptex, int dst_x, int dst_y, const region_t *reg_tgt,
const region_t *reg_visible); const region_t *reg_visible);
void gl_resize(struct gl_data *, int width, int height); void gl_resize(struct gl_data *, int width, int height);
@ -121,6 +136,14 @@ void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
void gl_destroy_blur_context(backend_t *base, void *ctx); void gl_destroy_blur_context(backend_t *base, void *ctx);
void gl_get_blur_size(void *blur_context, int *width, int *height); void gl_get_blur_size(void *blur_context, int *width, int *height);
bool gl_round(backend_t *backend_data, struct managed_win *w, void *ctx_,
void *image_data, const region_t *reg_round, const region_t *reg_visible);
void *gl_create_round_context(backend_t *base, void *args);
void gl_destroy_round_context(backend_t *base, void *ctx);
bool gl_store_back_texture(backend_t *backend_data, struct managed_win *w,
void *ctx_, const region_t *reg_tgt, int x, int y, int width, int height);
bool gl_is_image_transparent(backend_t *base, void *image_data); bool gl_is_image_transparent(backend_t *base, void *image_data);
void gl_fill(backend_t *base, struct color, const region_t *clip); void gl_fill(backend_t *base, struct color, const region_t *clip);

View File

@ -45,6 +45,8 @@ struct _glx_data {
Display *display; Display *display;
int screen; int screen;
xcb_window_t target_win; xcb_window_t target_win;
int glx_event;
int glx_error;
GLXContext ctx; GLXContext ctx;
}; };
@ -244,7 +246,7 @@ static backend_t *glx_init(session_t *ps) {
XVisualInfo *pvis = NULL; XVisualInfo *pvis = NULL;
// Check for GLX extension // Check for GLX extension
if (!ps->glx_exists) { if (!glXQueryExtension(ps->dpy, &gd->glx_event, &gd->glx_error)) {
log_error("No GLX extension."); log_error("No GLX extension.");
goto end; goto end;
} }
@ -466,7 +468,10 @@ static void glx_present(backend_t *base, const region_t *region attr_unused) {
struct _glx_data *gd = (void *)base; struct _glx_data *gd = (void *)base;
gl_present(base, region); gl_present(base, region);
glXSwapBuffers(gd->display, gd->target_win); glXSwapBuffers(gd->display, gd->target_win);
glFinish(); // XXX there should be no need to block, the core should wait for render to finish
if (!gd->gl.is_nvidia) {
glFinish();
}
} }
static int glx_buffer_age(backend_t *base) { static int glx_buffer_age(backend_t *base) {
@ -489,6 +494,7 @@ struct backend_operations glx_ops = {
.image_op = gl_image_op, .image_op = gl_image_op,
.copy = gl_copy, .copy = gl_copy,
.blur = gl_blur, .blur = gl_blur,
.round = gl_round,
.is_image_transparent = gl_is_image_transparent, .is_image_transparent = gl_is_image_transparent,
.present = glx_present, .present = glx_present,
.buffer_age = glx_buffer_age, .buffer_age = glx_buffer_age,
@ -496,7 +502,10 @@ struct backend_operations glx_ops = {
.fill = gl_fill, .fill = gl_fill,
.create_blur_context = gl_create_blur_context, .create_blur_context = gl_create_blur_context,
.destroy_blur_context = gl_destroy_blur_context, .destroy_blur_context = gl_destroy_blur_context,
.create_round_context = gl_create_round_context,
.destroy_round_context = gl_destroy_round_context,
.get_blur_size = gl_get_blur_size, .get_blur_size = gl_get_blur_size,
.store_back_texture = gl_store_back_texture,
.max_buffer_age = 5, // Why? .max_buffer_age = 5, // Why?
}; };

View File

@ -90,7 +90,9 @@ struct _xrender_image_data {
bool owned; bool owned;
}; };
static void compose(backend_t *base, void *img_data, int dst_x, int dst_y, uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_ntraps, int cr, int wid, int hei);
static void compose(backend_t *base, struct managed_win *w, void *img_data, int dst_x, int dst_y,
const region_t *reg_paint, const region_t *reg_visible) { const region_t *reg_paint, const region_t *reg_visible) {
struct _xrender_data *xd = (void *)base; struct _xrender_data *xd = (void *)base;
struct _xrender_image_data *img = img_data; struct _xrender_image_data *img = img_data;
@ -104,10 +106,52 @@ static void compose(backend_t *base, void *img_data, int dst_x, int dst_y,
// sure we get everything into the buffer // sure we get everything into the buffer
x_clear_picture_clip_region(base->c, img->pict); x_clear_picture_clip_region(base->c, img->pict);
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg); // Are we rounding corners?
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0, session_t *ps = base->ps;
to_i16_checked(dst_x), to_i16_checked(dst_y), int cr = (w ? w->corner_radius : 0);
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
if (cr == 0) {
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, op, img->pict, alpha_pict, xd->back[2], 0, 0, 0, 0,
to_i16_checked(dst_x), to_i16_checked(dst_y),
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
} else {
// Rounded corners
const int fullwid = w ? w->widthb : 0;
const int fullhei = w ? w-> heightb : 0;
//const int fullwid = img->width;
//const int fullhei = img->height;
//log_warn("f(%d, %d) imge(%d %d) imgf(%d %d) sdw(%d %d) dst(%d %d) s:%d b:%d", fullwid, fullhei, img->ewidth, img->eheight, img->width, img->height, w->shadow_width, w->shadow_height, dst_x, dst_y, w->shadow, w->g.border_width);
xcb_render_picture_t p_tmp = x_create_picture_with_standard(
ps->c, ps->root, fullwid, fullhei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {
.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0,
.y = 0,
.width = to_u16_checked(fullwid),
.height = to_u16_checked(fullhei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
p_tmp, trans, 1, &rect);
uint32_t max_ntraps = to_u32_checked(cr);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, cr, fullwid, fullhei);
xcb_render_trapezoids(
ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8),
0, 0, n, traps);
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, &reg);
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OVER, img->pict, p_tmp, xd->back[2],
0, 0, 0, 0,
//0, 0, to_i16_checked(x), to_i16_checked(y),
to_i16_checked(dst_x), to_i16_checked(dst_y),
to_u16_checked(img->ewidth), to_u16_checked(img->eheight));
xcb_render_free_picture(ps->c, p_tmp);
}
pixman_region32_fini(&reg); pixman_region32_fini(&reg);
} }
@ -245,6 +289,15 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_,
return true; return true;
} }
static bool x_round(struct backend_base *backend_data attr_unused, struct managed_win *w attr_unused,
void *ctx_ attr_unused, void *image_data attr_unused, const region_t *reg_blur attr_unused,
const region_t *reg_visible attr_unused) {
// dummy implementation, we already perform the rounding in compose
// TODO: should move the compose code here and call it from here
return true;
}
static void * static void *
bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) { bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
xcb_generic_error_t *e; xcb_generic_error_t *e;
@ -506,6 +559,13 @@ void *create_blur_context(backend_t *base attr_unused, enum blur_method method,
return ret; return ret;
} }
if (method == BLUR_METHOD_DUAL_KAWASE || method == BLUR_METHOD_ALT_KAWASE) {
log_warn("Blur method 'kawase' is not compatible with the 'xrender' "
"backend.");
ret->method = BLUR_METHOD_NONE;
return ret;
}
ret->method = BLUR_METHOD_KERNEL; ret->method = BLUR_METHOD_KERNEL;
struct conv **kernels; struct conv **kernels;
int kernel_count; int kernel_count;
@ -551,6 +611,19 @@ void get_blur_size(void *blur_context, int *width, int *height) {
*height = ctx->resize_height; *height = ctx->resize_height;
} }
bool store_back_texture(backend_t *backend_data attr_unused, struct managed_win *w attr_unused, void *ctx_ attr_unused,
const region_t *reg_tgt attr_unused, int x attr_unused, int y attr_unused, int width attr_unused, int height attr_unused) {
return true;
}
void *create_round_context(struct backend_base *base attr_unused, void *args attr_unused) {
static int dummy_context;
return &dummy_context;
}
void destroy_round_context(struct backend_base *base attr_unused, void *ctx attr_unused) {
}
backend_t *backend_xrender_init(session_t *ps) { backend_t *backend_xrender_init(session_t *ps) {
auto xd = ccalloc(1, struct _xrender_data); auto xd = ccalloc(1, struct _xrender_data);
init_backend_base(&xd->base, ps); init_backend_base(&xd->base, ps);
@ -638,6 +711,7 @@ struct backend_operations xrender_ops = {
.init = backend_xrender_init, .init = backend_xrender_init,
.deinit = deinit, .deinit = deinit,
.blur = blur, .blur = blur,
.round = x_round,
.present = present, .present = present,
.compose = compose, .compose = compose,
.fill = fill, .fill = fill,
@ -654,7 +728,11 @@ struct backend_operations xrender_ops = {
.copy = copy, .copy = copy,
.create_blur_context = create_blur_context, .create_blur_context = create_blur_context,
.destroy_blur_context = destroy_blur_context, .destroy_blur_context = destroy_blur_context,
.create_round_context = create_round_context,
.destroy_round_context = destroy_round_context,
.get_blur_size = get_blur_size, .get_blur_size = get_blur_size,
.store_back_texture = store_back_texture
}; };
// vim: set noet sw=8 ts=8: // vim: set noet sw=8 ts=8:

View File

@ -75,6 +75,9 @@
/// @brief Maximum OpenGL buffer age. /// @brief Maximum OpenGL buffer age.
#define CGLX_MAX_BUFFER_AGE 5 #define CGLX_MAX_BUFFER_AGE 5
/// @brief Maximum passes for blur.
#define MAX_BLUR_PASS 6
// Window flags // Window flags
// === Types === // === Types ===
@ -157,6 +160,8 @@ typedef struct session {
backend_t *backend_data; backend_t *backend_data;
/// backend blur context /// backend blur context
void *backend_blur_context; void *backend_blur_context;
/// round corners context
void *backend_round_context;
/// graphic drivers used /// graphic drivers used
enum driver drivers; enum driver drivers;
/// file watch handle /// file watch handle
@ -214,7 +219,6 @@ typedef struct session {
/// Custom GLX program used for painting window. /// Custom GLX program used for painting window.
// XXX should be in struct glx_session // XXX should be in struct glx_session
glx_prog_main_t glx_prog_win; glx_prog_main_t glx_prog_win;
struct glx_fbconfig_info *argb_fbconfig;
#endif #endif
/// Sync fence to sync draw operations /// Sync fence to sync draw operations
xcb_sync_fence_t sync_fence; xcb_sync_fence_t sync_fence;
@ -340,12 +344,14 @@ typedef struct session {
int randr_error; int randr_error;
/// Whether X Present extension exists. /// Whether X Present extension exists.
bool present_exists; bool present_exists;
#ifdef CONFIG_OPENGL
/// Whether X GLX extension exists. /// Whether X GLX extension exists.
bool glx_exists; bool glx_exists;
/// Event base number for X GLX extension. /// Event base number for X GLX extension.
int glx_event; int glx_event;
/// Error base number for X GLX extension. /// Error base number for X GLX extension.
int glx_error; int glx_error;
#endif
/// Whether X Xinerama extension exists. /// Whether X Xinerama extension exists.
bool xinerama_exists; bool xinerama_exists;
/// Xinerama screen regions. /// Xinerama screen regions.
@ -528,7 +534,8 @@ static inline void wintype_arr_enable(bool arr[]) {
} }
} }
/** /**
* Get current system clock in 40 microseconds. * Get current system clock in milliseconds.
*/ */
int64_t get_time_ms(void); int64_t get_time_ms(void);

View File

@ -88,6 +88,11 @@ enum blur_method parse_blur_method(const char *src) {
return BLUR_METHOD_BOX; return BLUR_METHOD_BOX;
} else if (strcmp(src, "gaussian") == 0) { } else if (strcmp(src, "gaussian") == 0) {
return BLUR_METHOD_GAUSSIAN; return BLUR_METHOD_GAUSSIAN;
} else if (strcmp(src, "kawase") == 0 || strcmp(src, "dual_kawase") == 0) {
return BLUR_METHOD_DUAL_KAWASE;
} else if (strcmp(src, "kawase_alt") == 0 || strcmp(src, "alt_kawase") == 0) {
// new code from tryone144's `improved_dbo` branch
return BLUR_METHOD_ALT_KAWASE;
} else if (strcmp(src, "none") == 0) { } else if (strcmp(src, "none") == 0) {
return BLUR_METHOD_NONE; return BLUR_METHOD_NONE;
} }
@ -489,6 +494,12 @@ 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].corner_radius) {
opt->wintype_option[i].corner_radius = -1;
}
if (!mask[i].round_borders) {
opt->wintype_option[i].round_borders = -1;
}
} }
} }
@ -497,15 +508,6 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
*opt = (struct options){ *opt = (struct options){
.backend = BKEND_XRENDER, .backend = BKEND_XRENDER,
.glx_no_stencil = false, .glx_no_stencil = false,
.transition_length = 300,
.transition_pow_x = 0.1,
.transition_pow_y = 0.1,
.transition_pow_w = 0.1,
.transition_pow_h = 0.1,
.size_transition = true,
.no_scale_down = false,
.spawn_center_screen = false,
.spawn_center = true,
.mark_wmwin_focused = false, .mark_wmwin_focused = false,
.mark_ovredir_focused = false, .mark_ovredir_focused = false,
.detect_rounded_corners = false, .detect_rounded_corners = false,
@ -519,6 +521,15 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.benchmark = 0, .benchmark = 0,
.benchmark_wid = XCB_NONE, .benchmark_wid = XCB_NONE,
.logpath = NULL, .logpath = NULL,
.transition_length = 300,
.transition_pow_x = 0.1,
.transition_pow_y = 0.1,
.transition_pow_w = 0.1,
.transition_pow_h = 0.1,
.size_transition = true,
.no_scale_down = false,
.spawn_center_screen = false,
.spawn_center = true,
.refresh_rate = 0, .refresh_rate = 0,
.sw_opti = false, .sw_opti = false,
@ -551,6 +562,7 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.blur_method = BLUR_METHOD_NONE, .blur_method = BLUR_METHOD_NONE,
.blur_radius = 3, .blur_radius = 3,
.blur_deviation = 0.84089642, .blur_deviation = 0.84089642,
.blur_strength = {.strength = -1, .iterations = 3, .offset = 2.75, .expand = 50},
.blur_background_frame = false, .blur_background_frame = false,
.blur_background_fixed = false, .blur_background_fixed = false,
.blur_background_blacklist = NULL, .blur_background_blacklist = NULL,
@ -569,7 +581,9 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
.no_ewmh_fullscreen = false, .no_ewmh_fullscreen = false,
.track_leader = false, .track_leader = false,
};
.rounded_corners_blacklist = NULL,
.round_borders_blacklist = NULL};
char *ret = NULL; char *ret = NULL;
#ifdef CONFIG_LIBCONFIG #ifdef CONFIG_LIBCONFIG

View File

@ -44,6 +44,8 @@ typedef struct win_option_mask {
bool full_shadow : 1; bool full_shadow : 1;
bool redir_ignore : 1; bool redir_ignore : 1;
bool opacity : 1; bool opacity : 1;
bool corner_radius : 1;
bool round_borders : 1;
} win_option_mask_t; } win_option_mask_t;
typedef struct win_option { typedef struct win_option {
@ -53,6 +55,8 @@ typedef struct win_option {
bool full_shadow; bool full_shadow;
bool redir_ignore; bool redir_ignore;
double opacity; double opacity;
int corner_radius;
int round_borders;
} win_option_t; } win_option_t;
enum blur_method { enum blur_method {
@ -60,9 +64,18 @@ enum blur_method {
BLUR_METHOD_KERNEL, BLUR_METHOD_KERNEL,
BLUR_METHOD_BOX, BLUR_METHOD_BOX,
BLUR_METHOD_GAUSSIAN, BLUR_METHOD_GAUSSIAN,
BLUR_METHOD_DUAL_KAWASE,
BLUR_METHOD_ALT_KAWASE,
BLUR_METHOD_INVALID, BLUR_METHOD_INVALID,
}; };
typedef struct blur_strength {
int expand;
int strength;
int iterations;
float offset;
} blur_strength_t;
typedef struct _c2_lptr c2_lptr_t; typedef struct _c2_lptr c2_lptr_t;
/// Structure representing all options. /// Structure representing all options.
@ -87,7 +100,7 @@ typedef struct options {
bool glx_no_stencil; bool glx_no_stencil;
/// Whether to avoid rebinding pixmap on window damage. /// Whether to avoid rebinding pixmap on window damage.
bool glx_no_rebind_pixmap; bool glx_no_rebind_pixmap;
/// Length of window transitions /// Length of window transitions
int transition_length; int transition_length;
/// For smoothing on the x-coordinate of window animations /// For smoothing on the x-coordinate of window animations
float transition_pow_x; float transition_pow_x;
@ -207,6 +220,8 @@ typedef struct options {
int blur_radius; int blur_radius;
// Standard deviation for the gaussian blur // Standard deviation for the gaussian blur
double blur_deviation; double blur_deviation;
/// Blur strength (for kawase blur)
blur_strength_t blur_strength;
/// Whether to blur background when the window frame is not opaque. /// Whether to blur background when the window frame is not opaque.
/// Implies blur_background. /// Implies blur_background.
bool blur_background_frame; bool blur_background_frame;
@ -255,6 +270,15 @@ 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;
// === Rounded corners related ===
int corner_radius;
/// Rounded corners blacklist. A linked list of conditions.
c2_lptr_t *rounded_corners_blacklist;
/// Do we round the borders of rounded windows?
int round_borders;
/// Rounded borders blacklist. A linked list of conditions.
c2_lptr_t *round_borders_blacklist;
} options_t; } options_t;
extern const char *const BACKEND_STRS[NUM_BKEND + 1]; extern const char *const BACKEND_STRS[NUM_BKEND + 1];
@ -331,3 +355,42 @@ static inline bool parse_vsync(const char *str) {
} }
// vim: set noet sw=8 ts=8 : // vim: set noet sw=8 ts=8 :
/**
* Parse a blur_strength option argument.
*/
static inline attr_pure blur_strength_t
parse_kawase_blur_strength(const int level) {
static const blur_strength_t values[20] = {
{ .expand = 10, .strength =1, .iterations = 1, .offset = 1.5 }, // 1
{ .expand = 10, .strength =2, .iterations = 1, .offset = 2.0 }, // 2
{ .expand = 20, .strength =3, .iterations = 2, .offset = 2.5 }, // 3
{ .expand = 20, .strength =4, .iterations = 2, .offset = 3.0 }, // 4
{ .expand = 50, .strength =5, .iterations = 3, .offset = 2.75 }, // 5
{ .expand = 50, .strength =6, .iterations = 3, .offset = 3.5 }, // 6
{ .expand = 50, .strength =7, .iterations = 3, .offset = 4.25 }, // 7
{ .expand = 50, .strength =8, .iterations = 3, .offset = 5.0 }, // 8
{ .expand = 150, .strength =9, .iterations = 4, .offset = 3.71429f }, // 9
{ .expand = 150, .strength =10, .iterations = 4, .offset = 4.42857f }, // 10
{ .expand = 150, .strength =11, .iterations = 4, .offset = 5.14286f }, // 11
{ .expand = 150, .strength =12, .iterations = 4, .offset = 5.85714f }, // 12
{ .expand = 150, .strength =13, .iterations = 4, .offset = 6.57143f }, // 13
{ .expand = 150, .strength =14, .iterations = 4, .offset = 7.28571f }, // 14
{ .expand = 150, .strength =15, .iterations = 4, .offset = 8.0 }, // 15
{ .expand = 400, .strength =16, .iterations = 5, .offset = 6.0 }, // 16
{ .expand = 400, .strength =17, .iterations = 5, .offset = 7.0 }, // 17
{ .expand = 400, .strength =18, .iterations = 5, .offset = 8.0 }, // 18
{ .expand = 400, .strength =19, .iterations = 5, .offset = 9.0 }, // 19
{ .expand = 400, .strength =20, .iterations = 5, .offset = 10.0 }, // 20
};
if (level < 1 || level > 20) {
log_error("(\"%d\"): Invalid blur_strength argument. Needs to be a number between 1 and 20. Will default to 5", level);
return values[5];
}
log_info("blur-strength: %d [.iter = %d, .offset = %f, .expand = %d]",
level, values[level - 1].iterations, values[level - 1].offset, values[level - 1].expand);
return values[level - 1];;
}

View File

@ -284,6 +284,17 @@ static inline void parse_wintype_config(const config_t *cfg, const char *member_
o->opacity = normalize_d(fval); o->opacity = normalize_d(fval);
mask->opacity = true; mask->opacity = true;
} }
if (config_setting_lookup_int(setting, "corner-radius", &ival)) {
o->corner_radius = ival;
mask->corner_radius = true;
// log_warn("%s: corner-radius: %d", member_name, ival);
}
if (config_setting_lookup_int(setting, "round-borders", &ival)) {
o->round_borders = ival;
mask->round_borders = true;
// log_warn("%s: round_borders: %d", member_name, ival);
}
} }
} }
@ -353,29 +364,31 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
// -O (fade_out_step) // -O (fade_out_step)
if (config_lookup_float(&cfg, "fade-out-step", &dval)) if (config_lookup_float(&cfg, "fade-out-step", &dval))
opt->fade_out_step = normalize_d(dval); opt->fade_out_step = normalize_d(dval);
// --transition-length
if (config_lookup_int(&cfg, "transition-length", &ival)) // --transition-length
opt->transition_length = ival; if (config_lookup_int(&cfg, "transition-length", &ival))
// --transition-pow-x opt->transition_length = ival;
if (config_lookup_float(&cfg, "transition-pow-x", &dval)) // --transition-pow-x
opt->transition_pow_x = dval; if (config_lookup_float(&cfg, "transition-pow-x", &dval))
// --transition-pow-y opt->transition_pow_x = dval;
if (config_lookup_float(&cfg, "transition-pow-y", &dval)) // --transition-pow-y
opt->transition_pow_y = dval; if (config_lookup_float(&cfg, "transition-pow-y", &dval))
// --transition-pow-w opt->transition_pow_y = dval;
if (config_lookup_float(&cfg, "transition-pow-w", &dval)) // --transition-pow-w
opt->transition_pow_w = dval; if (config_lookup_float(&cfg, "transition-pow-w", &dval))
// --transition-pow-h opt->transition_pow_w = dval;
if (config_lookup_float(&cfg, "transition-pow-h", &dval)) // --transition-pow-h
opt->transition_pow_h = dval; if (config_lookup_float(&cfg, "transition-pow-h", &dval))
// --size-transition opt->transition_pow_h = dval;
lcfg_lookup_bool(&cfg, "size-transition", &opt->size_transition); // --size-transition
// --spawn-center-screen lcfg_lookup_bool(&cfg, "size-transition", &opt->size_transition);
lcfg_lookup_bool(&cfg, "spawn-center-screen", &opt->spawn_center_screen); // --spawn-center-screen
// --spawn-center lcfg_lookup_bool(&cfg, "spawn-center-screen", &opt->spawn_center_screen);
lcfg_lookup_bool(&cfg, "spawn-center", &opt->spawn_center); // --spawn-center
// --no-scale-down lcfg_lookup_bool(&cfg, "spawn-center", &opt->spawn_center);
lcfg_lookup_bool(&cfg, "no-scale-down", &opt->no_scale_down); // --no-scale-down
lcfg_lookup_bool(&cfg, "no-scale-down", &opt->no_scale_down);
// -r (shadow_radius) // -r (shadow_radius)
config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius); config_lookup_int(&cfg, "shadow-radius", &opt->shadow_radius);
// -o (shadow_opacity) // -o (shadow_opacity)
@ -390,6 +403,14 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
// --active_opacity // --active_opacity
if (config_lookup_float(&cfg, "active-opacity", &dval)) if (config_lookup_float(&cfg, "active-opacity", &dval))
opt->active_opacity = normalize_d(dval); opt->active_opacity = normalize_d(dval);
// --corner-radius
config_lookup_int(&cfg, "corner-radius", &opt->corner_radius);
// --rounded-corners-exclude
parse_cfg_condlst(&cfg, &opt->rounded_corners_blacklist, "rounded-corners-exclude");
// --round-borders
config_lookup_int(&cfg, "round-borders", &opt->round_borders);
// --round-borders-exclude
parse_cfg_condlst(&cfg, &opt->round_borders_blacklist, "round-borders-exclude");
// -e (frame_opacity) // -e (frame_opacity)
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity); config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
// -c (shadow_enable) // -c (shadow_enable)
@ -469,6 +490,7 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
} }
lcfg_lookup_bool(&cfg, "vsync", &opt->vsync); lcfg_lookup_bool(&cfg, "vsync", &opt->vsync);
// --backend // --backend
lcfg_lookup_bool(&cfg, "experimental-backends", &opt->experimental_backends);
if (config_lookup_string(&cfg, "backend", &sval)) { if (config_lookup_string(&cfg, "backend", &sval)) {
opt->backend = parse_backend(sval); opt->backend = parse_backend(sval);
if (opt->backend >= NUM_BKEND) { if (opt->backend >= NUM_BKEND) {
@ -545,6 +567,10 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
config_lookup_int(&cfg, "blur-size", &opt->blur_radius); config_lookup_int(&cfg, "blur-size", &opt->blur_radius);
// --blur-deviation // --blur-deviation
config_lookup_float(&cfg, "blur-deviation", &opt->blur_deviation); config_lookup_float(&cfg, "blur-deviation", &opt->blur_deviation);
// --blur-strength
if (config_lookup_int(&cfg, "blur-strength", &ival) && ival) {
opt->blur_strength = parse_kawase_blur_strength(ival);
}
// --blur-background // --blur-background
if (config_lookup_bool(&cfg, "blur-background", &ival) && ival) { if (config_lookup_bool(&cfg, "blur-background", &ival) && ival) {
if (opt->blur_method == BLUR_METHOD_NONE) { if (opt->blur_method == BLUR_METHOD_NONE) {
@ -570,7 +596,6 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
lcfg_lookup_bool(&cfg, "glx-no-stencil", &opt->glx_no_stencil); lcfg_lookup_bool(&cfg, "glx-no-stencil", &opt->glx_no_stencil);
// --glx-no-rebind-pixmap // --glx-no-rebind-pixmap
lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &opt->glx_no_rebind_pixmap); lcfg_lookup_bool(&cfg, "glx-no-rebind-pixmap", &opt->glx_no_rebind_pixmap);
lcfg_lookup_bool(&cfg, "force-win-blend", &opt->force_win_blend);
// --glx-swap-method // --glx-swap-method
if (config_lookup_string(&cfg, "glx-swap-method", &sval)) { if (config_lookup_string(&cfg, "glx-swap-method", &sval)) {
char *endptr; char *endptr;
@ -608,8 +633,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
} }
// --xrender-sync // --xrender-sync
if (config_lookup_bool(&cfg, "xrender-sync", &ival) && ival) { if (config_lookup_bool(&cfg, "xrender-sync", &ival) && ival) {
log_error("Please use xrender-sync-fence instead of xrender-sync."); log_warn("Please use xrender-sync-fence instead of xrender-sync.");
goto err; opt->xrender_sync_fence = true;
} }
// --xrender-sync-fence // --xrender-sync-fence
lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence); lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence);
@ -617,21 +642,25 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval)) if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval))
log_warn("\"clear-shadow\" is removed as an option, and is always" log_warn("\"clear-shadow\" is removed as an option, and is always"
" enabled now. Consider removing it from your config file"); " enabled now. Consider removing it from your config file");
if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval)) { if (lcfg_lookup_bool(&cfg, "paint-on-overlay", &bval))
log_error("\"paint-on-overlay\" has been removed as an option, and " log_warn("\"paint-on-overlay\" has been removed as an option, and "
"the feature is enabled whenever possible"); "is enabled whenever possible");
goto err;
}
if (config_lookup_float(&cfg, "alpha-step", &dval)) { if (config_lookup_float(&cfg, "alpha-step", &dval))
log_error("\"alpha-step\" has been removed, compton now tries to make use" log_warn("\"alpha-step\" has been removed, compton now tries to make use"
" of all alpha values"); " of all alpha values");
goto err;
}
const char *deprecation_message attr_unused = const char *deprecation_message =
"has been removed. If you encounter problems " "has been removed. If you encounter problems "
"without this feature, please feel free to open a bug report"; "without this feature, please feel free to open a bug report";
if (lcfg_lookup_bool(&cfg, "glx-use-copysubbuffermesa", &bval) && bval) {
log_error("\"glx-use-copysubbuffermesa\" %s", deprecation_message);
return ERR_PTR(-1);
}
if (lcfg_lookup_bool(&cfg, "glx-copy-from-front", &bval) && bval) {
log_error("\"glx-copy-from-front\" %s", deprecation_message);
return ERR_PTR(-1);
}
config_setting_t *blur_cfg = config_lookup(&cfg, "blur"); config_setting_t *blur_cfg = config_lookup(&cfg, "blur");
if (blur_cfg) { if (blur_cfg) {
@ -655,6 +684,10 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
} }
config_setting_lookup_float(blur_cfg, "deviation", &opt->blur_deviation); config_setting_lookup_float(blur_cfg, "deviation", &opt->blur_deviation);
if (config_setting_lookup_int(blur_cfg, "strength", &ival) && ival) {
opt->blur_strength = parse_kawase_blur_strength(ival);
}
} }
// Wintype settings // Wintype settings

View File

@ -100,9 +100,6 @@ static inline xcb_window_t attr_pure ev_window(session_t *ps, xcb_generic_event_
} }
} }
#define CASESTRRET(s) \
case s: return #s;
static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) { static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) {
static char buf[128]; static char buf[128];
switch (ev->response_type & 0x7f) { switch (ev->response_type & 0x7f) {
@ -165,8 +162,6 @@ static inline const char *attr_pure ev_focus_detail_name(xcb_focus_in_event_t *e
return "Unknown"; return "Unknown";
} }
#undef CASESTRRET
static inline void ev_focus_in(session_t *ps, xcb_focus_in_event_t *ev) { static inline void ev_focus_in(session_t *ps, xcb_focus_in_event_t *ev) {
log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev), log_debug("{ mode: %s, detail: %s }\n", ev_focus_mode_name(ev),
ev_focus_detail_name(ev)); ev_focus_detail_name(ev));
@ -180,9 +175,8 @@ static inline void ev_focus_out(session_t *ps, xcb_focus_out_event_t *ev) {
} }
static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) { static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
if (ev->parent == ps->root) { assert(ev->parent == ps->root);
add_win_top(ps, ev->window); add_win_top(ps, ev->window);
}
} }
/// Handle configure event of a regular window /// Handle configure event of a regular window
@ -202,105 +196,122 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
auto mw = (struct managed_win *)w; auto mw = (struct managed_win *)w;
float t = get_time_ms(); float t = get_time_ms();
if (mw->oldX == -500 && mw->oldY == -500 && mw->oldW == 0 && mw->oldH == 0) { if (mw->oldX == -10000 && mw->oldY == -10000 && mw->oldW == 0 && mw->oldH == 0) {
if (!mw->isOld) { if (!mw->isOld) {
/* mw->isOld = true; */ /* mw->isOld = true; */
if (ps->o.spawn_center_screen) { if (ps->o.spawn_center_screen) {
mw->oldX = ps->root_width/2; mw->oldX = ps->root_width / 2;
mw->oldY = ps->root_height/2; mw->oldY = ps->root_height / 2;
mw->oldW = 1; mw->oldW = 1;
mw->oldH = 1; mw->oldH = 1;
} else if (ps->o.spawn_center) { } else if (ps->o.spawn_center) {
mw->oldX = ce->x + ce->width/2; mw->oldX = ce->x + ce->width / 2;
mw->oldY = ce->y + ce->height/2; mw->oldY = ce->y + ce->height / 2;
mw->oldW = 1; mw->oldW = 1;
mw->oldH = 1; mw->oldH = 1;
} else { } else {
mw->oldX = ce->x; mw->oldX = ce->x;
mw->oldY = ce->y; mw->oldY = ce->y;
mw->oldW = ce->width; mw->oldW = ce->width;
mw->oldH = ce->height; mw->oldH = ce->height;
} }
} else { } else {
mw->oldX = ce->x; mw->oldX = ce->x;
mw->oldY = ce->y; mw->oldY = ce->y;
mw->oldW = ce->width; mw->oldW = ce->width;
mw->oldH = ce->height; mw->oldH = ce->height;
} }
mw->newX = ce->x; mw->newX = ce->x;
mw->newY = ce->y; mw->newY = ce->y;
mw->newW = ce->width; mw->newW = ce->width;
mw->newH = ce->height; mw->newH = ce->height;
mw->moveTimeX = t; mw->moveTimeX = t;
mw->moveTimeY = t; mw->moveTimeY = t;
mw->moveTimeW = t; mw->moveTimeW = t;
mw->moveTimeH = t; mw->moveTimeH = t;
} else { } else {
if (mw->newX == mw->g.x && mw->newY == mw->g.y) { if (mw->newX == mw->g.x && mw->newY == mw->g.y) {
mw->oldX = mw->g.x; mw->oldX = mw->g.x;
mw->oldY = mw->g.y; mw->oldY = mw->g.y;
mw->oldW = mw->g.width; mw->oldW = mw->g.width;
mw->oldH = mw->g.height; mw->oldH = mw->g.height;
mw->moveTimeX = t; mw->moveTimeX = t;
mw->moveTimeY = t; mw->moveTimeY = t;
mw->moveTimeW = t; mw->moveTimeW = t;
mw->moveTimeH = t; mw->moveTimeH = t;
} }
if (mw->newX != ce->x || mw->newY != ce->y || mw->newW != ce->width || mw->newH != ce->height) { if (mw->newX != ce->x || mw->newY != ce->y || mw->newW != ce->width ||
float moveDx = ((float) t - mw->moveTimeX) / ps->o.transition_length; mw->newH != ce->height) {
float moveDy = ((float) t - mw->moveTimeY) / ps->o.transition_length; float moveDx = ((float)t - mw->moveTimeX) / ps->o.transition_length;
float moveDw = ((float) t - mw->moveTimeW) / ps->o.transition_length; float moveDy = ((float)t - mw->moveTimeY) / ps->o.transition_length;
float moveDh = ((float) t - mw->moveTimeH) / ps->o.transition_length; float moveDw = ((float)t - mw->moveTimeW) / ps->o.transition_length;
float moveDh = ((float)t - mw->moveTimeH) / ps->o.transition_length;
if (mw->moveTimeX != 0.0 && moveDx < 1.0 && mw->oldX != mw->newX) { if (mw->moveTimeX != 0.0 && moveDx < 1.0 && mw->oldX != mw->newX) {
float oldMoveDx = pow((float) (mw->newX - mw->g.x) / (float) (mw->newX - ce->x), 1 / ps->o.transition_pow_x); float oldMoveDx = pow(
float fakeT = (t - oldMoveDx * (float) ps->o.transition_length); (float)(mw->newX - mw->g.x) / (float)(mw->newX - ce->x),
/* printf("X: %f,%f\n", fakeT, t); */ 1 / ps->o.transition_pow_x);
mw->moveTimeX = isnanf(fakeT)? t : fakeT; float fakeT =
} else { (t - oldMoveDx * (float)ps->o.transition_length);
mw->moveTimeX = t; /* printf("X: %f,%f\n", fakeT, t); */
} mw->moveTimeX = isnanf(fakeT) ? t : fakeT;
if (mw->moveTimeY != 0.0 && moveDy < 1.0 && mw->oldY != mw->newY) { } else {
float oldMoveDy = pow((float) (mw->newY - mw->g.y) / (float) (mw->newY - ce->y), 1 / ps->o.transition_pow_y); mw->moveTimeX = t;
float fakeT = (t - oldMoveDy * (float) ps->o.transition_length); }
/* printf("Y: %f,%f\n", fakeT, t); */ if (mw->moveTimeY != 0.0 && moveDy < 1.0 && mw->oldY != mw->newY) {
mw->moveTimeY = isnanf(fakeT)? t : fakeT; float oldMoveDy = pow(
} else { (float)(mw->newY - mw->g.y) / (float)(mw->newY - ce->y),
mw->moveTimeY = t; 1 / ps->o.transition_pow_y);
} float fakeT =
if (mw->moveTimeW != 0.0 && moveDw < 1.0 && mw->oldW != mw->newW) { (t - oldMoveDy * (float)ps->o.transition_length);
float oldMoveDw = pow((float) (mw->newW - mw->g.width) / (float) (mw->newW - ce->width), 1 / ps->o.transition_pow_w); /* printf("Y: %f,%f\n", fakeT, t); */
float fakeT = (t - oldMoveDw * (float) ps->o.transition_length); mw->moveTimeY = isnanf(fakeT) ? t : fakeT;
/* printf("Y: %f,%f\n", fakeT, t); */ } else {
mw->moveTimeW = isnanf(fakeT)? t : fakeT; mw->moveTimeY = t;
} else { }
mw->moveTimeW = t; if (mw->moveTimeW != 0.0 && moveDw < 1.0 && mw->oldW != mw->newW) {
} float oldMoveDw = pow((float)(mw->newW - mw->g.width) /
if (mw->moveTimeH != 0.0 && moveDh < 1.0 && mw->oldH != mw->newH) { (float)(mw->newW - ce->width),
float oldMoveDh = pow((float) (mw->newH - mw->g.height) / (float) (mw->newH - ce->height), 1 / ps->o.transition_pow_h); 1 / ps->o.transition_pow_w);
float fakeT = (t - oldMoveDh * (float) ps->o.transition_length); float fakeT =
/* printf("Y: %f,%f\n", fakeT, t); */ (t - oldMoveDw * (float)ps->o.transition_length);
mw->moveTimeH = isnanf(fakeT)? t : fakeT; /* printf("Y: %f,%f\n", fakeT, t); */
} else { mw->moveTimeW = isnanf(fakeT) ? t : fakeT;
mw->moveTimeH = t; } else {
} mw->moveTimeW = t;
}
if (mw->moveTimeH != 0.0 && moveDh < 1.0 && mw->oldH != mw->newH) {
float oldMoveDh = pow((float)(mw->newH - mw->g.height) /
(float)(mw->newH - ce->height),
1 / ps->o.transition_pow_h);
float fakeT =
(t - oldMoveDh * (float)ps->o.transition_length);
/* printf("Y: %f,%f\n", fakeT, t); */
mw->moveTimeH = isnanf(fakeT) ? t : fakeT;
} else {
mw->moveTimeH = t;
}
mw->oldX = mw->newX; mw->oldX = mw->newX;
mw->oldY = mw->newY; mw->oldY = mw->newY;
mw->oldW = mw->newW; mw->oldW = mw->newW;
mw->oldH = mw->newH; mw->oldH = mw->newH;
mw->newX = ce->x; mw->newX = ce->x;
mw->newY = ce->y; mw->newY = ce->y;
mw->newW = ce->width; mw->newW = ce->width;
mw->newH = ce->height; mw->newH = ce->height;
if (ps->o.no_scale_down && mw->newW < mw->oldW) { mw->oldW = mw->newW; } if (ps->o.no_scale_down && mw->newW < mw->oldW) {
if (ps->o.no_scale_down && mw->newH < mw->oldH) { mw->oldH = mw->newH; } mw->oldW = mw->newW;
} }
} if (ps->o.no_scale_down && mw->newH < mw->oldH) {
mw->oldH = mw->newH;
}
}
}
if (mw->state == WSTATE_UNMAPPED || mw->state == WSTATE_UNMAPPING || if (mw->state == WSTATE_UNMAPPED || mw->state == WSTATE_UNMAPPING ||
mw->state == WSTATE_DESTROYING) { mw->state == WSTATE_DESTROYING) {
@ -353,7 +364,7 @@ static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event
log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }", log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
ev->event, ev->window, ev->above_sibling, ev->override_redirect); ev->event, ev->window, ev->above_sibling, ev->override_redirect);
if (ev->window == ps->root) { if (ev->window == ps->root) {
set_root_flags(ps, ROOT_FLAGS_CONFIGURED); configure_root(ps, ev->width, ev->height);
} else { } else {
configure_win(ps, ev); configure_win(ps, ev);
} }
@ -361,23 +372,8 @@ static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event
static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) { static inline void ev_destroy_notify(session_t *ps, xcb_destroy_notify_event_t *ev) {
auto w = find_win(ps, ev->window); auto w = find_win(ps, ev->window);
auto mw = find_toplevel(ps, ev->window); if (w) {
if (mw && mw->client_win == mw->base.id) {
// We only want _real_ frame window
assert(&mw->base == w);
mw = NULL;
}
assert(w == NULL || mw == NULL);
if (w != NULL) {
auto _ attr_unused = destroy_win_start(ps, w); auto _ attr_unused = destroy_win_start(ps, w);
} else if (mw != NULL) {
win_unmark_client(ps, mw);
win_set_flags(mw, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
} else {
log_debug("Received a destroy notify from an unknown window, %#010x",
ev->window);
} }
} }
@ -400,7 +396,7 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
return; return;
} }
win_set_flags(w, WIN_FLAGS_MAPPED); win_queue_update(w, WIN_UPDATE_MAP);
// FocusIn/Out may be ignored when the window is unmapped, so we must // FocusIn/Out may be ignored when the window is unmapped, so we must
// recheck focus here // recheck focus here
@ -415,14 +411,8 @@ static inline void ev_unmap_notify(session_t *ps, xcb_unmap_notify_event_t *ev)
} }
static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) { static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t *ev) {
log_debug("Window %#010x has new parent: %#010x, override_redirect: %d", log_debug("{ new_parent: %#010x, override_redirect: %d }", ev->parent,
ev->window, ev->parent, ev->override_redirect); ev->override_redirect);
auto w_top = find_toplevel(ps, ev->window);
if (w_top) {
win_unmark_client(ps, w_top);
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
}
if (ev->parent == ps->root) { if (ev->parent == ps->root) {
// X will generate reparent notifiy even if the parent didn't actually // X will generate reparent notifiy even if the parent didn't actually
@ -442,11 +432,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
{ {
auto w = find_win(ps, ev->window); auto w = find_win(ps, ev->window);
if (w) { if (w) {
auto ret = destroy_win_start(ps, w); auto _ attr_unused = destroy_win_start(ps, w);
if (!ret && w->managed) {
auto mw = (struct managed_win *)w;
CHECK(win_skip_fading(ps, mw));
}
} }
} }
@ -455,35 +441,28 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
ps->c, ev->window, XCB_CW_EVENT_MASK, ps->c, ev->window, XCB_CW_EVENT_MASK,
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)}); (const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)});
if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) { // Check if the window is an undetected client window
log_debug("Window %#010x doesn't have WM_STATE property, it is " // Firstly, check if it's a known client window
"probably not a client window. But we will listen for " if (!find_toplevel(ps, ev->window)) {
"property change in case it gains one.", // If not, look for its frame window
ev->window); auto w_top = find_toplevel2(ps, ev->parent);
xcb_change_window_attributes( // If found, and the client window has not been determined, or its
ps->c, ev->window, XCB_CW_EVENT_MASK, // frame may not have a correct client, continue
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) | if (w_top &&
XCB_EVENT_MASK_PROPERTY_CHANGE}); (!w_top->client_win || w_top->client_win == w_top->base.id)) {
} else { // If it has WM_STATE, mark it the client window
auto w_real_top = find_managed_window_or_parent(ps, ev->parent); if (wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
if (w_real_top && w_real_top->state != WSTATE_UNMAPPED && w_top->wmwin = false;
w_real_top->state != WSTATE_UNMAPPING) { win_unmark_client(ps, w_top);
log_debug("Mark window %#010x (%s) as having a stale " win_mark_client(ps, w_top, ev->window);
"client", }
w_real_top->base.id, w_real_top->name); // Otherwise, watch for WM_STATE on it
win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
ps->pending_updates = true;
} else {
if (!w_real_top)
log_debug("parent %#010x not found", ev->parent);
else { else {
// Window is not currently mapped, unmark its xcb_change_window_attributes(
// client to trigger a client recheck when it is ps->c, ev->window, XCB_CW_EVENT_MASK,
// mapped later. (const uint32_t[]){
win_unmark_client(ps, w_real_top); determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
log_debug("parent %#010x (%s) is in state %d", XCB_EVENT_MASK_PROPERTY_CHANGE});
w_real_top->base.id, w_real_top->name,
w_real_top->state);
} }
} }
} }
@ -576,12 +555,14 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
(const uint32_t[]){determine_evmask( (const uint32_t[]){determine_evmask(
ps, ev->window, WIN_EVMODE_UNKNOWN)}); ps, ev->window, WIN_EVMODE_UNKNOWN)});
auto w_top = find_managed_window_or_parent(ps, ev->window); auto w_top = find_toplevel2(ps, ev->window);
// ev->window might have not been managed yet, in that case w_top // Initialize client_win as early as possible
// would be NULL. if (w_top &&
if (w_top) { (!w_top->client_win || w_top->client_win == w_top->base.id) &&
win_set_flags(w_top, WIN_FLAGS_CLIENT_STALE); wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
ps->pending_updates = true; w_top->wmwin = false;
win_unmark_client(ps, w_top);
win_mark_client(ps, w_top, ev->window);
} }
} }
} }
@ -606,7 +587,14 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
win_update_opacity_prop(ps, w); win_update_opacity_prop(ps, w);
// we cannot receive OPACITY change when window is destroyed // we cannot receive OPACITY change when window is destroyed
assert(w->state != WSTATE_DESTROYING); assert(w->state != WSTATE_DESTROYING);
win_update_opacity_target(ps, w); w->opacity_target = win_calc_opacity_target(ps, w, false);
if (w->state == WSTATE_MAPPED) {
// See the winstate_t transition table
w->state = WSTATE_FADING;
}
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
} }
} }
@ -743,14 +731,14 @@ static inline void ev_shape_notify(session_t *ps, xcb_shape_notify_event_t *ev)
* if we attempt to rebuild border_size * if we attempt to rebuild border_size
*/ */
// Mark the old border_size as damaged // Mark the old border_size as damaged
region_t tmp = win_get_bounding_shape_global_by_val(w); region_t tmp = win_get_bounding_shape_global_by_val(w, true);
add_damage(ps, &tmp); add_damage(ps, &tmp);
pixman_region32_fini(&tmp); pixman_region32_fini(&tmp);
win_update_bounding_shape(ps, w); win_update_bounding_shape(ps, w);
// Mark the new border_size as damaged // Mark the new border_size as damaged
tmp = win_get_bounding_shape_global_by_val(w); tmp = win_get_bounding_shape_global_by_val(w, true);
add_damage(ps, &tmp); add_damage(ps, &tmp);
pixman_region32_fini(&tmp); pixman_region32_fini(&tmp);

View File

@ -16,7 +16,7 @@ cflags = []
required_xcb_packages = [ required_xcb_packages = [
'xcb-render', 'xcb-damage', 'xcb-randr', 'xcb-sync', 'xcb-composite', 'xcb-render', 'xcb-damage', 'xcb-randr', 'xcb-sync', 'xcb-composite',
'xcb-shape', 'xcb-xinerama', 'xcb-xfixes', 'xcb-present', 'xcb-glx', 'xcb' 'xcb-shape', 'xcb-xinerama', 'xcb-xfixes', 'xcb-present', 'xcb'
] ]
required_packages = [ required_packages = [
@ -81,8 +81,8 @@ endif
host_system = host_machine.system() host_system = host_machine.system()
if host_system == 'linux' if host_system == 'linux'
cflags += ['-DHAS_INOTIFY'] cflags += ['-DHAS_INOTIFY']
elif (host_system == 'freebsd' or host_system == 'netbsd' or elif host_system == 'freebsd' or host_system == 'netbsd' or
host_system == 'dragonfly' or host_system == 'openbsd') host_system == 'dragonfly' or host_system == 'openbsd'
cflags += ['-DHAS_KQUEUE'] cflags += ['-DHAS_KQUEUE']
endif endif

File diff suppressed because it is too large Load Diff

View File

@ -36,10 +36,40 @@ typedef struct {
GLint unifm_offset_x; GLint unifm_offset_x;
/// Location of uniform "offset_y" in blur GLSL program. /// Location of uniform "offset_y" in blur GLSL program.
GLint unifm_offset_y; GLint unifm_offset_y;
/// Location of uniform "factor_center" in blur GLSL program. /// Location of uniform "opacity" in conv-blur and (dual filter) kawase-blur GLSL program.
GLint unifm_factor_center; GLint unifm_opacity;
/// Location of uniform "offset" in kawase-blur GLSL program.
GLint unifm_offset;
/// Location of uniform "halfpixel" in kawase-blur GLSL program.
GLint unifm_halfpixel;
/// Location of uniform "fulltex" in kawase-blur GLSL program.
GLint unifm_fulltex;
} glx_blur_pass_t; } glx_blur_pass_t;
typedef struct {
/// Fragment shader for rounded corners.
GLuint frag_shader;
/// GLSL program for rounded corners.
GLuint prog;
/// Location of uniform "radius" in rounded-corners GLSL program.
GLint unifm_radius;
/// Location of uniform "texcoord" in rounded-corners GLSL program.
GLint unifm_texcoord;
/// Location of uniform "texsize" in rounded-corners GLSL program.
GLint unifm_texsize;
/// Location of uniform "borderw" in rounded-corners GLSL program.
GLint unifm_borderw;
/// Location of uniform "borderc" in rounded-corners GLSL program.
GLint unifm_borderc;
/// Location of uniform "resolution" in rounded-corners GLSL program.
GLint unifm_resolution;
/// Location of uniform "texture_scr" in rounded-corners GLSL program.
GLint unifm_tex_scr;
/// Location of uniform "texture_wnd" in rounded-corners GLSL program.
GLint unifm_tex_wnd;
} glx_round_pass_t;
/// Structure containing GLX-dependent data for a session. /// Structure containing GLX-dependent data for a session.
typedef struct glx_session { typedef struct glx_session {
// === OpenGL related === // === OpenGL related ===
@ -49,7 +79,10 @@ typedef struct glx_session {
bool has_texture_non_power_of_two; bool has_texture_non_power_of_two;
/// Current GLX Z value. /// Current GLX Z value.
int z; int z;
/// Cached blur textures for every pass
glx_blur_cache_t blur_cache;
glx_blur_pass_t *blur_passes; glx_blur_pass_t *blur_passes;
glx_round_pass_t *round_passes;
} glx_session_t; } glx_session_t;
/// @brief Wrapper of a binded GLX texture. /// @brief Wrapper of a binded GLX texture.
@ -69,8 +102,8 @@ typedef struct _glx_texture {
bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z, bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
GLfloat factor, const region_t *reg_tgt); GLfloat factor, const region_t *reg_tgt);
bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx, int dy, bool glx_render(session_t *ps, struct managed_win *w, const glx_texture_t *ptex, int x, int y, int dx, int dy,
int width, int height, int z, double opacity, bool argb, bool neg, int width, int height, int z, double opacity, bool argb, bool neg, int cr,
const region_t *reg_tgt, const glx_prog_main_t *pprogram); const region_t *reg_tgt, const glx_prog_main_t *pprogram);
bool glx_init(session_t *ps, bool need_render); bool glx_init(session_t *ps, bool need_render);
@ -81,6 +114,8 @@ void glx_on_root_change(session_t *ps);
bool glx_init_blur(session_t *ps); bool glx_init_blur(session_t *ps);
bool glx_init_rounded_corners(session_t *ps);
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
bool glx_load_prog_main(const char *vshader_str, const char *fshader_str, bool glx_load_prog_main(const char *vshader_str, const char *fshader_str,
glx_prog_main_t *pprogram); glx_prog_main_t *pprogram);
@ -91,6 +126,11 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
void glx_release_pixmap(session_t *ps, glx_texture_t *ptex); void glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
bool glx_bind_texture(session_t *ps, glx_texture_t **pptex,
int x, int y, int width, int height, bool repeat);
void glx_release_texture(session_t *ps, glx_texture_t **ptex);
void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2); void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2);
/** /**
@ -103,7 +143,15 @@ static inline bool glx_tex_binded(const glx_texture_t *ptex, xcb_pixmap_t pixmap
void glx_set_clip(session_t *ps, const region_t *reg); void glx_set_clip(session_t *ps, const region_t *reg);
bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z, bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
GLfloat factor_center, const region_t *reg_tgt, glx_blur_cache_t *pbc); double opacity, const region_t *reg_tgt, glx_blur_cache_t *pbc);
bool glx_round_corners_dst0(session_t *ps, struct managed_win *w, const glx_texture_t *ptex, int shader_idx,
int dx, int dy, int width, int height, float z, float cr,
const region_t *reg_tgt, glx_blur_cache_t *pbc);
bool glx_round_corners_dst1(session_t *ps, struct managed_win *w, const glx_texture_t *ptex, int shader_idx,
int dx, int dy, int width, int height, float z, float cr,
const region_t *reg_tgt, glx_blur_cache_t *pbc);
GLuint glx_create_shader(GLenum shader_type, const char *shader_str); GLuint glx_create_shader(GLenum shader_type, const char *shader_str);
@ -157,17 +205,19 @@ static inline void free_glx_fbo(GLuint *pfbo) {
* Free data in glx_blur_cache_t on resize. * Free data in glx_blur_cache_t on resize.
*/ */
static inline void free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) { static inline void free_glx_bc_resize(session_t *ps, glx_blur_cache_t *pbc) {
free_texture_r(ps, &pbc->textures[0]); for (int i = 0; i < MAX_BLUR_PASS; i++) {
free_texture_r(ps, &pbc->textures[1]); free_texture_r(ps, &pbc->textures[i]);
pbc->width = 0; pbc->width[i] = 0;
pbc->height = 0; pbc->height[i] = 0;
}
} }
/** /**
* Free a glx_blur_cache_t * Free a glx_blur_cache_t
*/ */
static inline void free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) { static inline void free_glx_bc(session_t *ps, glx_blur_cache_t *pbc) {
free_glx_fbo(&pbc->fbo); for (int i = 0; i < MAX_BLUR_PASS; i++)
free_glx_fbo(&pbc->fbos[i]);
free_glx_bc_resize(ps, pbc); free_glx_bc_resize(ps, pbc);
} }
@ -196,7 +246,6 @@ static inline void free_texture(session_t *ps, glx_texture_t **pptex) {
*/ */
static inline void free_paint_glx(session_t *ps, paint_t *ppaint) { static inline void free_paint_glx(session_t *ps, paint_t *ppaint) {
free_texture(ps, &ppaint->ptex); free_texture(ps, &ppaint->ptex);
ppaint->fbcfg = NULL;
} }
/** /**
@ -207,6 +256,8 @@ static inline void free_win_res_glx(session_t *ps, struct managed_win *w) {
free_paint_glx(ps, &w->shadow_paint); free_paint_glx(ps, &w->shadow_paint);
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
free_glx_bc(ps, &w->glx_blur_cache); free_glx_bc(ps, &w->glx_blur_cache);
free_glx_bc(ps, &w->glx_round_cache);
free_texture(ps, &w->glx_texture_bg);
free(w->paint.fbcfg); free(w->paint.fbcfg);
#endif #endif
} }

View File

@ -114,6 +114,18 @@ static void usage(const char *argv0, int ret) {
"--active-opacity opacity\n" "--active-opacity opacity\n"
" Default opacity for active windows. (0.0 - 1.0)\n" " Default opacity for active windows. (0.0 - 1.0)\n"
"\n" "\n"
"--corner-radius value\n"
" Round the corners of windows. (defaults to 0)\n"
"\n"
"--rounded-corners-exclude condition\n"
" Exclude conditions for rounded corners.\n"
"\n"
"--round-borders value\n"
" When rounding corners, round the borders of windows. (defaults to 1)\n"
"\n"
"--round-borders-exclude condition\n"
" Exclude conditions for rounding borders.\n"
"\n"
"--mark-wmwin-focused\n" "--mark-wmwin-focused\n"
" Try to detect WM windows and mark them as active.\n" " Try to detect WM windows and mark them as active.\n"
"\n" "\n"
@ -202,8 +214,8 @@ static void usage(const char *argv0, int ret) {
"\n" "\n"
"--blur-method\n" "--blur-method\n"
" The algorithm used for background bluring. Available choices are:\n" " The algorithm used for background bluring. Available choices are:\n"
" 'none' to disable, 'gaussian', 'box' or 'kernel' for custom\n" " 'none' to disable, 'dual_kawase', 'gaussian', 'box' or 'kernel'\n"
" convolution blur with --blur-kern.\n" " for custom convolution blur with --blur-kern.\n"
" Note: 'gaussian' and 'box' require --experimental-backends.\n" " Note: 'gaussian' and 'box' require --experimental-backends.\n"
"\n" "\n"
"--blur-size\n" "--blur-size\n"
@ -211,6 +223,10 @@ static void usage(const char *argv0, int ret) {
"\n" "\n"
"--blur-deviation\n" "--blur-deviation\n"
" The standard deviation for the 'gaussian' blur method.\n" " The standard deviation for the 'gaussian' blur method.\n"
"\n"
"--blur-strength\n"
" Only valid for '--blur-method dual_kawase'!\n"
" The strength of the kawase blur as an integer between 1 and 20. Defaults to 5.\n"
"\n" "\n"
"--blur-background\n" "--blur-background\n"
" Blur background of semi-transparent / ARGB windows. Bad in\n" " Blur background of semi-transparent / ARGB windows. Bad in\n"
@ -227,6 +243,7 @@ static void usage(const char *argv0, int ret) {
" opacity.\n" " opacity.\n"
"\n" "\n"
"--blur-kern matrix\n" "--blur-kern matrix\n"
" Only valid for '--blur-method convolution'!\n"
" Specify the blur convolution kernel, with the following format:\n" " Specify the blur convolution kernel, with the following format:\n"
" WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n" " WIDTH,HEIGHT,ELE1,ELE2,ELE3,ELE4,ELE5...\n"
" The element in the center must not be included, it will be forever\n" " The element in the center must not be included, it will be forever\n"
@ -398,8 +415,10 @@ static const struct option longopts[] = {
{"opengl", no_argument, NULL, 289}, {"opengl", no_argument, NULL, 289},
{"backend", required_argument, NULL, 290}, {"backend", required_argument, NULL, 290},
{"glx-no-stencil", no_argument, NULL, 291}, {"glx-no-stencil", no_argument, NULL, 291},
{"glx-copy-from-front", no_argument, NULL, 292},
{"benchmark", required_argument, NULL, 293}, {"benchmark", required_argument, NULL, 293},
{"benchmark-wid", required_argument, NULL, 294}, {"benchmark-wid", required_argument, NULL, 294},
{"glx-use-copysubbuffermesa", no_argument, NULL, 295},
{"blur-background-exclude", required_argument, NULL, 296}, {"blur-background-exclude", required_argument, NULL, 296},
{"active-opacity", required_argument, NULL, 297}, {"active-opacity", required_argument, NULL, 297},
{"glx-no-rebind-pixmap", no_argument, NULL, 298}, {"glx-no-rebind-pixmap", no_argument, NULL, 298},
@ -435,6 +454,11 @@ static const struct option longopts[] = {
{"blur-method", required_argument, NULL, 328}, {"blur-method", required_argument, NULL, 328},
{"blur-size", required_argument, NULL, 329}, {"blur-size", required_argument, NULL, 329},
{"blur-deviation", required_argument, NULL, 330}, {"blur-deviation", required_argument, NULL, 330},
{"blur-strength", required_argument, NULL, 331},
{"corner-radius", required_argument, NULL, 332},
{"rounded-corners-exclude", required_argument, NULL, 333},
{"round-borders", required_argument, NULL, 334},
{"round-borders-exclude", required_argument, NULL, 335},
{"experimental-backends", no_argument, NULL, 733}, {"experimental-backends", no_argument, NULL, 733},
{"monitor-repaint", no_argument, NULL, 800}, {"monitor-repaint", no_argument, NULL, 800},
{"diagnostics", no_argument, NULL, 801}, {"diagnostics", no_argument, NULL, 801},
@ -467,23 +491,21 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
} else if (o == 'b') { } else if (o == 'b') {
*fork = true; *fork = true;
} else if (o == 'd') { } else if (o == 'd') {
log_error("-d is removed, please use the DISPLAY " log_warn("-d will be ignored, please use the DISPLAY "
"environment variable"); "environment variable");
goto err;
} else if (o == 314) { } else if (o == 314) {
*all_xerrors = true; *all_xerrors = true;
} else if (o == 318) { } else if (o == 318) {
printf("%s\n", COMPTON_VERSION); printf("%s\n", COMPTON_VERSION);
return true; return true;
} else if (o == 'S') { } else if (o == 'S') {
log_error("-S is no longer available"); log_warn("-S will be ignored");
goto err;
} else if (o == 320) { } else if (o == 320) {
log_error("--no-name-pixmap is no longer available"); log_warn("--no-name-pixmap will be ignored");
goto err;
} else if (o == '?' || o == ':') { } else if (o == '?' || o == ':') {
usage(argv[0], 1); usage(argv[0], 1);
goto err; *exit_code = 1;
return true;
} }
} }
@ -491,13 +513,11 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
if (optind < argc) { if (optind < argc) {
// log is not initialized here yet // log is not initialized here yet
fprintf(stderr, "picom doesn't accept positional arguments.\n"); fprintf(stderr, "picom doesn't accept positional arguments.\n");
goto err; *exit_code = 1;
return true;
} }
return false; return false;
err:
*exit_code = 1;
return true;
} }
/** /**
@ -516,10 +536,9 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// Parse commandline arguments. Range checking will be done later. // Parse commandline arguments. Range checking will be done later.
const char *deprecation_message attr_unused = const char *deprecation_message = "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 " "open a bug report.";
"open a bug report.";
optind = 1; optind = 1;
while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) { while (-1 != (o = getopt_long(argc, argv, shortopts, longopts, &longopt_idx))) {
switch (o) { switch (o) {
@ -645,14 +664,14 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
break; break;
case 271: case 271:
// --alpha-step // --alpha-step
log_error("--alpha-step has been removed, we now tries to " log_warn("--alpha-step has been removed, we now tries to "
"make use of all alpha values"); "make use of all alpha values");
return false; break;
case 272: log_error("use of --dbe is deprecated"); return false; case 272: log_warn("use of --dbe is deprecated"); break;
case 273: case 273:
log_error("--paint-on-overlay has been removed, the feature is enabled " log_warn("--paint-on-overlay has been removed, and is enabled "
"whenever possible"); "when possible");
return false; break;
P_CASEBOOL(274, sw_opti); P_CASEBOOL(274, sw_opti);
case 275: case 275:
// --vsync-aggressive // --vsync-aggressive
@ -704,11 +723,19 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
exit(1); exit(1);
break; break;
P_CASEBOOL(291, glx_no_stencil); P_CASEBOOL(291, glx_no_stencil);
case 292:
log_error("--glx-copy-from-front %s", deprecation_message);
exit(1);
break;
P_CASEINT(293, benchmark); P_CASEINT(293, benchmark);
case 294: case 294:
// --benchmark-wid // --benchmark-wid
opt->benchmark_wid = (xcb_window_t)strtol(optarg, NULL, 0); opt->benchmark_wid = (xcb_window_t)strtol(optarg, NULL, 0);
break; break;
case 295:
log_error("--glx-use-copysubbuffermesa %s", deprecation_message);
exit(1);
break;
case 296: case 296:
// --blur-background-exclude // --blur-background-exclude
condlst_add(&opt->blur_background_blacklist, optarg); condlst_add(&opt->blur_background_blacklist, optarg);
@ -787,8 +814,9 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
P_CASEBOOL(311, vsync_use_glfinish); P_CASEBOOL(311, vsync_use_glfinish);
case 312: case 312:
// --xrender-sync // --xrender-sync
log_error("Please use --xrender-sync-fence instead of --xrender-sync"); log_warn("Please use --xrender-sync-fence instead of --xrender-sync");
return false; opt->xrender_sync_fence = true;
break;
P_CASEBOOL(313, xrender_sync_fence); P_CASEBOOL(313, xrender_sync_fence);
P_CASEBOOL(315, no_fading_destroyed_argb); P_CASEBOOL(315, no_fading_destroyed_argb);
P_CASEBOOL(316, force_win_blend); P_CASEBOOL(316, force_win_blend);
@ -835,7 +863,15 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
// --blur-deviation // --blur-deviation
opt->blur_deviation = atof(optarg); opt->blur_deviation = atof(optarg);
break; break;
case 331:
// --blur-strength
opt->blur_strength = parse_kawase_blur_strength(atoi(optarg));
break;
case 332: opt->corner_radius = atoi(optarg); break;
case 333: condlst_add(&opt->rounded_corners_blacklist, optarg); break;
case 334: opt->round_borders = atoi(optarg); break;
case 335: condlst_add(&opt->round_borders_blacklist, optarg); break;
P_CASEBOOL(733, experimental_backends); P_CASEBOOL(733, experimental_backends);
P_CASEBOOL(800, monitor_repaint); P_CASEBOOL(800, monitor_repaint);
case 801: opt->print_diagnostics = true; break; case 801: opt->print_diagnostics = true; break;
@ -915,6 +951,13 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
opt->track_leader = true; opt->track_leader = true;
} }
// Blur method kawase is not compatible with the xrender backend
if (opt->backend != BKEND_GLX && (opt->blur_method == BLUR_METHOD_DUAL_KAWASE
|| opt->blur_method == BLUR_METHOD_ALT_KAWASE)) {
log_warn("Blur method 'kawase' is incompatible with the XRender backend. Fall back to default.\n");
opt->blur_method = BLUR_METHOD_KERNEL;
}
// Fill default blur kernel // Fill default blur kernel
if (opt->blur_method == BLUR_METHOD_KERNEL && if (opt->blur_method == BLUR_METHOD_KERNEL &&
(!opt->blur_kerns || !opt->blur_kerns[0])) { (!opt->blur_kerns || !opt->blur_kerns[0])) {
@ -924,6 +967,15 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
CHECK(opt->blur_kernel_count); CHECK(opt->blur_kernel_count);
} }
// override blur_kernel_count for kawase
if (opt->blur_method == BLUR_METHOD_DUAL_KAWASE ||
opt->blur_method == BLUR_METHOD_ALT_KAWASE) {
opt->blur_kernel_count = MAX_BLUR_PASS;
opt->blur_kerns = ccalloc(opt->blur_kernel_count, struct conv *);
CHECK(opt->blur_kerns);
CHECK(opt->blur_kernel_count);
}
if (opt->resize_damage < 0) { if (opt->resize_damage < 0) {
log_warn("Negative --resize-damage will not work correctly."); log_warn("Negative --resize-damage will not work correctly.");
} }

View File

@ -19,7 +19,6 @@
#include <string.h> #include <string.h>
#include <xcb/composite.h> #include <xcb/composite.h>
#include <xcb/damage.h> #include <xcb/damage.h>
#include <xcb/glx.h>
#include <xcb/present.h> #include <xcb/present.h>
#include <xcb/randr.h> #include <xcb/randr.h>
#include <xcb/render.h> #include <xcb/render.h>
@ -99,9 +98,7 @@ const char *const BACKEND_STRS[] = {[BKEND_XRENDER] = "xrender",
session_t *ps_g = NULL; session_t *ps_g = NULL;
void set_root_flags(session_t *ps, uint64_t flags) { void set_root_flags(session_t *ps, uint64_t flags) {
log_debug("Setting root flags: %lu", flags);
ps->root_flags |= flags; ps->root_flags |= flags;
ps->pending_updates = true;
} }
void quit(session_t *ps) { void quit(session_t *ps) {
@ -124,16 +121,15 @@ static inline void free_xinerama_info(session_t *ps) {
} }
/** /**
* Get current system clock in 40micro second accuracy. * Get current system clock in milliseconds.
*/ */
int64_t get_time_ms(void) { int64_t get_time_ms(void) {
struct timespec tp; struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp); clock_gettime(CLOCK_MONOTONIC, &tp);
/* return (int64_t)tp.tv_sec * 1000 + (int64_t)tp.tv_nsec / 1000000; //ultra high refresh */ // return (int64_t)tp.tv_sec * 1000 + (int64_t)tp.tv_nsec / 1000000;
return (int64_t)tp.tv_sec * 100 + (int64_t)tp.tv_nsec / 250000; //ultra high refresh return (int64_t)tp.tv_sec * 100 + (int64_t)tp.tv_nsec / 250000;
} }
// XXX Move to x.c // XXX Move to x.c
void cxinerama_upd_scrs(session_t *ps) { void cxinerama_upd_scrs(session_t *ps) {
// XXX Consider deprecating Xinerama, switch to RandR when necessary // XXX Consider deprecating Xinerama, switch to RandR when necessary
@ -182,7 +178,7 @@ static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t
if (!w) if (!w)
w = find_toplevel(ps, wid); w = find_toplevel(ps, wid);
if (!w) if (!w)
w = find_managed_window_or_parent(ps, wid); w = find_toplevel2(ps, wid);
return w; return w;
} }
@ -305,9 +301,9 @@ uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode) {
struct managed_win *w = NULL; struct managed_win *w = NULL;
// Check if it's a mapped frame window // Check if it's a mapped frame window
if (mode == WIN_EVMODE_FRAME || if (WIN_EVMODE_FRAME == mode ||
((w = find_managed_win(ps, wid)) && w->a.map_state == XCB_MAP_STATE_VIEWABLE)) { ((w = find_managed_win(ps, wid)) && w->a.map_state == XCB_MAP_STATE_VIEWABLE)) {
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
if (!ps->o.use_ewmh_active_win) { if (!ps->o.use_ewmh_active_win) {
evmask |= XCB_EVENT_MASK_FOCUS_CHANGE; evmask |= XCB_EVENT_MASK_FOCUS_CHANGE;
} }
@ -377,219 +373,31 @@ static void recheck_focus(session_t *ps) {
} }
/** /**
* Rebuild cached <code>screen_reg</code>. * Look for the client window of a particular window.
*/ */
static void rebuild_screen_reg(session_t *ps) { xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
get_screen_region(ps, &ps->screen_reg); if (wid_has_prop(ps, w, ps->atoms->aWM_STATE)) {
} return w;
/**
* Rebuild <code>shadow_exclude_reg</code>.
*/
static void rebuild_shadow_exclude_reg(session_t *ps) {
bool ret = parse_geometry(ps, ps->o.shadow_exclude_reg_str, &ps->shadow_exclude_reg);
if (!ret)
exit(1);
}
/// Free up all the images and deinit the backend
static void destroy_backend(session_t *ps) {
win_stack_foreach_managed_safe(w, &ps->window_stack) {
// Wrapping up fading in progress
if (win_skip_fading(ps, w)) {
// `w` is freed by win_skip_fading
continue;
}
if (ps->backend_data) {
if (w->state == WSTATE_MAPPED) {
win_release_images(ps->backend_data, w);
} else {
assert(!w->win_image);
assert(!w->shadow_image);
}
}
free_paint(ps, &w->paint);
} }
if (ps->backend_data && ps->root_image) { xcb_query_tree_reply_t *reply =
ps->backend_data->ops->release_image(ps->backend_data, ps->root_image); xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
ps->root_image = NULL; if (!reply)
return 0;
xcb_window_t *children = xcb_query_tree_children(reply);
int nchildren = xcb_query_tree_children_length(reply);
int i;
xcb_window_t ret = 0;
for (i = 0; i < nchildren; ++i) {
if ((ret = find_client_win(ps, children[i])))
break;
} }
if (ps->backend_data) { free(reply);
// deinit backend
if (ps->backend_blur_context) {
ps->backend_data->ops->destroy_blur_context(
ps->backend_data, ps->backend_blur_context);
ps->backend_blur_context = NULL;
}
ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL;
}
}
static bool initialize_blur(session_t *ps) { return ret;
struct kernel_blur_args kargs;
struct gaussian_blur_args gargs;
struct box_blur_args bargs;
void *args = NULL;
switch (ps->o.blur_method) {
case BLUR_METHOD_BOX:
bargs.size = ps->o.blur_radius;
args = (void *)&bargs;
break;
case BLUR_METHOD_KERNEL:
kargs.kernel_count = ps->o.blur_kernel_count;
kargs.kernels = ps->o.blur_kerns;
args = (void *)&kargs;
break;
case BLUR_METHOD_GAUSSIAN:
gargs.size = ps->o.blur_radius;
gargs.deviation = ps->o.blur_deviation;
args = (void *)&gargs;
break;
default: return true;
}
ps->backend_blur_context = ps->backend_data->ops->create_blur_context(
ps->backend_data, ps->o.blur_method, args);
return ps->backend_blur_context != NULL;
}
/// Init the backend and bind all the window pixmap to backend images
static bool initialize_backend(session_t *ps) {
if (ps->o.experimental_backends) {
assert(!ps->backend_data);
// Reinitialize win_data
assert(backend_list[ps->o.backend]);
ps->backend_data = backend_list[ps->o.backend]->init(ps);
if (!ps->backend_data) {
log_fatal("Failed to initialize backend, aborting...");
quit(ps);
return false;
}
ps->backend_data->ops = backend_list[ps->o.backend];
if (!initialize_blur(ps)) {
log_fatal("Failed to prepare for background blur, aborting...");
ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL;
quit(ps);
return false;
}
// window_stack shouldn't include window that's
// not in the hash table at this point. Since
// there cannot be any fading windows.
HASH_ITER2(ps->windows, _w) {
if (!_w->managed) {
continue;
}
auto w = (struct managed_win *)_w;
assert(w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED);
if (w->state == WSTATE_MAPPED) {
// We need to reacquire image
log_debug("Marking window %#010x (%s) for update after "
"redirection",
w->base.id, w->name);
if (w->shadow) {
struct color c = {
.red = ps->o.shadow_red,
.green = ps->o.shadow_green,
.blue = ps->o.shadow_blue,
.alpha = ps->o.shadow_opacity,
};
win_bind_shadow(ps->backend_data, w, c,
ps->gaussian_map);
}
w->flags |= WIN_FLAGS_PIXMAP_STALE;
ps->pending_updates = true;
}
}
}
// The old backends binds pixmap lazily, nothing to do here
return true;
}
/// Handle configure event of the root window
static void configure_root(session_t *ps) {
auto r = XCB_AWAIT(xcb_get_geometry, ps->c, ps->root);
if (!r) {
log_fatal("Failed to fetch root geometry");
abort();
}
log_info("Root configuration changed, new geometry: %dx%d", r->width, r->height);
bool has_root_change = false;
if (ps->redirected) {
// On root window changes
if (ps->o.experimental_backends) {
assert(ps->backend_data);
has_root_change = ps->backend_data->ops->root_change != NULL;
} else {
// Old backend can handle root change
has_root_change = true;
}
if (!has_root_change) {
// deinit/reinit backend and free up resources if the backend
// cannot handle root change
destroy_backend(ps);
}
free_paint(ps, &ps->tgt_buffer);
}
ps->root_width = r->width;
ps->root_height = r->height;
rebuild_screen_reg(ps);
rebuild_shadow_exclude_reg(ps);
// Invalidate reg_ignore from the top
auto top_w = win_stack_find_next_managed(ps, &ps->window_stack);
if (top_w) {
rc_region_unref(&top_w->reg_ignore);
top_w->reg_ignore_valid = false;
}
if (ps->redirected) {
for (int i = 0; i < ps->ndamage; i++) {
pixman_region32_clear(&ps->damage_ring[i]);
}
ps->damage = ps->damage_ring + ps->ndamage - 1;
#ifdef CONFIG_OPENGL
// GLX root change callback
if (BKEND_GLX == ps->o.backend && !ps->o.experimental_backends) {
glx_on_root_change(ps);
}
#endif
if (has_root_change) {
if (ps->backend_data != NULL) {
ps->backend_data->ops->root_change(ps->backend_data, ps);
}
// Old backend's root_change is not a specific function
} else {
if (!initialize_backend(ps)) {
log_fatal("Failed to re-initialize backend after root "
"change, aborting...");
ps->quit = true;
// TODO only event handlers should request ev_break,
// otherwise it's too hard to keep track of what can break
// the event loop
ev_break(ps->loop, EVBREAK_ALL);
return;
}
// Re-acquire the root pixmap.
root_damaged(ps);
}
force_repaint(ps);
}
return;
} }
static void handle_root_flags(session_t *ps) { static void handle_root_flags(session_t *ps) {
@ -607,11 +415,6 @@ static void handle_root_flags(session_t *ps) {
} }
ps->root_flags &= ~(uint64_t)ROOT_FLAGS_SCREEN_CHANGE; ps->root_flags &= ~(uint64_t)ROOT_FLAGS_SCREEN_CHANGE;
} }
if ((ps->root_flags & ROOT_FLAGS_CONFIGURED) != 0) {
configure_root(ps);
ps->root_flags &= ~(uint64_t)ROOT_FLAGS_CONFIGURED;
}
} }
static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) { static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
@ -668,8 +471,20 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
w->frame_opacity = 1.0; w->frame_opacity = 1.0;
} }
// The below moved to it's own function:
// `win_determine_rounded_corners` (win.c)
/*
// Don't round full screen windows & excluded windows
if ((w && win_is_fullscreen(ps, w)) ||
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL)) {
w->corner_radius = 0;
} else {
w->corner_radius = ps->o.corner_radius;
}
*/
// Update window mode // Update window mode
w->mode = win_calc_mode(w); w->mode = win_calc_mode(ps, w);
// Destroy all reg_ignore above when frame opaque state changes on // Destroy all reg_ignore above when frame opaque state changes on
// SOLID mode // SOLID mode
@ -678,56 +493,63 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
} }
} }
win_stack_foreach_managed(w, &ps->window_stack) { win_stack_foreach_managed(w, &ps->window_stack) {
bool posChanged = (w->oldX != -30000 && w->oldY != -30000 && w->oldW != 0 && w->oldH != 0) bool posChanged =
&& (w->g.x != w->newX || w->g.y != w->newY || w->g.width != w->newW || w->g.height != w->newH); (w->oldX != -10000 && w->oldY != -10000 && w->oldW != 0 && w->oldH != 0) &&
(w->g.x != w->newX || w->g.y != w->newY || w->g.width != w->newW ||
w->g.height != w->newH);
if (posChanged) { if (posChanged) {
float t = get_time_ms(); float t = get_time_ms();
float moveDx = (t - w->moveTimeX) / ps->o.transition_length; float moveDx = (t - w->moveTimeX) / ps->o.transition_length;
float moveDy = (t - w->moveTimeY) / ps->o.transition_length; float moveDy = (t - w->moveTimeY) / ps->o.transition_length;
float moveDw = (t - w->moveTimeW) / ps->o.transition_length; float moveDw = (t - w->moveTimeW) / ps->o.transition_length;
float moveDh = (t - w->moveTimeH) / ps->o.transition_length; float moveDh = (t - w->moveTimeH) / ps->o.transition_length;
if (moveDx >= 1.0) moveDx = 1.0; if (moveDx >= 1.0)
if (moveDy >= 1.0) moveDy = 1.0; moveDx = 1.0;
if (moveDw >= 1.0) moveDw = 1.0; if (moveDy >= 1.0)
if (moveDh >= 1.0) moveDh = 1.0; moveDy = 1.0;
if (moveDw >= 1.0)
moveDw = 1.0;
if (moveDh >= 1.0)
moveDh = 1.0;
float q = pow (moveDx, ps->o.transition_pow_x); float q = pow(moveDx, ps->o.transition_pow_x);
float k = pow (moveDy, ps->o.transition_pow_y); float k = pow(moveDy, ps->o.transition_pow_y);
float g = pow (moveDw, ps->o.transition_pow_w); float g = pow(moveDw, ps->o.transition_pow_w);
float z = pow (moveDh, ps->o.transition_pow_h); float z = pow(moveDh, ps->o.transition_pow_h);
float x = (float) w->oldX * (1-q) + (float) w->newX * q; float x = (float)w->oldX * (1 - q) + (float)w->newX * q;
float y = (float) w->oldY * (1-k) + (float) w->newY * k; float y = (float)w->oldY * (1 - k) + (float)w->newY * k;
float W = (float) w->oldW * (1-g) + (float) w->newW * g; float W = (float)w->oldW * (1 - g) + (float)w->newW * g;
float h = (float) w->oldH * (1-z) + (float) w->newH * z; float h = (float)w->oldH * (1 - z) + (float)w->newH * z;
add_damage_from_win(ps, w); add_damage_from_win(ps, w);
w->g.x = (int) x; w->g.x = (int)x;
w->g.y = (int) y; w->g.y = (int)y;
if (ps->o.size_transition) { if (ps->o.size_transition) {
w->g.width = (int) W; w->g.width = (int)W;
w->g.height = (int) h; w->g.height = (int)h;
} }
/* w->to_paint = true; */ /* w->to_paint = true; */
w->mode = WMODE_TRANS; w->mode = WMODE_TRANS;
*fade_running = true; *fade_running = true;
} }
// TODO // TODO
//if ((w->shadow && posChanged) || (ps->o.size_transition && w->pixmap_damaged)) { // if ((w->shadow && posChanged) || (ps->o.size_transition &&
// rc_region_unref(&w->extents); // w->pixmap_damaged)) {
// rc_region_unref(&w->border_size); // rc_region_unref(&w->extents);
// w->extents = win_extents(ps, w); // rc_region_unref(&w->border_size);
// calc_win_size(ps, w); // w->extents = win_extents(ps, w);
// calc_win_size(ps, w);
// if (ps->shape_exists && ps->o.shadow_ignore_shaped // if (ps->shape_exists && ps->o.shadow_ignore_shaped
// && ps->o.detect_rounded_corners && w->bounding_shaped) // && ps->o.detect_rounded_corners && w->bounding_shaped)
// win_update_shape(ps, w); // win_update_shape(ps, w);
//} //}
/* add_damage_win(ps, w); */ /* add_damage_win(ps, w); */
} }
// 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();
@ -747,6 +569,11 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
rc_region_unref(&w->reg_ignore); rc_region_unref(&w->reg_ignore);
} }
// Clear flags if we are not using experimental backends
if (!ps->o.experimental_backends) {
w->flags = 0;
}
// log_trace("%d %d %s", w->a.map_state, w->ever_damaged, w->name); // log_trace("%d %d %s", w->a.map_state, w->ever_damaged, w->name);
// Give up if it's not damaged or invisible, or it's unmapped and its // Give up if it's not damaged or invisible, or it's unmapped and its
@ -801,10 +628,10 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
// w->mode == WMODE_SOLID or WMODE_FRAME_TRANS // w->mode == WMODE_SOLID or WMODE_FRAME_TRANS
region_t *tmp = rc_region_new(); region_t *tmp = rc_region_new();
if (w->mode == WMODE_SOLID) { if (w->mode == WMODE_SOLID) {
*tmp = win_get_bounding_shape_global_by_val(w); *tmp = win_get_bounding_shape_global_by_val(w, false);
} else { } else {
// w->mode == WMODE_FRAME_TRANS // w->mode == WMODE_FRAME_TRANS
win_get_region_noframe_local(w, tmp); win_get_region_noframe_local(w, tmp, false);
pixman_region32_intersect(tmp, tmp, &w->bounding_shape); pixman_region32_intersect(tmp, tmp, &w->bounding_shape);
pixman_region32_translate(tmp, w->g.x, w->g.y); pixman_region32_translate(tmp, w->g.x, w->g.y);
} }
@ -894,6 +721,243 @@ static struct managed_win *paint_preprocess(session_t *ps, bool *fade_running) {
return bottom; return bottom;
} }
/**
* Rebuild cached <code>screen_reg</code>.
*/
static void rebuild_screen_reg(session_t *ps) {
get_screen_region(ps, &ps->screen_reg);
}
/**
* Rebuild <code>shadow_exclude_reg</code>.
*/
static void rebuild_shadow_exclude_reg(session_t *ps) {
bool ret = parse_geometry(ps, ps->o.shadow_exclude_reg_str, &ps->shadow_exclude_reg);
if (!ret)
exit(1);
}
/// Free up all the images and deinit the backend
static void destroy_backend(session_t *ps) {
win_stack_foreach_managed_safe(w, &ps->window_stack) {
// Wrapping up fading in progress
if (win_skip_fading(ps, w)) {
// `w` is freed by win_skip_fading
continue;
}
if (ps->backend_data) {
if (w->state == WSTATE_MAPPED) {
win_release_images(ps->backend_data, w);
} else {
assert(!w->win_image);
assert(!w->shadow_image);
}
}
free_paint(ps, &w->paint);
}
if (ps->backend_data && ps->root_image) {
ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
ps->root_image = NULL;
}
if (ps->backend_data) {
// deinit backend
if (ps->backend_blur_context) {
ps->backend_data->ops->destroy_blur_context(
ps->backend_data, ps->backend_blur_context);
ps->backend_blur_context = NULL;
}
if (ps->backend_round_context) {
ps->backend_data->ops->destroy_round_context(
ps->backend_data, ps->backend_round_context);
ps->backend_round_context = NULL;
}
ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL;
}
}
static bool initialize_blur(session_t *ps) {
struct kernel_blur_args kargs;
struct gaussian_blur_args gargs;
struct dual_kawase_blur_args dkargs;
struct box_blur_args bargs;
void *args = NULL;
switch (ps->o.blur_method) {
case BLUR_METHOD_BOX:
bargs.size = ps->o.blur_radius;
args = (void *)&bargs;
break;
case BLUR_METHOD_KERNEL:
kargs.kernel_count = ps->o.blur_kernel_count;
kargs.kernels = ps->o.blur_kerns;
args = (void *)&kargs;
break;
case BLUR_METHOD_GAUSSIAN:
gargs.size = ps->o.blur_radius;
gargs.deviation = ps->o.blur_deviation;
args = (void *)&gargs;
break;
case BLUR_METHOD_ALT_KAWASE:
case BLUR_METHOD_DUAL_KAWASE:
dkargs.size = ps->o.blur_radius;
dkargs.strength = ps->o.blur_strength;
args = (void *)&dkargs;
break;
default: return true;
}
ps->backend_blur_context = ps->backend_data->ops->create_blur_context(
ps->backend_data, ps->o.blur_method, args);
return ps->backend_blur_context != NULL;
}
static bool initialize_round_corners(session_t *ps) {
struct round_corners_args cargs;
cargs.corner_radius = ps->o.corner_radius;
cargs.round_borders = ps->o.round_borders;
ps->backend_round_context =
ps->backend_data->ops->create_round_context(ps->backend_data, &cargs);
return ps->backend_round_context != NULL;
}
/// Init the backend and bind all the window pixmap to backend images
static bool initialize_backend(session_t *ps) {
if (ps->o.experimental_backends) {
assert(!ps->backend_data);
// Reinitialize win_data
assert(backend_list[ps->o.backend]);
ps->backend_data = backend_list[ps->o.backend]->init(ps);
if (!ps->backend_data) {
log_fatal("Failed to initialize backend, aborting...");
quit(ps);
return false;
}
ps->backend_data->ops = backend_list[ps->o.backend];
if (!initialize_blur(ps)) {
log_fatal("Failed to prepare for background blur, aborting...");
ps->backend_data->ops->deinit(ps->backend_data);
ps->backend_data = NULL;
quit(ps);
return false;
}
if (!initialize_round_corners(ps)) {
log_fatal("Failed to prepare for rounded corners, will "
"ignore...");
ps->o.corner_radius = 0;
}
// window_stack shouldn't include window that's
// not in the hash table at this point. Since
// there cannot be any fading windows.
HASH_ITER2(ps->windows, _w) {
if (!_w->managed) {
continue;
}
auto w = (struct managed_win *)_w;
assert(w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED);
if (w->state == WSTATE_MAPPED) {
// We need to reacquire image
log_debug("Marking window %#010x (%s) for update after "
"redirection",
w->base.id, w->name);
if (w->shadow) {
struct color c = {
.red = ps->o.shadow_red,
.green = ps->o.shadow_green,
.blue = ps->o.shadow_blue,
.alpha = ps->o.shadow_opacity,
};
win_bind_shadow(ps->backend_data, w, c,
ps->gaussian_map);
}
w->flags |= WIN_FLAGS_PIXMAP_STALE;
ps->pending_updates = true;
}
}
}
// The old backends binds pixmap lazily, nothing to do here
return true;
}
/// Handle configure event of a root window
void configure_root(session_t *ps, int width, int height) {
log_info("Root configuration changed, new geometry: %dx%d", width, height);
bool has_root_change = false;
if (ps->redirected) {
// On root window changes
if (ps->o.experimental_backends) {
assert(ps->backend_data);
has_root_change = ps->backend_data->ops->root_change != NULL;
} else {
// Old backend can handle root change
has_root_change = true;
}
if (!has_root_change) {
// deinit/reinit backend and free up resources if the backend
// cannot handle root change
destroy_backend(ps);
}
free_paint(ps, &ps->tgt_buffer);
}
ps->root_width = width;
ps->root_height = height;
rebuild_screen_reg(ps);
rebuild_shadow_exclude_reg(ps);
// Invalidate reg_ignore from the top
auto top_w = win_stack_find_next_managed(ps, &ps->window_stack);
if (top_w) {
rc_region_unref(&top_w->reg_ignore);
top_w->reg_ignore_valid = false;
}
if (ps->redirected) {
for (int i = 0; i < ps->ndamage; i++) {
pixman_region32_clear(&ps->damage_ring[i]);
}
ps->damage = ps->damage_ring + ps->ndamage - 1;
#ifdef CONFIG_OPENGL
// GLX root change callback
if (BKEND_GLX == ps->o.backend && !ps->o.experimental_backends) {
glx_on_root_change(ps);
}
#endif
if (has_root_change) {
if (ps->backend_data != NULL) {
ps->backend_data->ops->root_change(ps->backend_data, ps);
}
// Old backend's root_change is not a specific function
} else {
if (!initialize_backend(ps)) {
log_fatal("Failed to re-initialize backend after root "
"change, aborting...");
ps->quit = true;
// TODO only event handlers should request ev_break,
// otherwise it's too hard to keep track of what can break
// the event loop
ev_break(ps->loop, EVBREAK_ALL);
return;
}
// Re-acquire the root pixmap.
root_damaged(ps);
}
force_repaint(ps);
}
return;
}
void root_damaged(session_t *ps) { void root_damaged(session_t *ps) {
if (ps->root_tile_paint.pixmap) { if (ps->root_tile_paint.pixmap) {
free_root_tile(ps); free_root_tile(ps);
@ -1277,7 +1341,6 @@ static bool redirect_start(session_t *ps) {
// Re-detect driver since we now have a backend // Re-detect driver since we now have a backend
ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root); ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root);
apply_driver_workarounds(ps, ps->drivers);
root_damaged(ps); root_damaged(ps);
@ -1363,6 +1426,12 @@ static void handle_new_windows(session_t *ps) {
} }
static void refresh_windows(session_t *ps) { static void refresh_windows(session_t *ps) {
win_stack_foreach_managed_safe(w, &ps->window_stack) {
win_process_updates(ps, w);
}
}
static void refresh_stale_images(session_t *ps) {
win_stack_foreach_managed(w, &ps->window_stack) { win_stack_foreach_managed(w, &ps->window_stack) {
win_process_flags(ps, w); win_process_flags(ps, w);
} }
@ -1399,13 +1468,7 @@ static void handle_pending_updates(EV_P_ struct session *ps) {
// Call fill_win on new windows // Call fill_win on new windows
handle_new_windows(ps); handle_new_windows(ps);
// Handle screen changes // Process window updates
// This HAS TO be called before refresh_windows, as handle_root_flags
// could call configure_root, which will release images and mark them
// stale.
handle_root_flags(ps);
// Process window flags
refresh_windows(ps); refresh_windows(ps);
{ {
@ -1417,6 +1480,12 @@ static void handle_pending_updates(EV_P_ struct session *ps) {
free(r); free(r);
} }
// Refresh pixmaps and shadows
refresh_stale_images(ps);
// Handle screen changes
handle_root_flags(ps);
e = xcb_request_check(ps->c, xcb_ungrab_server_checked(ps->c)); e = xcb_request_check(ps->c, xcb_ungrab_server_checked(ps->c));
if (e) { if (e) {
log_fatal_x_error(e, "failed to ungrab x server"); log_fatal_x_error(e, "failed to ungrab x server");
@ -1670,9 +1739,11 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
.randr_exists = 0, .randr_exists = 0,
.randr_event = 0, .randr_event = 0,
.randr_error = 0, .randr_error = 0,
#ifdef CONFIG_OPENGL
.glx_exists = false, .glx_exists = false,
.glx_event = 0, .glx_event = 0,
.glx_error = 0, .glx_error = 0,
#endif
.xrfilter_convolution_exists = false, .xrfilter_convolution_exists = false,
.atoms_wintypes = {0}, .atoms_wintypes = {0},
@ -1739,7 +1810,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
xcb_prefetch_extension_data(ps->c, &xcb_xinerama_id); xcb_prefetch_extension_data(ps->c, &xcb_xinerama_id);
xcb_prefetch_extension_data(ps->c, &xcb_present_id); xcb_prefetch_extension_data(ps->c, &xcb_present_id);
xcb_prefetch_extension_data(ps->c, &xcb_sync_id); xcb_prefetch_extension_data(ps->c, &xcb_sync_id);
xcb_prefetch_extension_data(ps->c, &xcb_glx_id);
ext_info = xcb_get_extension_data(ps->c, &xcb_render_id); ext_info = xcb_get_extension_data(ps->c, &xcb_render_id);
if (!ext_info || !ext_info->present) { if (!ext_info || !ext_info->present) {
@ -1795,13 +1865,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
XCB_XFIXES_MINOR_VERSION) XCB_XFIXES_MINOR_VERSION)
.sequence); .sequence);
ext_info = xcb_get_extension_data(ps->c, &xcb_glx_id);
if (ext_info && ext_info->present) {
ps->glx_exists = true;
ps->glx_error = ext_info->first_error;
ps->glx_event = ext_info->first_event;
}
// Parse configuration file // Parse configuration file
win_option_mask_t winopt_mask[NUM_WINTYPES] = {{0}}; win_option_mask_t winopt_mask[NUM_WINTYPES] = {{0}};
bool shadow_enabled = false, fading_enable = false, hasneg = false; bool shadow_enabled = false, fading_enable = false, hasneg = false;
@ -1869,6 +1932,8 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
c2_list_postprocess(ps, ps->o.blur_background_blacklist) && c2_list_postprocess(ps, ps->o.blur_background_blacklist) &&
c2_list_postprocess(ps, ps->o.invert_color_list) && c2_list_postprocess(ps, ps->o.invert_color_list) &&
c2_list_postprocess(ps, ps->o.opacity_rules) && c2_list_postprocess(ps, ps->o.opacity_rules) &&
c2_list_postprocess(ps, ps->o.rounded_corners_blacklist) &&
c2_list_postprocess(ps, ps->o.round_borders_blacklist) &&
c2_list_postprocess(ps, ps->o.focus_blacklist))) { c2_list_postprocess(ps, ps->o.focus_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");
@ -1925,24 +1990,23 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
} }
ps->sync_fence = XCB_NONE; ps->sync_fence = XCB_NONE;
if (ps->xsync_exists) { if (!ps->xsync_exists && ps->o.xrender_sync_fence) {
log_error("XSync extension not found. No XSync fence sync is "
"possible. (xrender-sync-fence can't be enabled)");
ps->o.xrender_sync_fence = false;
}
if (ps->o.xrender_sync_fence) {
ps->sync_fence = x_new_id(ps->c); ps->sync_fence = x_new_id(ps->c);
e = xcb_request_check( e = xcb_request_check(
ps->c, xcb_sync_create_fence(ps->c, ps->root, ps->sync_fence, 0)); ps->c, xcb_sync_create_fence(ps->c, ps->root, ps->sync_fence, 0));
if (e) { if (e) {
if (ps->o.xrender_sync_fence) { log_error_x_error(e, "Failed to create a XSync fence. "
log_error_x_error(e, "Failed to create a XSync fence. " "xrender-sync-fence will be disabled");
"xrender-sync-fence will be " ps->o.xrender_sync_fence = false;
"disabled");
ps->o.xrender_sync_fence = false;
}
ps->sync_fence = XCB_NONE; ps->sync_fence = XCB_NONE;
free(e); free(e);
} }
} else if (ps->o.xrender_sync_fence) {
log_error("XSync extension not found. No XSync fence sync is "
"possible. (xrender-sync-fence can't be enabled)");
ps->o.xrender_sync_fence = false;
} }
// Query X RandR // Query X RandR
@ -2014,8 +2078,12 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
} }
} }
// Target window must be initialized before the backend
//
// backend_operations::present == NULL means this backend doesn't need a target
// window; non experimental backends always need a target window
ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root); ps->drivers = detect_driver(ps->c, ps->backend_data, ps->root);
apply_driver_workarounds(ps, ps->drivers);
// Initialize filters, must be preceded by OpenGL context creation // Initialize filters, must be preceded by OpenGL context creation
if (!ps->o.experimental_backends && !init_render(ps)) { if (!ps->o.experimental_backends && !init_render(ps)) {
@ -2132,7 +2200,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
e = xcb_request_check(ps->c, xcb_grab_server_checked(ps->c)); e = xcb_request_check(ps->c, xcb_grab_server_checked(ps->c));
if (e) { if (e) {
log_fatal_x_error(e, "Failed to grab X server"); log_fatal_x_error(e, "Failed to grab X server");
free(e);
goto err; goto err;
} }
@ -2151,7 +2218,6 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
if (e) { if (e) {
log_fatal_x_error(e, "Failed to ungrab server"); log_fatal_x_error(e, "Failed to ungrab server");
free(e); free(e);
goto err;
} }
ps->server_grabbed = false; ps->server_grabbed = false;
@ -2241,6 +2307,8 @@ static void session_destroy(session_t *ps) {
free_wincondlst(&ps->o.opacity_rules); free_wincondlst(&ps->o.opacity_rules);
free_wincondlst(&ps->o.paint_blacklist); free_wincondlst(&ps->o.paint_blacklist);
free_wincondlst(&ps->o.unredir_if_possible_blacklist); free_wincondlst(&ps->o.unredir_if_possible_blacklist);
free_wincondlst(&ps->o.rounded_corners_blacklist);
free_wincondlst(&ps->o.round_borders_blacklist);
// Free tracked atom list // Free tracked atom list
{ {

View File

@ -25,11 +25,7 @@
#include "win.h" #include "win.h"
#include "x.h" #include "x.h"
enum root_flags { enum root_flags { ROOT_FLAGS_SCREEN_CHANGE = 1 };
ROOT_FLAGS_SCREEN_CHANGE = 1, // Received RandR screen change notify, we
// use this to track refresh rate changes
ROOT_FLAGS_CONFIGURED = 2 // Received configure notify on the root window
};
// == Functions == // == Functions ==
// TODO move static inline functions that are only used in picom.c, into // TODO move static inline functions that are only used in picom.c, into
@ -42,6 +38,11 @@ void add_damage(session_t *ps, const region_t *damage);
uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode); uint32_t determine_evmask(session_t *ps, xcb_window_t wid, win_evmode_t mode);
xcb_window_t find_client_win(session_t *ps, xcb_window_t w);
/// Handle configure event of a root window
void configure_root(session_t *ps, int width, int height);
void circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce); void circulate_win(session_t *ps, xcb_circulate_notify_event_t *ce);
void update_refresh_rate(session_t *ps); void update_refresh_rate(session_t *ps);

View File

@ -48,14 +48,15 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
bool repeat, int depth, xcb_visualid_t visual, bool force) { bool repeat, int depth, xcb_visualid_t visual, bool force) {
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
// XXX This is a mess. But this will go away after the backend refactor. // XXX This is a mess. But this will go away after the backend refactor.
static thread_local struct glx_fbconfig_info *argb_fbconfig = NULL;
if (!ppaint->pixmap) if (!ppaint->pixmap)
return false; return false;
struct glx_fbconfig_info *fbcfg; struct glx_fbconfig_info *fbcfg;
if (!visual) { if (!visual) {
assert(depth == 32); assert(depth == 32);
if (!ps->argb_fbconfig) { if (!argb_fbconfig) {
ps->argb_fbconfig = argb_fbconfig =
glx_find_fbconfig(ps->dpy, ps->scr, glx_find_fbconfig(ps->dpy, ps->scr,
(struct xvisual_info){.red_size = 8, (struct xvisual_info){.red_size = 8,
.green_size = 8, .green_size = 8,
@ -63,11 +64,11 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
.alpha_size = 8, .alpha_size = 8,
.visual_depth = 32}); .visual_depth = 32});
} }
if (!ps->argb_fbconfig) { if (!argb_fbconfig) {
log_error("Failed to find appropriate FBConfig for 32 bit depth"); log_error("Failed to find appropriate FBConfig for 32 bit depth");
return false; return false;
} }
fbcfg = ps->argb_fbconfig; fbcfg = argb_fbconfig;
} else { } else {
auto m = x_get_visual_info(ps->c, visual); auto m = x_get_visual_info(ps->c, visual);
if (m.visual_depth < 0) { if (m.visual_depth < 0) {
@ -185,28 +186,139 @@ void free_paint(session_t *ps, paint_t *ppaint) {
ppaint->pixmap = XCB_NONE; ppaint->pixmap = XCB_NONE;
} }
void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, double opacity, uint32_t
bool argb, bool neg, xcb_render_picture_t pict, glx_texture_t *ptex, make_circle(int cx, int cy, int radius, uint32_t max_ntraps, xcb_render_trapezoid_t traps[]) {
const region_t *reg_paint, const glx_prog_main_t *pprogram) { uint32_t n = 0, k = 0;
int y1, y2;
double w;
while (k < max_ntraps) {
y1 = (int)(-radius * cos(M_PI * k / max_ntraps));
traps[n].top = (cy + y1) << 16;
traps[n].left.p1.y = (cy + y1) << 16;
traps[n].right.p1.y = (cy + y1) << 16;
w = sqrt(radius * radius - y1 * y1) * 65536;
traps[n].left.p1.x = (int)((cx << 16) - w);
traps[n].right.p1.x = (int)((cx << 16) + w);
do {
k++;
y2 = (int)(-radius * cos(M_PI * k / max_ntraps));
} while (y1 == y2);
traps[n].bottom = (cy + y2) << 16;
traps[n].left.p2.y = (cy + y2) << 16;
traps[n].right.p2.y = (cy + y2) << 16;
w = sqrt(radius * radius - y2 * y2) * 65536;
traps[n].left.p2.x = (int)((cx << 16) - w);
traps[n].right.p2.x = (int)((cx << 16) + w);
n++;
}
return n;
}
uint32_t make_rectangle(int x, int y, int wid, int hei, xcb_render_trapezoid_t traps[]) {
traps[0].top = y << 16;
traps[0].left.p1.y = y << 16;
traps[0].left.p1.x = x << 16;
traps[0].left.p2.y = (y + hei) << 16;
traps[0].left.p2.x = x << 16;
traps[0].bottom = (y + hei) << 16;
traps[0].right.p1.x = (x + wid) << 16;
traps[0].right.p1.y = y << 16;
traps[0].right.p2.x = (x + wid) << 16;
traps[0].right.p2.y = (y + hei) << 16;
return 1;
}
uint32_t make_rounded_window_shape(xcb_render_trapezoid_t traps[], uint32_t max_ntraps, int cr, int wid, int hei)
{
uint32_t n = make_circle(cr, cr, cr, max_ntraps, traps);
n += make_circle(wid - cr, cr, cr, max_ntraps, traps + n);
n += make_circle(wid - cr, hei - cr, cr, max_ntraps, traps + n);
n += make_circle(cr, hei - cr, cr, max_ntraps, traps + n);
n += make_rectangle(0, cr, cr, hei - 2 * cr, traps + n);
n += make_rectangle(cr, 0, wid - 2 * cr, cr, traps + n);
n += make_rectangle(wid - cr, cr, cr, hei - 2 * cr, traps + n);
n += make_rectangle(cr, hei - cr, wid - 2 * cr, cr, traps + n);
n += make_rectangle(cr, cr, wid - 2 * cr, hei - 2 * cr,
traps + n);
return n;
}
void render(session_t *ps, struct managed_win *w attr_unused, int x, int y,
int dx, int dy, int wid, int hei, int fullwid, int fullhei, double opacity,
bool argb, bool neg, int cr, xcb_render_picture_t pict, glx_texture_t *ptex,
const region_t *reg_paint, const glx_prog_main_t *pprogram, clip_t *clip) {
switch (ps->o.backend) { switch (ps->o.backend) {
case BKEND_XRENDER: case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: { case BKEND_XR_GLX_HYBRID: {
auto alpha_step = (int)(opacity * MAX_ALPHA); auto alpha_step = (int)(opacity * MAX_ALPHA);
xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step]; xcb_render_picture_t alpha_pict = ps->alpha_picts[alpha_step];
if (alpha_step != 0) { if (alpha_step != 0) {
uint8_t op = ((!argb && !alpha_pict) ? XCB_RENDER_PICT_OP_SRC if (cr) {
//log_warn("f(%d, %d) wh(%d %d) xy(%d %d) dxy(%d %d)", fullwid, fullhei, wid, hei, x, y, dx, dy);
xcb_render_picture_t p_tmp = x_create_picture_with_standard(
ps->c, ps->root, fullwid, fullhei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {
.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0,
.y = 0,
.width = to_u16_checked(fullwid),
.height = to_u16_checked(fullhei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
p_tmp, trans, 1, &rect);
uint32_t max_ntraps = to_u32_checked(cr);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, cr, fullwid, fullhei);
xcb_render_trapezoids(
ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8),
0, 0, n, traps);
xcb_render_composite(
ps->c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp,
ps->tgt_buffer.pict, to_i16_checked(x), to_i16_checked(y),
to_i16_checked(x), to_i16_checked(y), to_i16_checked(dx), to_i16_checked(dy),
to_u16_checked(wid), to_u16_checked(hei));
xcb_render_free_picture(ps->c, p_tmp);
} else {
xcb_render_picture_t p_tmp = alpha_pict;
if(clip){
p_tmp = x_create_picture_with_standard(ps->c, ps->root, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t black = {.red = 255, .blue = 255, .green = 255, .alpha = 255};
const xcb_rectangle_t rect = {.x = 0, .y = 0, .width = to_u16_checked(wid), .height = to_u16_checked(hei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, p_tmp, black, 1, &rect);
if(alpha_pict) {
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, alpha_pict, XCB_NONE, p_tmp, 0, 0, 0, 0, 0, 0, to_u16_checked(wid), to_u16_checked(hei));
}
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OUT_REVERSE, clip->pict, XCB_NONE, p_tmp, 0, 0, 0, 0, to_i16_checked(clip->x), to_i16_checked(clip->y), to_u16_checked(wid), to_u16_checked(hei));
}
uint8_t op = ((!argb && !alpha_pict && !clip) ? XCB_RENDER_PICT_OP_SRC
: XCB_RENDER_PICT_OP_OVER); : XCB_RENDER_PICT_OP_OVER);
xcb_render_composite( xcb_render_composite(
ps->c, op, pict, alpha_pict, ps->tgt_buffer.pict, ps->c, op, pict, p_tmp/*alpha_pict*/, ps->tgt_buffer.pict,
to_i16_checked(x), to_i16_checked(y), 0, 0, to_i16_checked(dx), to_i16_checked(x), to_i16_checked(y), 0, 0, to_i16_checked(dx),
to_i16_checked(dy), to_u16_checked(wid), to_u16_checked(hei)); to_i16_checked(dy), to_u16_checked(wid), to_u16_checked(hei));
if(clip){
xcb_render_free_picture(ps->c, p_tmp);
}
}
} }
break; break;
} }
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
case BKEND_GLX: case BKEND_GLX:
glx_render(ps, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb, glx_render(ps, w, ptex, x, y, dx, dy, wid, hei, ps->psglx->z, opacity, argb,
neg, reg_paint, pprogram); neg, cr, reg_paint, pprogram);
ps->psglx->z += 1; ps->psglx->z += 1;
break; break;
#endif #endif
@ -221,21 +333,24 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, doubl
} }
static inline void static inline void
paint_region(session_t *ps, const struct managed_win *w, int x, int y, int wid, int hei, paint_region(session_t *ps, struct managed_win *w, int x, int y, int wid, int hei,
double opacity, const region_t *reg_paint, xcb_render_picture_t pict) { double opacity, const region_t *reg_paint, xcb_render_picture_t pict) {
const int dx = (w ? w->g.x : 0) + x; const int dx = (w ? w->g.x : 0) + x;
const int dy = (w ? w->g.y : 0) + y; const int dy = (w ? w->g.y : 0) + y;
const int fullwid = w ? w->widthb : 0;
const int fullhei = w ? w-> heightb : 0;
const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend)); const bool argb = (w && (win_has_alpha(w) || ps->o.force_win_blend));
const bool neg = (w && w->invert_color); const bool neg = (w && w->invert_color);
render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg, pict, render(ps, w, x, y, dx, dy, wid, hei, fullwid, fullhei, opacity, argb, neg,
(w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint, (w ? w->corner_radius : 0),
pict, (w ? w->paint.ptex : ps->root_tile_paint.ptex), reg_paint,
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
w ? &ps->glx_prog_win : NULL w ? &ps->glx_prog_win : NULL
#else #else
NULL NULL
#endif #endif
); , XCB_NONE);
} }
/** /**
@ -258,6 +373,46 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
return true; return true;
} }
/**
* Rounde the corners of a window.
* Applies a fragment shader to discard corners
*
*/
static inline void
win_round_corners(session_t *ps, struct managed_win *w attr_unused, int shader_idx attr_unused, float cr attr_unused,
xcb_render_picture_t tgt_buffer attr_unused, const region_t *reg_paint) {
#ifdef CONFIG_OPENGL
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
#endif
//log_debug("x:%d y:%d w:%d h:%d", x, y, wid, hei);
switch (ps->o.backend) {
case BKEND_XRENDER:
case BKEND_XR_GLX_HYBRID: {
// XRender method is implemented inside render()
} break;
#ifdef CONFIG_OPENGL
case BKEND_GLX:
if (shader_idx == 1) {
glx_round_corners_dst1(ps, w, w->glx_texture_bg, shader_idx, x, y, wid, hei,
(float)ps->psglx->z - 0.5f, cr, reg_paint, &w->glx_round_cache);
} else {
glx_round_corners_dst0(ps, w, w->glx_texture_bg, shader_idx, x, y, wid, hei,
(float)ps->psglx->z - 0.5f, cr, reg_paint, &w->glx_round_cache);
}
break;
#endif
default: assert(0);
}
#ifndef CONFIG_OPENGL
(void)reg_paint;
#endif
}
/** /**
* Paint a window itself and dim it if asked. * Paint a window itself and dim it if asked.
*/ */
@ -529,8 +684,8 @@ static void paint_root(session_t *ps, const region_t *reg_paint) {
* Generate shadow <code>Picture</code> for a window. * Generate shadow <code>Picture</code> for a window.
*/ */
static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacity) { static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacity) {
/* const int width = w->widthb; */ //const int width = w->widthb;
/* const int height = w->heightb; */ //const int height = w->heightb;
const int width = w->newW; // TODO! const int width = w->newW; // TODO!
const int height = w->newH; const int height = w->newH;
// log_trace("(): building shadow for %s %d %d", w->name, width, height); // log_trace("(): building shadow for %s %d %d", w->name, width, height);
@ -613,9 +768,29 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
return; return;
} }
render(ps, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width, xcb_render_picture_t td = XCB_NONE;
w->shadow_height, w->shadow_opacity, true, false, w->shadow_paint.pict, if (w->corner_radius) {
w->shadow_paint.ptex, reg_paint, NULL); uint32_t max_ntraps = to_u32_checked(w->corner_radius);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, w->corner_radius, w->widthb, w->heightb);
td = x_create_picture_with_standard(ps->c, ps->root, w->widthb, w->heightb, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0, .y = 0, .width = to_u16_checked(w->widthb), .height = to_u16_checked(w->heightb)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td, trans, 1, &rect);
xcb_render_trapezoids(ps->c, XCB_RENDER_PICT_OP_OVER, solid_picture(ps->c, ps->root, false, 1, 0, 0, 0), td, x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0, 0, n, traps);
}
clip_t clip = { .pict = td, -(w->shadow_dx), .y = -(w->shadow_dy) };
render(ps, w, 0, 0, w->g.x + w->shadow_dx, w->g.y + w->shadow_dy, w->shadow_width,
w->shadow_height, w->widthb, w->heightb, w->shadow_opacity, true, false, 0, w->shadow_paint.pict,
w->shadow_paint.ptex, reg_paint, NULL, w->corner_radius ? &clip : NULL);
if(td){
xcb_render_free_picture(ps->c, td);
}
} }
/** /**
@ -635,7 +810,7 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
*/ */
static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y, static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y,
uint16_t wid, uint16_t hei, struct x_convolution_kernel **blur_kerns, uint16_t wid, uint16_t hei, struct x_convolution_kernel **blur_kerns,
int nkernels, const region_t *reg_clip) { int nkernels, const region_t *reg_clip, xcb_render_picture_t rounded) {
assert(blur_kerns); assert(blur_kerns);
assert(blur_kerns[0]); assert(blur_kerns[0]);
@ -680,7 +855,8 @@ static bool xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t
} }
if (src_pict != tgt_buffer) if (src_pict != tgt_buffer)
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE, //xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, rounded,
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded,
tgt_buffer, 0, 0, 0, 0, x, y, wid, hei); tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
free_picture(ps->c, &tmp_picture); free_picture(ps->c, &tmp_picture);
@ -698,6 +874,7 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
const int16_t y = w->g.y; const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb); const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb); const auto hei = to_u16_checked(w->heightb);
const int cr = (w ? w->corner_radius : 0);
double factor_center = 1.0; double factor_center = 1.0;
// Adjust blur strength according to window opacity, to make it appear // Adjust blur strength according to window opacity, to make it appear
@ -729,13 +906,27 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
&ps->blur_kerns_cache[i]); &ps->blur_kerns_cache[i]);
} }
xcb_render_picture_t td = XCB_NONE;
if (cr) {
uint32_t max_ntraps = to_u32_checked(cr);
xcb_render_trapezoid_t traps[4 * max_ntraps + 5];
uint32_t n = make_rounded_window_shape(traps, max_ntraps, cr, wid, hei);
td = x_create_picture_with_standard(ps->c, ps->root, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
xcb_render_color_t trans = {.red = 0, .blue = 0, .green = 0, .alpha = 0};
const xcb_rectangle_t rect = {.x = 0, .y = 0, .width = to_u16_checked(wid), .height = to_u16_checked(hei)};
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td, trans, 1, &rect);
xcb_render_trapezoids(ps->c, XCB_RENDER_PICT_OP_OVER, solid_picture(ps->c, ps->root, false, 1, 0, 0, 0), td, x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0, 0, n, traps);
}
// Minimize the region we try to blur, if the window itself is not // Minimize the region we try to blur, if the window itself is not
// opaque, only the frame is. // opaque, only the frame is.
region_t reg_blur = win_get_bounding_shape_global_by_val(w); region_t reg_blur = win_get_bounding_shape_global_by_val(w, true);
if (w->mode == WMODE_FRAME_TRANS && !ps->o.force_win_blend) { if (w->mode == WMODE_FRAME_TRANS && !ps->o.force_win_blend) {
region_t reg_noframe; region_t reg_noframe;
pixman_region32_init(&reg_noframe); pixman_region32_init(&reg_noframe);
win_get_region_noframe_local(w, &reg_noframe); win_get_region_noframe_local(w, &reg_noframe, true);
pixman_region32_translate(&reg_noframe, w->g.x, w->g.y); pixman_region32_translate(&reg_noframe, w->g.x, w->g.y);
pixman_region32_subtract(&reg_blur, &reg_blur, &reg_noframe); pixman_region32_subtract(&reg_blur, &reg_blur, &reg_noframe);
pixman_region32_fini(&reg_noframe); pixman_region32_fini(&reg_noframe);
@ -743,14 +934,14 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
// Translate global coordinates to local ones // Translate global coordinates to local ones
pixman_region32_translate(&reg_blur, -x, -y); pixman_region32_translate(&reg_blur, -x, -y);
xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache, xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache,
ps->o.blur_kernel_count, &reg_blur); ps->o.blur_kernel_count, &reg_blur, td);
pixman_region32_clear(&reg_blur); pixman_region32_clear(&reg_blur);
} break; } break;
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
case BKEND_GLX: case BKEND_GLX:
// TODO: Handle frame opacity // TODO: Handle frame opacity
glx_blur_dst(ps, x, y, wid, hei, (float)ps->psglx->z - 0.5f, glx_blur_dst(ps, x, y, wid, hei, (float)ps->psglx->z - 0.5f,
(float)factor_center, reg_paint, &w->glx_blur_cache); (float)w->opacity, reg_paint, &w->glx_blur_cache);
break; break;
#endif #endif
default: assert(0); default: assert(0);
@ -852,7 +1043,9 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// //
// Whether this is beneficial is to be determined XXX // Whether this is beneficial is to be determined XXX
for (auto w = t; w; w = w->prev_trans) { for (auto w = t; w; w = w->prev_trans) {
region_t bshape = win_get_bounding_shape_global_by_val(w); region_t bshape_no_corners = win_get_bounding_shape_global_by_val(w, false);
region_t bshape_corners = win_get_bounding_shape_global_by_val(w, true);
// Painting shadow // Painting shadow
if (w->shadow) { if (w->shadow) {
// Lazy shadow building // Lazy shadow building
@ -881,7 +1074,7 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// saving GPU power and handling shaped windows (XXX // saving GPU power and handling shaped windows (XXX
// unconfirmed) // unconfirmed)
if (!ps->o.wintype_option[w->window_type].full_shadow) if (!ps->o.wintype_option[w->window_type].full_shadow)
pixman_region32_subtract(&reg_tmp, &reg_tmp, &bshape); pixman_region32_subtract(&reg_tmp, &reg_tmp, &bshape_no_corners);
if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 && if (ps->o.xinerama_shadow_crop && w->xinerama_scr >= 0 &&
w->xinerama_scr < ps->xinerama_nscrs) w->xinerama_scr < ps->xinerama_nscrs)
@ -908,12 +1101,25 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Remember, reg_ignore is the union of all windows above the current // Remember, reg_ignore is the union of all windows above the current
// window. // window.
pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore); pixman_region32_subtract(&reg_tmp, &region, w->reg_ignore);
pixman_region32_intersect(&reg_tmp, &reg_tmp, &bshape); pixman_region32_intersect(&reg_tmp, &reg_tmp, &bshape_corners);
pixman_region32_fini(&bshape); pixman_region32_fini(&bshape_corners);
pixman_region32_fini(&bshape_no_corners);
reg_tmp = region; reg_tmp = region;
if (pixman_region32_not_empty(&reg_tmp) || true) { if (pixman_region32_not_empty(&reg_tmp) || true) {
set_tgt_clip(ps, &reg_tmp); set_tgt_clip(ps, &reg_tmp);
// If rounded corners backup the region first
#ifdef CONFIG_OPENGL
if (w->corner_radius > 0) {
const int16_t x = w->g.x;
const int16_t y = w->g.y;
const auto wid = to_u16_checked(w->widthb);
const auto hei = to_u16_checked(w->heightb);
glx_bind_texture(ps, &w->glx_texture_bg, x, y, wid, hei, false);
}
#endif
// Blur window background // Blur window background
if (w->blur_background && if (w->blur_background &&
(w->mode == WMODE_TRANS || (w->mode == WMODE_TRANS ||
@ -923,6 +1129,12 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
// Painting the window // Painting the window
paint_one(ps, w, &reg_tmp); paint_one(ps, w, &reg_tmp);
// Round window corners
if (w->corner_radius > 0) {
win_round_corners(ps, w, 1, (float)w->corner_radius,
ps->tgt_buffer.pict, &reg_tmp);
}
} }
} }
@ -1011,8 +1223,8 @@ void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
else else
glFlush(); glFlush();
glXWaitX(); glXWaitX();
glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width, glx_render(ps, t, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
ps->root_height, 0, 1.0, false, false, &region, NULL); ps->root_height, 0, 1.0, false, false, 0, &region, NULL);
// falls through // falls through
case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break; case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
#endif #endif
@ -1131,13 +1343,15 @@ bool init_render(session_t *ps) {
} }
// Blur filter // Blur filter
if (ps->o.blur_method && ps->o.blur_method != BLUR_METHOD_KERNEL) { if (ps->o.blur_method && ps->o.blur_method != BLUR_METHOD_KERNEL &&
log_warn("Old backends only support blur method \"kernel\". Your blur " ps->o.blur_method != BLUR_METHOD_DUAL_KAWASE && ps->o.blur_method != BLUR_METHOD_ALT_KAWASE) {
log_warn("Old backends only support blur methods \"kernel|kawase\". Your blur "
"setting will not be applied"); "setting will not be applied");
ps->o.blur_method = BLUR_METHOD_NONE; ps->o.blur_method = BLUR_METHOD_NONE;
} }
if (ps->o.blur_method == BLUR_METHOD_KERNEL) { if (ps->o.blur_method == BLUR_METHOD_KERNEL || ps->o.blur_method == BLUR_METHOD_DUAL_KAWASE
|| ps->o.blur_method == BLUR_METHOD_ALT_KAWASE) {
ps->blur_kerns_cache = ps->blur_kerns_cache =
ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *); ccalloc(ps->o.blur_kernel_count, struct x_convolution_kernel *);
@ -1176,6 +1390,18 @@ bool init_render(session_t *ps) {
return false; return false;
} }
} }
// Initialize our rounded corners fragment shader
if (ps->o.corner_radius > 0 && ps->o.backend == BKEND_GLX) {
#ifdef CONFIG_OPENGL
if (!glx_init_rounded_corners(ps)) {
log_error("Failed to init rounded corners shader.");
return false;
}
#else
assert(false);
#endif
}
return true; return true;
} }

View File

@ -25,9 +25,15 @@ typedef struct paint {
#endif #endif
} paint_t; } paint_t;
void render(session_t *ps, int x, int y, int dx, int dy, int w, int h, double opacity, typedef struct clip {
bool argb, bool neg, xcb_render_picture_t pict, glx_texture_t *ptex, xcb_render_picture_t pict;
const region_t *reg_paint, const glx_prog_main_t *pprogram); int x;
int y;
} clip_t;
void render(session_t *ps, struct managed_win *, int x, int y, int dx, int dy, int w, int h, int fullw, int fullh, double opacity,
bool argb, bool neg, int cr, xcb_render_picture_t pict, glx_texture_t *ptex,
const region_t *reg_paint, const glx_prog_main_t *pprogram, clip_t *clip);
void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint); void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint);
void paint_all(session_t *ps, struct managed_win *const t, bool ignore_damage); void paint_all(session_t *ps, struct managed_win *const t, bool ignore_damage);

View File

@ -33,6 +33,9 @@ safe_isnan(double a) {
return __builtin_isnan(a); return __builtin_isnan(a);
} }
#define CASESTRRET(s) \
case s: return #s
/// Same as assert(false), but make sure we abort _even in release builds_. /// Same as assert(false), but make sure we abort _even in release builds_.
/// Silence compiler warning caused by release builds making some code paths reachable. /// Silence compiler warning caused by release builds making some code paths reachable.
#define BUG() \ #define BUG() \

364
src/win.c
View File

@ -46,6 +46,9 @@
// TODO Make more window states internal // TODO Make more window states internal
struct managed_win_internal { struct managed_win_internal {
struct managed_win base; struct managed_win base;
/// A bit mask of unhandled window updates
uint_fast32_t pending_updates;
}; };
#define OPAQUE (0xffffffff) #define OPAQUE (0xffffffff)
@ -53,6 +56,17 @@ static const int WIN_GET_LEADER_MAX_RECURSION = 20;
static const int ROUNDED_PIXELS = 1; static const int ROUNDED_PIXELS = 1;
static const double ROUNDED_PERCENT = 0.05; static const double ROUNDED_PERCENT = 0.05;
/// Generate a "return by value" function, from a function that returns the
/// region via a region_t pointer argument.
/// Function signature has to be (win *, region_t *, bool)
#define gen_by_val_corners(fun) \
region_t fun##_by_val(const struct managed_win *w, bool include_corners) { \
region_t ret; \
pixman_region32_init(&ret); \
fun(w, &ret, include_corners); \
return ret; \
}
/// Generate a "return by value" function, from a function that returns the /// Generate a "return by value" function, from a function that returns the
/// region via a region_t pointer argument. /// region via a region_t pointer argument.
/// Function signature has to be (win *, region_t *) /// Function signature has to be (win *, region_t *)
@ -109,6 +123,19 @@ static void win_update_focused(session_t *ps, struct managed_win *w) {
w->focused = true; w->focused = true;
} }
} }
// Always recalculate the window target opacity, since some opacity-related
// options depend on the output value of win_is_focused_real() instead of
// w->focused
auto opacity_target_old = w->opacity_target;
w->opacity_target = win_calc_opacity_target(ps, w, false);
if (opacity_target_old != w->opacity_target && w->state == WSTATE_MAPPED) {
// Only MAPPED can transition to FADING
w->state = WSTATE_FADING;
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
}
} }
/** /**
@ -170,16 +197,18 @@ static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
/** /**
* Get a rectangular region a window occupies, excluding shadow. * Get a rectangular region a window occupies, excluding shadow.
*/ */
static void win_get_region_local(const struct managed_win *w, region_t *res) { static void win_get_region_local(const struct managed_win *w, region_t *res, bool include_corners) {
assert(w->widthb >= 0 && w->heightb >= 0); assert(w->widthb >= 0 && w->heightb >= 0);
pixman_region32_fini(res); pixman_region32_fini(res);
pixman_region32_init_rect(res, 0, 0, (uint)w->widthb, (uint)w->heightb); pixman_region32_init_rect(res, 0, 0, (uint)w->widthb, (uint)w->heightb);
if(!include_corners) win_region_remove_corners(w, res);
} }
/** /**
* Get a rectangular region a window occupies, excluding frame and shadow. * Get a rectangular region a window occupies, excluding frame and shadow.
*/ */
void win_get_region_noframe_local(const struct managed_win *w, region_t *res) { void win_get_region_noframe_local(const struct managed_win *w, region_t *res, bool include_corners) {
const margin_t extents = win_calc_frame_extents(w); const margin_t extents = win_calc_frame_extents(w);
int x = extents.left; int x = extents.left;
@ -190,10 +219,11 @@ void win_get_region_noframe_local(const struct managed_win *w, region_t *res) {
pixman_region32_fini(res); pixman_region32_fini(res);
if (width > 0 && height > 0) { if (width > 0 && height > 0) {
pixman_region32_init_rect(res, x, y, (uint)width, (uint)height); pixman_region32_init_rect(res, x, y, (uint)width, (uint)height);
if(!include_corners) win_region_remove_corners(w, res);
} }
} }
void win_get_region_frame_local(const struct managed_win *w, region_t *res) { void win_get_region_frame_local(const struct managed_win *w, region_t *res, bool include_corners) {
const margin_t extents = win_calc_frame_extents(w); const margin_t extents = win_calc_frame_extents(w);
auto outer_width = extents.left + extents.right + w->g.width; auto outer_width = extents.left + extents.right + w->g.width;
auto outer_height = extents.top + extents.bottom + w->g.height; auto outer_height = extents.top + extents.bottom + w->g.height;
@ -216,10 +246,11 @@ void win_get_region_frame_local(const struct managed_win *w, region_t *res) {
region_t reg_win; region_t reg_win;
pixman_region32_init_rects(&reg_win, (rect_t[]){0, 0, outer_width, outer_height}, 1); pixman_region32_init_rects(&reg_win, (rect_t[]){0, 0, outer_width, outer_height}, 1);
pixman_region32_intersect(res, &reg_win, res); pixman_region32_intersect(res, &reg_win, res);
if(!include_corners) win_region_remove_corners(w, res);
pixman_region32_fini(&reg_win); pixman_region32_fini(&reg_win);
} }
gen_by_val(win_get_region_frame_local); gen_by_val_corners(win_get_region_frame_local);
/** /**
* Add a window to damaged area. * Add a window to damaged area.
@ -244,7 +275,6 @@ static inline void win_release_pixmap(backend_t *base, struct managed_win *w) {
if (w->win_image) { if (w->win_image) {
base->ops->release_image(base, w->win_image); base->ops->release_image(base, w->win_image);
w->win_image = NULL; w->win_image = NULL;
// Bypassing win_set_flags, because `w` might have been destroyed
w->flags |= WIN_FLAGS_PIXMAP_NONE; w->flags |= WIN_FLAGS_PIXMAP_NONE;
} }
} }
@ -254,7 +284,6 @@ static inline void win_release_shadow(backend_t *base, struct managed_win *w) {
if (w->shadow_image) { if (w->shadow_image) {
base->ops->release_image(base, w->shadow_image); base->ops->release_image(base, w->shadow_image);
w->shadow_image = NULL; w->shadow_image = NULL;
// Bypassing win_set_flags, because `w` might have been destroyed
w->flags |= WIN_FLAGS_SHADOW_NONE; w->flags |= WIN_FLAGS_SHADOW_NONE;
} }
} }
@ -275,11 +304,11 @@ static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w
b->ops->bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual), true); b->ops->bind_pixmap(b, pixmap, x_get_visual_info(b->c, w->a.visual), true);
if (!w->win_image) { if (!w->win_image) {
log_error("Failed to bind pixmap"); log_error("Failed to bind pixmap");
win_set_flags(w, WIN_FLAGS_IMAGE_ERROR); w->flags |= WIN_FLAGS_IMAGE_ERROR;
return false; return false;
} }
win_clear_flags(w, WIN_FLAGS_PIXMAP_NONE); w->flags &= ~WIN_FLAGS_PIXMAP_NONE;
return true; return true;
} }
@ -293,13 +322,13 @@ bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color
log_error("Failed to bind shadow image, shadow will be disabled for " log_error("Failed to bind shadow image, shadow will be disabled for "
"%#010x (%s)", "%#010x (%s)",
w->base.id, w->name); w->base.id, w->name);
win_set_flags(w, WIN_FLAGS_SHADOW_NONE); w->flags |= WIN_FLAGS_SHADOW_NONE;
w->shadow = false; w->shadow = false;
return false; return false;
} }
log_debug("New shadow for %#010x (%s)", w->base.id, w->name); log_debug("New shadow for %#010x (%s)", w->base.id, w->name);
win_clear_flags(w, WIN_FLAGS_SHADOW_NONE); w->flags &= ~WIN_FLAGS_SHADOW_NONE;
return true; return true;
} }
@ -309,38 +338,40 @@ void win_release_images(struct backend_base *backend, struct managed_win *w) {
// But if we are not releasing any images anyway, we don't care about the stale // But if we are not releasing any images anyway, we don't care about the stale
// flags. // flags.
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) { if ((w->flags & WIN_FLAGS_PIXMAP_NONE) == 0) {
assert(!win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE)); assert((w->flags & WIN_FLAGS_PIXMAP_STALE) == 0);
win_release_pixmap(backend, w); win_release_pixmap(backend, w);
} }
if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) { if ((w->flags & WIN_FLAGS_SHADOW_NONE) == 0) {
assert(!win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE)); assert((w->flags & WIN_FLAGS_SHADOW_STALE) == 0);
win_release_shadow(backend, w); win_release_shadow(backend, w);
} }
} }
void win_process_flags(session_t *ps, struct managed_win *w) { void win_process_flags(session_t *ps, struct managed_win *w) {
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) { // Make sure all pending window updates are processed before this. Making this
map_win_start(ps, w); // assumption simplifies some checks (e.g. whether window is mapped)
win_clear_flags(w, WIN_FLAGS_MAPPED); assert(((struct managed_win_internal *)w)->pending_updates == 0);
if (!w->flags || (w->flags & WIN_FLAGS_IMAGE_ERROR) != 0) {
return;
} }
// Not a loop // Not a loop
while (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE) && while ((w->flags & WIN_FLAGS_IMAGES_STALE) != 0) {
!win_check_flags_all(w, WIN_FLAGS_IMAGE_ERROR)) {
// Image needs to be updated, update it. // Image needs to be updated, update it.
if (!ps->backend_data) { if (!ps->backend_data) {
// We are using legacy backend, nothing to do here. // We are using legacy backend, nothing to do here.
break; break;
} }
if (win_check_flags_all(w, WIN_FLAGS_PIXMAP_STALE)) { if ((w->flags & WIN_FLAGS_PIXMAP_STALE) != 0) {
// Check to make sure the window is still mapped, otherwise we // Check to make sure the window is still mapped, otherwise we
// won't be able to rebind pixmap after releasing it, yet we might // won't be able to rebind pixmap after releasing it, yet we might
// still need the pixmap for rendering. // still need the pixmap for rendering.
assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING); assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
if (!win_check_flags_all(w, WIN_FLAGS_PIXMAP_NONE)) { if ((w->flags & WIN_FLAGS_PIXMAP_NONE) == 0) {
// Must release images first, otherwise breaks // Must release images first, otherwise breaks
// NVIDIA driver // NVIDIA driver
win_release_pixmap(ps->backend_data, w); win_release_pixmap(ps->backend_data, w);
@ -348,8 +379,8 @@ void win_process_flags(session_t *ps, struct managed_win *w) {
win_bind_pixmap(ps->backend_data, w); win_bind_pixmap(ps->backend_data, w);
} }
if (win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE)) { if ((w->flags & WIN_FLAGS_SHADOW_STALE) != 0) {
if (!win_check_flags_all(w, WIN_FLAGS_SHADOW_NONE)) { if ((w->flags & WIN_FLAGS_SHADOW_NONE) == 0) {
win_release_shadow(ps->backend_data, w); win_release_shadow(ps->backend_data, w);
} }
if (w->shadow) { if (w->shadow) {
@ -367,14 +398,7 @@ void win_process_flags(session_t *ps, struct managed_win *w) {
} }
// Clear stale image flags // Clear stale image flags
if (win_check_flags_any(w, WIN_FLAGS_IMAGES_STALE)) { w->flags &= ~WIN_FLAGS_IMAGES_STALE;
win_clear_flags(w, WIN_FLAGS_IMAGES_STALE);
}
if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) {
win_recheck_client(ps, w);
win_clear_flags(w, WIN_FLAGS_CLIENT_STALE);
}
} }
/** /**
@ -542,11 +566,15 @@ bool win_client_has_alpha(const struct managed_win *w) {
w->client_pictfmt->direct.alpha_mask; w->client_pictfmt->direct.alpha_mask;
} }
winmode_t win_calc_mode(const struct managed_win *w) { winmode_t win_calc_mode(session_t *ps, const struct managed_win *w) {
if (w->opacity < 1.0) { if (w->opacity < 1.0) {
return WMODE_TRANS; return WMODE_TRANS;
} }
if (ps->o.backend == BKEND_GLX && w->corner_radius > 0) {
return WMODE_TRANS;
}
if (win_has_alpha(w)) { if (win_has_alpha(w)) {
if (w->client_win == XCB_NONE) { if (w->client_win == XCB_NONE) {
// This is a window not managed by the WM, and it has alpha, // This is a window not managed by the WM, and it has alpha,
@ -586,17 +614,18 @@ winmode_t win_calc_mode(const struct managed_win *w) {
* *
* @param ps current session * @param ps current session
* @param w struct _win object representing the window * @param w struct _win object representing the window
* @param ignore_state whether window state should be ignored in opacity calculation
* *
* @return target opacity * @return target opacity
*/ */
double win_calc_opacity_target(session_t *ps, const struct managed_win *w) { double win_calc_opacity_target(session_t *ps, const struct managed_win *w, bool ignore_state) {
double opacity = 1; double opacity = 1;
if (w->state == WSTATE_UNMAPPED) { if (w->state == WSTATE_UNMAPPED && !ignore_state) {
// be consistent // be consistent
return 0; return 0;
} }
if (w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) { if ((w->state == WSTATE_UNMAPPING || w->state == WSTATE_DESTROYING) && !ignore_state) {
return 0; return 0;
} }
// Try obeying opacity property and window type opacity firstly // Try obeying opacity property and window type opacity firstly
@ -697,7 +726,7 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
w->shadow = shadow_new; w->shadow = shadow_new;
assert(!w->shadow_image); assert(!w->shadow_image);
assert(!w->win_image); assert(!w->win_image);
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE)); //assert(w->flags & WIN_FLAGS_IMAGES_NONE);
return; return;
} }
@ -723,14 +752,14 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
// asserting the existence of the shadow image. // asserting the existence of the shadow image.
if (w->shadow) { if (w->shadow) {
// Mark the new extents as damaged if the shadow is added // Mark the new extents as damaged if the shadow is added
assert(!w->shadow_image || win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) || assert(!w->shadow_image || (w->flags & WIN_FLAGS_SHADOW_STALE) ||
!ps->o.experimental_backends); !ps->o.experimental_backends);
pixman_region32_clear(&extents); pixman_region32_clear(&extents);
win_extents(w, &extents); win_extents(w, &extents);
add_damage_from_win(ps, w); add_damage_from_win(ps, w);
} else { } else {
// Mark the old extents as damaged if the shadow is removed // Mark the old extents as damaged if the shadow is removed
assert(w->shadow_image || win_check_flags_all(w, WIN_FLAGS_SHADOW_STALE) || assert(w->shadow_image || (w->flags & WIN_FLAGS_SHADOW_STALE) ||
!ps->o.experimental_backends); !ps->o.experimental_backends);
add_damage(ps, &extents); add_damage(ps, &extents);
} }
@ -740,7 +769,7 @@ static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new
// Delayed update of shadow image // Delayed update of shadow image
// By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to re-create or // By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to re-create or
// release the shaodw in based on whether w->shadow is set. // release the shaodw in based on whether w->shadow is set.
win_set_flags(w, WIN_FLAGS_SHADOW_STALE); w->flags |= WIN_FLAGS_SHADOW_STALE;
ps->pending_updates = true; ps->pending_updates = true;
} }
@ -878,6 +907,48 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w)
win_set_blur_background(ps, w, blur_background_new); win_set_blur_background(ps, w, blur_background_new);
} }
/**
* Determine if a window should have rounded corners.
*/
static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
if (w->a.map_state != XCB_MAP_STATE_VIEWABLE /*|| ps->o.corner_radius == 0*/)
return;
// Don't round full screen windows & excluded windows
if ((w && win_is_fullscreen(ps, w)) ||
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL)) {
w->corner_radius = 0;
//log_warn("xy(%d %d) wh(%d %d) will NOT round corners", w->g.x, w->g.y, w->widthb, w->heightb);
} else {
w->corner_radius = ps->o.corner_radius;
//log_warn("xy(%d %d) wh(%d %d) will round corners", w->g.x, w->g.y, w->widthb, w->heightb);
// HACK: we reset this so we can query the color once
// we query the color in glx_round_corners_dst0 using glReadPixels
//w->border_col = { -1., -1, -1, -1 };
w->border_col[0] = w->border_col[1] = w->border_col[2] = w->border_col[3] = -1.0;
// wintypes config section override
if (!safe_isnan(ps->o.wintype_option[w->window_type].corner_radius) &&
ps->o.wintype_option[w->window_type].corner_radius >= 0) {
w->corner_radius = ps->o.wintype_option[w->window_type].corner_radius;
//log_warn("xy(%d %d) wh(%d %d) wintypes:corner_radius: %d", w->g.x, w->g.y, w->widthb, w->heightb, w->corner_radius);
}
if (w && c2_match(ps, w, ps->o.round_borders_blacklist, NULL)) {
w->round_borders = 0;
} else {
w->round_borders = ps->o.round_borders;
// wintypes config section override
if (!safe_isnan(ps->o.wintype_option[w->window_type].round_borders) &&
ps->o.wintype_option[w->window_type].round_borders >= 0) {
w->round_borders = ps->o.wintype_option[w->window_type].round_borders;
//log_warn("wintypes:round_borders: %d", w->round_borders);
}
}
}
}
/** /**
* Update window opacity according to opacity rules. * Update window opacity according to opacity rules.
*/ */
@ -904,7 +975,6 @@ void win_update_opacity_rule(session_t *ps, struct managed_win *w) {
* TODO need better name * TODO need better name
*/ */
void win_on_factor_change(session_t *ps, struct managed_win *w) { void win_on_factor_change(session_t *ps, struct managed_win *w) {
log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
// Focus needs to be updated first, as other rules might depend on the focused // Focus needs to be updated first, as other rules might depend on the focused
// state of the window // state of the window
win_update_focused(ps, w); win_update_focused(ps, w);
@ -912,8 +982,7 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
win_determine_shadow(ps, w); win_determine_shadow(ps, w);
win_determine_invert_color(ps, w); win_determine_invert_color(ps, w);
win_determine_blur_background(ps, w); win_determine_blur_background(ps, w);
w->mode = win_calc_mode(w); win_determine_rounded_corners(ps, w);
log_debug("Window mode changed to %d", w->mode);
win_update_opacity_rule(ps, w); win_update_opacity_rule(ps, w);
if (w->a.map_state == XCB_MAP_STATE_VIEWABLE) if (w->a.map_state == XCB_MAP_STATE_VIEWABLE)
w->paint_excluded = c2_match(ps, w, ps->o.paint_blacklist, NULL); w->paint_excluded = c2_match(ps, w, ps->o.paint_blacklist, NULL);
@ -921,7 +990,15 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
w->unredir_if_possible_excluded = w->unredir_if_possible_excluded =
c2_match(ps, w, ps->o.unredir_if_possible_blacklist, NULL); c2_match(ps, w, ps->o.unredir_if_possible_blacklist, NULL);
win_update_opacity_target(ps, w); auto opacity_target_old = w->opacity_target;
w->opacity_target = win_calc_opacity_target(ps, w, false);
if (opacity_target_old != w->opacity_target && w->state == WSTATE_MAPPED) {
// Only MAPPED can transition to FADING
w->state = WSTATE_FADING;
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
}
w->reg_ignore_valid = false; w->reg_ignore_valid = false;
} }
@ -940,7 +1017,7 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w) {
// Invalidate the shadow we built // Invalidate the shadow we built
if (w->state == WSTATE_MAPPED || w->state == WSTATE_MAPPING || if (w->state == WSTATE_MAPPED || w->state == WSTATE_MAPPING ||
w->state == WSTATE_FADING) { w->state == WSTATE_FADING) {
win_set_flags(w, WIN_FLAGS_IMAGES_STALE); w->flags |= WIN_FLAGS_IMAGES_STALE;
ps->pending_updates = true; ps->pending_updates = true;
} else { } else {
assert(w->state == WSTATE_UNMAPPED); assert(w->state == WSTATE_UNMAPPED);
@ -1014,9 +1091,9 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
win_on_factor_change(ps, w); win_on_factor_change(ps, w);
auto r = xcb_get_window_attributes_reply( auto r = xcb_get_window_attributes_reply(
ps->c, xcb_get_window_attributes(ps->c, w->client_win), &e); ps->c, xcb_get_window_attributes(ps->c, w->client_win), NULL);
if (!r) { if (!r) {
log_error_x_error(e, "Failed to get client window attributes"); log_error("Failed to get client window attributes");
return; return;
} }
@ -1032,8 +1109,6 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
*/ */
void win_unmark_client(session_t *ps, struct managed_win *w) { void win_unmark_client(session_t *ps, struct managed_win *w) {
xcb_window_t client = w->client_win; xcb_window_t client = w->client_win;
log_debug("Detaching client window %#010x from frame %#010x (%s)", client,
w->base.id, w->name);
w->client_win = XCB_NONE; w->client_win = XCB_NONE;
@ -1043,42 +1118,13 @@ void win_unmark_client(session_t *ps, struct managed_win *w) {
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)}); (const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)});
} }
/**
* Look for the client window of a particular window.
*/
static xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
if (wid_has_prop(ps, w, ps->atoms->aWM_STATE)) {
return w;
}
xcb_query_tree_reply_t *reply =
xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
if (!reply)
return 0;
xcb_window_t *children = xcb_query_tree_children(reply);
int nchildren = xcb_query_tree_children_length(reply);
int i;
xcb_window_t ret = 0;
for (i = 0; i < nchildren; ++i) {
if ((ret = find_client_win(ps, children[i])))
break;
}
free(reply);
return ret;
}
/** /**
* Recheck client window of a window. * Recheck client window of a window.
* *
* @param ps current session * @param ps current session
* @param w struct _win of the parent window * @param w struct _win of the parent window
*/ */
void win_recheck_client(session_t *ps, struct managed_win *w) { static void win_recheck_client(session_t *ps, struct managed_win *w) {
assert(ps->server_grabbed);
// Initialize wmwin to false // Initialize wmwin to false
w->wmwin = false; w->wmwin = false;
@ -1088,14 +1134,14 @@ void win_recheck_client(session_t *ps, struct managed_win *w) {
// sets override-redirect flags on all frame windows. // sets override-redirect flags on all frame windows.
xcb_window_t cw = find_client_win(ps, w->base.id); xcb_window_t cw = find_client_win(ps, w->base.id);
if (cw) { if (cw) {
log_debug("(%#010x): client %#010x", w->base.id, cw); log_trace("(%#010x): client %#010x", w->base.id, cw);
} }
// Set a window's client window to itself if we couldn't find a // Set a window's client window to itself if we couldn't find a
// client window // client window
if (!cw) { if (!cw) {
cw = w->base.id; cw = w->base.id;
w->wmwin = !w->a.override_redirect; w->wmwin = !w->a.override_redirect;
log_debug("(%#010x): client self (%s)", w->base.id, log_trace("(%#010x): client self (%s)", w->base.id,
(w->wmwin ? "wmwin" : "override-redirected")); (w->wmwin ? "wmwin" : "override-redirected"));
} }
@ -1191,10 +1237,10 @@ struct win *fill_win(session_t *ps, struct win *w) {
.invert_color = false, .invert_color = false,
.blur_background = false, .blur_background = false,
.oldX = -10000, .oldX = -10000,
.oldY = -10000, .oldY = -10000,
.oldW = 0, .oldW = 0,
.oldH = 0, .oldH = 0,
.reg_ignore = NULL, .reg_ignore = NULL,
// The following ones are updated for other reasons // The following ones are updated for other reasons
@ -1262,6 +1308,8 @@ struct win *fill_win(session_t *ps, struct win *w) {
// Initialized during paint // Initialized during paint
.paint = PAINT_INIT, .paint = PAINT_INIT,
.shadow_paint = PAINT_INIT, .shadow_paint = PAINT_INIT,
.corner_radius = 0,
}; };
assert(!w->destroyed); assert(!w->destroyed);
@ -1305,6 +1353,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
// Allocate and initialize the new win structure // Allocate and initialize the new win structure
auto new_internal = cmalloc(struct managed_win_internal); auto new_internal = cmalloc(struct managed_win_internal);
auto new = (struct managed_win *)new_internal; auto new = (struct managed_win *)new_internal;
new_internal->pending_updates = 0;
// Fill structure // Fill structure
// We only need to initialize the part that are not initialized // We only need to initialize the part that are not initialized
@ -1550,7 +1599,7 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
pixman_region32_clear(&w->bounding_shape); pixman_region32_clear(&w->bounding_shape);
// Start with the window rectangular region // Start with the window rectangular region
win_get_region_local(w, &w->bounding_shape); win_get_region_local(w, &w->bounding_shape, true);
// Only request for a bounding region if the window is shaped // Only request for a bounding region if the window is shaped
// (while loop is used to avoid goto, not an actual loop) // (while loop is used to avoid goto, not an actual loop)
@ -1602,7 +1651,7 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
// Note we only do this when screen is redirected, because // Note we only do this when screen is redirected, because
// otherwise win_data is not valid // otherwise win_data is not valid
assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING); assert(w->state != WSTATE_UNMAPPING && w->state != WSTATE_DESTROYING);
win_set_flags(w, WIN_FLAGS_IMAGES_STALE); w->flags |= WIN_FLAGS_IMAGES_STALE;
ps->pending_updates = true; ps->pending_updates = true;
} }
free_paint(ps, &w->paint); free_paint(ps, &w->paint);
@ -1716,7 +1765,7 @@ static void unmap_win_finish(session_t *ps, struct managed_win *w) {
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); w->flags &= ~WIN_FLAGS_IMAGE_ERROR;
} }
/// Finish the destruction of a window (e.g. after fading has finished). /// Finish the destruction of a window (e.g. after fading has finished).
@ -1778,7 +1827,7 @@ static void destroy_win_finish(session_t *ps, struct win *w) {
static void map_win_finish(struct managed_win *w) { static void map_win_finish(struct managed_win *w) {
w->in_openclose = false; w->in_openclose = false;
w->isOld = true; w->isOld = true;
w->state = WSTATE_MAPPED; w->state = WSTATE_MAPPED;
} }
@ -1890,15 +1939,14 @@ bool destroy_win_start(session_t *ps, struct win *w) {
} }
if (w->managed) { if (w->managed) {
// Clear PIXMAP_STALE flag, since the window is destroyed there is no
// pixmap available so STALE doesn't make sense.
// Do this before changing the window state to destroying
win_clear_flags(mw, WIN_FLAGS_PIXMAP_STALE);
// Update state flags of a managed window // Update state flags of a managed window
mw->state = WSTATE_DESTROYING; mw->state = WSTATE_DESTROYING;
mw->a.map_state = XCB_MAP_STATE_UNMAPPED; mw->a.map_state = XCB_MAP_STATE_UNMAPPED;
mw->in_openclose = true; mw->in_openclose = true;
// Clear PIXMAP_STALE flag, since the window is destroyed there is no
// pixmap available so STALE doesn't make sense.
mw->flags &= ~WIN_FLAGS_PIXMAP_STALE;
} }
// don't need win_ev_stop because the window is gone anyway // don't need win_ev_stop because the window is gone anyway
@ -1918,6 +1966,7 @@ bool destroy_win_start(session_t *ps, struct win *w) {
} }
void unmap_win_start(session_t *ps, struct managed_win *w) { void unmap_win_start(session_t *ps, struct managed_win *w) {
auto internal_w = (struct managed_win_internal *)w;
assert(w); assert(w);
assert(w->base.managed); assert(w->base.managed);
assert(w->a._class != XCB_WINDOW_CLASS_INPUT_ONLY); assert(w->a._class != XCB_WINDOW_CLASS_INPUT_ONLY);
@ -1930,9 +1979,8 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
} }
if (unlikely(w->state == WSTATE_UNMAPPING || w->state == WSTATE_UNMAPPED)) { if (unlikely(w->state == WSTATE_UNMAPPING || w->state == WSTATE_UNMAPPED)) {
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) { if (internal_w->pending_updates & WIN_UPDATE_MAP) {
// Clear the pending map as this window is now unmapped internal_w->pending_updates &= ~(unsigned long)WIN_UPDATE_MAP;
win_clear_flags(w, WIN_FLAGS_MAPPED);
} else { } else {
log_warn("Trying to unmapping an already unmapped window %#010x " log_warn("Trying to unmapping an already unmapped window %#010x "
"\"%s\"", "\"%s\"",
@ -1947,12 +1995,11 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
w->a.map_state = XCB_MAP_STATE_UNMAPPED; w->a.map_state = XCB_MAP_STATE_UNMAPPED;
w->state = WSTATE_UNMAPPING; w->state = WSTATE_UNMAPPING;
w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old); w->opacity_target = win_calc_opacity_target(ps, w, false);
w->opacity_target = win_calc_opacity_target(ps, w);
// Clear PIXMAP_STALE flag, since the window is unmapped there is no pixmap // Clear PIXMAP_STALE flag, since the window is unmapped there is no pixmap
// available so STALE doesn't make sense. // available so STALE doesn't make sense.
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE); w->flags &= ~WIN_FLAGS_PIXMAP_STALE;
// don't care about properties anymore // don't care about properties anymore
win_ev_stop(ps, &w->base); win_ev_stop(ps, &w->base);
@ -2059,7 +2106,8 @@ void map_win_start(session_t *ps, struct managed_win *w) {
} }
assert(w->state == WSTATE_UNMAPPED); assert(w->state == WSTATE_UNMAPPED);
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_NONE) || !ps->o.experimental_backends); assert((w->flags & WIN_FLAGS_IMAGES_NONE) == WIN_FLAGS_IMAGES_NONE ||
!ps->o.experimental_backends);
// We stopped processing window size change when we were unmapped, refresh the // We stopped processing window size change when we were unmapped, refresh the
// size of the window // size of the window
@ -2087,13 +2135,14 @@ void map_win_start(session_t *ps, struct managed_win *w) {
// XXX Can we assume map_state is always viewable? // XXX Can we assume map_state is always viewable?
w->a.map_state = XCB_MAP_STATE_VIEWABLE; w->a.map_state = XCB_MAP_STATE_VIEWABLE;
if (!w->isOld) { if (!w->isOld) {
w->oldX = -1000; w->oldX = -10000;
w->oldY = -1000; w->oldY = -10000;
w->oldW = 0; w->oldW = 0;
w->oldH = 0; w->oldH = 0;
} }
win_update_screen(ps, w); win_update_screen(ps, w);
// Set window event mask before reading properties so that no property // Set window event mask before reading properties so that no property
@ -2108,7 +2157,7 @@ void map_win_start(session_t *ps, struct managed_win *w) {
} }
// Update window mode here to check for ARGB windows // Update window mode here to check for ARGB windows
w->mode = win_calc_mode(w); w->mode = win_calc_mode(ps, w);
// Detect client window here instead of in add_win() as the client // Detect client window here instead of in add_win() as the client
// window should have been prepared at this point // window should have been prepared at this point
@ -2139,13 +2188,13 @@ void map_win_start(session_t *ps, struct managed_win *w) {
// XXX We need to make sure that win_data is available // XXX We need to make sure that win_data is available
// iff `state` is MAPPED // iff `state` is MAPPED
w->state = WSTATE_MAPPING; w->state = WSTATE_MAPPING;
w->opacity_target_old = 0; w->opacity_target = win_calc_opacity_target(ps, w, false);
w->opacity_target = win_calc_opacity_target(ps, w);
log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id, log_debug("Window %#010x has opacity %f, opacity target is %f", w->base.id,
w->opacity, w->opacity_target); w->opacity, w->opacity_target);
win_determine_blur_background(ps, w); win_determine_blur_background(ps, w);
win_determine_rounded_corners(ps, w);
// Cannot set w->ever_damaged = false here, since window mapping could be // Cannot set w->ever_damaged = false here, since window mapping could be
// delayed, so a damage event might have already arrived before this function // delayed, so a damage event might have already arrived before this function
@ -2161,7 +2210,7 @@ void map_win_start(session_t *ps, struct managed_win *w) {
// the window's image will be bound // the window's image will be bound
win_update_bounding_shape(ps, w); win_update_bounding_shape(ps, w);
assert(win_check_flags_all(w, WIN_FLAGS_IMAGES_STALE)); assert((w->flags & WIN_FLAGS_IMAGES_STALE) == WIN_FLAGS_IMAGES_STALE);
#ifdef CONFIG_DBUS #ifdef CONFIG_DBUS
// Send D-Bus signal // Send D-Bus signal
@ -2175,60 +2224,6 @@ void map_win_start(session_t *ps, struct managed_win *w) {
} }
} }
/**
* Update target window opacity depending on the current state.
*/
void win_update_opacity_target(session_t *ps, struct managed_win *w) {
auto opacity_target_old = w->opacity_target;
w->opacity_target = win_calc_opacity_target(ps, w);
if (opacity_target_old == w->opacity_target) {
return;
}
if (w->state == WSTATE_MAPPED) {
// Opacity target changed while MAPPED. Transition to FADING.
assert(w->opacity == opacity_target_old);
w->opacity_target_old = opacity_target_old;
w->state = WSTATE_FADING;
log_debug("Window %#010x (%s) opacity %f, opacity target %f, set "
"old target %f",
w->base.id, w->name, w->opacity, w->opacity_target,
w->opacity_target_old);
} else if (w->state == WSTATE_MAPPING) {
// Opacity target changed while fading in.
if (w->opacity >= w->opacity_target) {
// Already reached new target opacity. Transition to
// FADING.
map_win_finish(w);
w->opacity_target_old = fmax(opacity_target_old, w->opacity);
w->state = WSTATE_FADING;
log_debug("Window %#010x (%s) opacity %f already reached "
"new opacity target %f while mapping, set old "
"target %f",
w->base.id, w->name, w->opacity, w->opacity_target,
w->opacity_target_old);
}
} else if (w->state == WSTATE_FADING) {
// Opacity target changed while FADING.
if ((w->opacity < opacity_target_old && w->opacity > w->opacity_target) ||
(w->opacity > opacity_target_old && w->opacity < w->opacity_target)) {
// Changed while fading in and will fade out or while
// fading out and will fade in.
w->opacity_target_old = opacity_target_old;
log_debug("Window %#010x (%s) opacity %f already reached "
"new opacity target %f while fading, set "
"old target %f",
w->base.id, w->name, w->opacity, w->opacity_target,
w->opacity_target_old);
}
}
if (!ps->redirected) {
CHECK(!win_skip_fading(ps, w));
}
}
/** /**
* Find a managed window from window id in window linked list of the session. * Find a managed window from window id in window linked list of the session.
*/ */
@ -2284,13 +2279,13 @@ struct managed_win *find_toplevel(session_t *ps, xcb_window_t id) {
} }
/** /**
* Find a managed window that is, or is a parent of `wid`. * Find out the WM frame of a client window by querying X.
* *
* @param ps current session * @param ps current session
* @param wid window ID * @param wid window ID
* @return struct _win object of the found window, NULL if not found * @return struct _win object of the found window, NULL if not found
*/ */
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid) { struct managed_win *find_toplevel2(session_t *ps, xcb_window_t wid) {
// TODO this should probably be an "update tree", then find_toplevel. // TODO this should probably be an "update tree", then find_toplevel.
// current approach is a bit more "racy" // current approach is a bit more "racy"
struct win *w = NULL; struct win *w = NULL;
@ -2351,35 +2346,31 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo
return false; return false;
} }
/// Set flags on a window. Some sanity checks are performed /// Queue an update on a window. A series of sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags) { void win_queue_update(struct managed_win *_w, enum win_update update) {
log_debug("Set flags %lu to window %#010x (%s)", flags, w->base.id, w->name); auto w = (struct managed_win_internal *)_w;
if (unlikely(w->state == WSTATE_DESTROYING)) { assert(popcount(update) == 1);
log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name); assert(update == WIN_UPDATE_MAP); // Currently the only supported update
if (unlikely(_w->state == WSTATE_DESTROYING)) {
log_error("Updates queued on a destroyed window %#010x (%s)", _w->base.id,
_w->name);
return; return;
} }
w->flags |= flags; w->pending_updates |= update;
} }
/// Clear flags on a window. Some sanity checks are performed /// Process pending updates on a window. Has to be called in X critical section
void win_clear_flags(struct managed_win *w, uint64_t flags) { void win_process_updates(struct session *ps, struct managed_win *_w) {
log_debug("Clear flags %lu from window %#010x (%s)", flags, w->base.id, w->name); assert(ps->server_grabbed);
if (unlikely(w->state == WSTATE_DESTROYING)) { auto w = (struct managed_win_internal *)_w;
log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
w->name); if (w->pending_updates & WIN_UPDATE_MAP) {
return; map_win_start(ps, _w);
} }
w->flags = w->flags & (~flags); w->pending_updates = 0;
}
bool win_check_flags_any(struct managed_win *w, uint64_t flags) {
return (w->flags & flags) != 0;
}
bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
return (w->flags & flags) == flags;
} }
/** /**
@ -2435,6 +2426,7 @@ win_stack_find_next_managed(const session_t *ps, const struct list_node *i) {
/// Return whether this window is mapped on the X server side /// 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) {
auto iw = (const struct managed_win_internal *)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 || (iw->pending_updates & WIN_UPDATE_MAP);
} }

View File

@ -40,13 +40,13 @@ typedef struct _glx_texture glx_texture_t;
// it is very unideal for it to be here // it is very unideal for it to be here
typedef struct { typedef struct {
/// Framebuffer used for blurring. /// Framebuffer used for blurring.
GLuint fbo; GLuint fbos[MAX_BLUR_PASS];
/// Textures used for blurring. /// Textures used for blurring.
GLuint textures[2]; GLuint textures[MAX_BLUR_PASS];
/// Width of the textures. /// Width of the textures.
int width; int width[MAX_BLUR_PASS];
/// Height of the textures. /// Height of the textures.
int height; int height[MAX_BLUR_PASS];
} glx_blur_cache_t; } glx_blur_cache_t;
#endif #endif
@ -131,7 +131,7 @@ struct managed_win {
/// See above about coordinate systems. /// See above about coordinate systems.
region_t bounding_shape; region_t bounding_shape;
/// Window flags. Definitions above. /// Window flags. Definitions above.
uint64_t flags; int_fast16_t flags;
/// The region of screen that will be obscured when windows above is painted, /// The region of screen that will be obscured when windows above is painted,
/// in global coordinates. /// in global coordinates.
/// We use this to reduce the pixels that needed to be paint when painting /// We use this to reduce the pixels that needed to be paint when painting
@ -192,8 +192,6 @@ struct managed_win {
double opacity; double opacity;
/// Target window opacity. /// Target window opacity.
double opacity_target; double opacity_target;
/// Previous window opacity.
double opacity_target_old;
/// true if window (or client window, for broken window managers /// true if window (or client window, for broken window managers
/// not transferring client window's _NET_WM_OPACITY value) has opacity prop /// not transferring client window's _NET_WM_OPACITY value) has opacity prop
bool has_opacity_prop; bool has_opacity_prop;
@ -204,6 +202,11 @@ struct managed_win {
/// Last window opacity value set by the rules. /// Last window opacity value set by the rules.
double opacity_set; double opacity_set;
/// Corner radius
int corner_radius;
bool round_borders;
float border_col[4];
// Fading-related members // Fading-related members
/// Override value of window fade state. Set by D-Bus method calls. /// Override value of window fade state. Set by D-Bus method calls.
switch_t fade_force; switch_t fade_force;
@ -248,21 +251,29 @@ struct managed_win {
/// Whether to blur window background. /// Whether to blur window background.
bool blur_background; bool blur_background;
/// Animation state /// Animation state
int oldX; int oldY; int oldW; int oldH; int oldX; int oldY; int oldW; int oldH;
int newX; int newY; int newW; int newH; int newX; int newY; int newW; int newH;
float moveTimeX; float moveTimeY; float moveTimeX; float moveTimeY;
float moveTimeW; float moveTimeH; float moveTimeW; float moveTimeH;
bool isOld; bool isOld;
#ifdef CONFIG_OPENGL #ifdef CONFIG_OPENGL
/// Textures and FBO background blur use. /// Textures and FBO background blur use.
glx_blur_cache_t glx_blur_cache; glx_blur_cache_t glx_blur_cache;
glx_blur_cache_t glx_round_cache;
/// Background texture of the window
glx_texture_t *glx_texture_bg;
#endif #endif
}; };
/// Process pending updates on a window. Has to be called in X critical section
void win_process_updates(struct session *ps, struct managed_win *_w);
/// Process pending images flags on a window. Has to be called in X critical section /// Process pending images flags on a window. Has to be called in X critical section
void win_process_flags(session_t *ps, struct managed_win *w); void win_process_flags(session_t *ps, struct managed_win *w);
/// Queue an update on a window. A series of sanity checks are performed
void win_queue_update(struct managed_win *_w, enum win_update update);
/// Bind a shadow to the window, with color `c` and shadow kernel `kernel` /// Bind a shadow to the window, with color `c` and shadow kernel `kernel`
bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c, bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color c,
struct conv *kernel); struct conv *kernel);
@ -284,7 +295,7 @@ bool must_use destroy_win_start(session_t *ps, struct win *w);
void win_release_images(struct backend_base *base, struct managed_win *w); void win_release_images(struct backend_base *base, struct managed_win *w);
int win_update_name(session_t *ps, struct managed_win *w); int win_update_name(session_t *ps, struct managed_win *w);
int win_get_role(session_t *ps, struct managed_win *w); int win_get_role(session_t *ps, struct managed_win *w);
winmode_t attr_pure win_calc_mode(const struct managed_win *w); winmode_t attr_pure win_calc_mode(session_t *ps, const struct managed_win *w);
void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val); void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val);
void win_set_fade_force(struct managed_win *w, switch_t val); void win_set_fade_force(struct managed_win *w, switch_t val);
void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val); void win_set_focused_force(session_t *ps, struct managed_win *w, switch_t val);
@ -296,7 +307,6 @@ void win_set_focused(session_t *ps, struct managed_win *w);
bool attr_pure win_should_fade(session_t *ps, const struct managed_win *w); bool attr_pure win_should_fade(session_t *ps, const struct managed_win *w);
void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w); void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
void win_update_prop_shadow(session_t *ps, struct managed_win *w); void win_update_prop_shadow(session_t *ps, struct managed_win *w);
void win_update_opacity_target(session_t *ps, struct managed_win *w);
void win_on_factor_change(session_t *ps, struct managed_win *w); void win_on_factor_change(session_t *ps, struct managed_win *w);
/** /**
* Update cache data in struct _win that depends on window size. * Update cache data in struct _win that depends on window size.
@ -305,7 +315,6 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w);
void win_update_wintype(session_t *ps, struct managed_win *w); void win_update_wintype(session_t *ps, struct managed_win *w);
void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client); void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client);
void win_unmark_client(session_t *ps, struct managed_win *w); void win_unmark_client(session_t *ps, struct managed_win *w);
void win_recheck_client(session_t *ps, struct managed_win *w);
bool win_get_class(session_t *ps, struct managed_win *w); bool win_get_class(session_t *ps, struct managed_win *w);
/** /**
@ -318,10 +327,12 @@ bool win_get_class(session_t *ps, struct managed_win *w);
* *
* @param ps current session * @param ps current session
* @param w struct _win object representing the window * @param w struct _win object representing the window
* @param ignore_state whether window state should be ignored in opacity calculation
* *
* @return target opacity * @return target opacity
*/ */
double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w); double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w,
bool ignore_state);
bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w); bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
void win_update_screen(session_t *, struct managed_win *); void win_update_screen(session_t *, struct managed_win *);
/** /**
@ -362,12 +373,12 @@ void add_damage_from_win(session_t *ps, const struct managed_win *w);
* *
* Return region in global coordinates. * Return region in global coordinates.
*/ */
void win_get_region_noframe_local(const struct managed_win *w, region_t *); void win_get_region_noframe_local(const struct managed_win *w, region_t *, bool include_corners);
/// Get the region for the frame of the window /// Get the region for the frame of the window
void win_get_region_frame_local(const struct managed_win *w, region_t *res); void win_get_region_frame_local(const struct managed_win *w, region_t *res, bool include_corners);
/// Get the region for the frame of the window, by value /// Get the region for the frame of the window, by value
region_t win_get_region_frame_local_by_val(const struct managed_win *w); region_t win_get_region_frame_local_by_val(const struct managed_win *w, bool include_corners);
/** /**
* Retrieve frame extents from a window. * Retrieve frame extents from a window.
*/ */
@ -407,13 +418,13 @@ struct managed_win *find_managed_win(session_t *ps, xcb_window_t id);
struct win *find_win(session_t *ps, xcb_window_t id); struct win *find_win(session_t *ps, xcb_window_t id);
struct managed_win *find_toplevel(session_t *ps, xcb_window_t id); struct managed_win *find_toplevel(session_t *ps, xcb_window_t id);
/** /**
* Find a managed window that is, or is a parent of `wid`. * Find out the WM frame of a client window by querying X.
* *
* @param ps current session * @param ps current session
* @param wid window ID * @param wid window ID
* @return struct _win object of the found window, NULL if not found * @return struct _win object of the found window, NULL if not found
*/ */
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid); struct managed_win *find_toplevel2(session_t *ps, xcb_window_t wid);
/** /**
* Check if a window is a fullscreen window. * Check if a window is a fullscreen window.
@ -439,22 +450,29 @@ bool win_is_mapped_in_x(const struct managed_win *w);
// Find the managed window immediately below `w` in the window stack // Find the managed window immediately below `w` in the window stack
struct managed_win *attr_pure win_stack_find_next_managed(const session_t *ps, struct managed_win *attr_pure win_stack_find_next_managed(const session_t *ps,
const struct list_node *w); const struct list_node *w);
/// Set flags on a window. Some sanity checks are performed
void win_set_flags(struct managed_win *w, uint64_t flags);
/// Clear flags on a window. Some sanity checks are performed
void win_clear_flags(struct managed_win *w, uint64_t flags);
/// Returns true if any of the flags in `flags` is set
bool win_check_flags_any(struct managed_win *w, uint64_t flags);
/// Returns true if all of the flags in `flags` are set
bool win_check_flags_all(struct managed_win *w, uint64_t flags);
/// Free all resources in a struct win /// Free all resources in a struct win
void free_win_res(session_t *ps, struct managed_win *w); void free_win_res(session_t *ps, struct managed_win *w);
static inline region_t win_get_bounding_shape_global_by_val(struct managed_win *w) { static inline void win_region_remove_corners(const struct managed_win *w, region_t *res) {
region_t corners;
pixman_region32_init_rects(
&corners,
(rect_t[]){
{.x1 = 0, .y1 = 0, .x2 = w->corner_radius, .y2 = w->corner_radius},
{.x1 = 0, .y1 = w->heightb-w->corner_radius, .x2 = w->corner_radius, .y2 = w->heightb},
{.x1 = w->widthb-w->corner_radius, .y1 = 0, .x2 = w->widthb, .y2 = w->corner_radius},
{.x1 = w->widthb-w->corner_radius, .y1 = w->heightb-w->corner_radius, .x2 = w->widthb, .y2 = w->heightb},
},
4);
pixman_region32_subtract(res, res, &corners);
}
static inline region_t win_get_bounding_shape_global_by_val(struct managed_win *w, bool include_corners) {
region_t ret; region_t ret;
pixman_region32_init(&ret); pixman_region32_init(&ret);
pixman_region32_copy(&ret, &w->bounding_shape); pixman_region32_copy(&ret, &w->bounding_shape);
if(!include_corners) win_region_remove_corners(w, &ret);
pixman_region32_translate(&ret, w->g.x, w->g.y); pixman_region32_translate(&ret, w->g.x, w->g.y);
return ret; return ret;
} }

View File

@ -27,6 +27,11 @@ typedef enum {
WMODE_SOLID, // The window is opaque including the frame WMODE_SOLID, // The window is opaque including the frame
} winmode_t; } winmode_t;
/// Pending window updates
enum win_update {
WIN_UPDATE_MAP = 1,
};
/// Transition table: /// Transition table:
/// (DESTROYED is when the win struct is destroyed and freed) /// (DESTROYED is when the win struct is destroyed and freed)
/// ('o' means in all other cases) /// ('o' means in all other cases)
@ -40,8 +45,8 @@ typedef enum {
/// | DESTROYING | - | o | - | - | - | - | Fading | /// | DESTROYING | - | o | - | - | - | - | Fading |
/// | | | | | | | |finished | /// | | | | | | | |finished |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+ /// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | MAPPING | Window | Window | o |Opacity| - | Fading | - | /// | MAPPING | Window | Window | o | - | - | Fading | - |
/// | |unmapped |destroyed | |change | |finished| | /// | |unmapped |destroyed | | | |finished| |
/// +-------------+---------+----------+-------+-------+--------+--------+---------+ /// +-------------+---------+----------+-------+-------+--------+--------+---------+
/// | FADING | Window | Window | - | o | - | Fading | - | /// | FADING | Window | Window | - | o | - | Fading | - |
/// | |unmapped |destroyed | | | |finished| | /// | |unmapped |destroyed | | | |finished| |
@ -81,13 +86,9 @@ enum win_flags {
WIN_FLAGS_SHADOW_STALE = 8, WIN_FLAGS_SHADOW_STALE = 8,
/// shadow has not been generated /// shadow has not been generated
WIN_FLAGS_SHADOW_NONE = 16, WIN_FLAGS_SHADOW_NONE = 16,
/// the client window needs to be updated
WIN_FLAGS_CLIENT_STALE = 32,
/// the window is mapped by X, we need to call map_win_start for it
WIN_FLAGS_MAPPED = 64,
}; };
static const uint64_t WIN_FLAGS_IMAGES_STALE = static const int_fast16_t WIN_FLAGS_IMAGES_STALE =
WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_SHADOW_STALE; WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_SHADOW_STALE;
#define WIN_FLAGS_IMAGES_NONE (WIN_FLAGS_PIXMAP_NONE | WIN_FLAGS_SHADOW_NONE) #define WIN_FLAGS_IMAGES_NONE (WIN_FLAGS_PIXMAP_NONE | WIN_FLAGS_SHADOW_NONE)

107
src/x.c
View File

@ -7,7 +7,6 @@
#include <pixman.h> #include <pixman.h>
#include <xcb/composite.h> #include <xcb/composite.h>
#include <xcb/damage.h> #include <xcb/damage.h>
#include <xcb/glx.h>
#include <xcb/render.h> #include <xcb/render.h>
#include <xcb/sync.h> #include <xcb/sync.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
@ -168,6 +167,15 @@ xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_
return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id); return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
} }
xcb_render_pictformat_t
x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) {
x_get_server_pictfmts(c);
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
return pictfmt->id;
}
int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) { int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
auto setup = xcb_get_setup(c); auto setup = xcb_get_setup(c);
for (auto screen = xcb_setup_roots_iterator(setup); screen.rem; for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
@ -231,6 +239,17 @@ x_create_picture_with_standard_and_pixmap(xcb_connection_t *c, xcb_pict_standard
return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr); return x_create_picture_with_pictfmt_and_pixmap(c, pictfmt, pixmap, valuemask, attr);
} }
xcb_render_picture_t
x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
xcb_pict_standard_t standard, uint32_t valuemask,
const xcb_render_create_picture_value_list_t *attr) {
x_get_server_pictfmts(c);
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
assert(pictfmt);
return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
}
/** /**
* Create an picture. * Create an picture.
*/ */
@ -338,80 +357,69 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c
int o = 0; int o = 0;
const char *name = "Unknown"; const char *name = "Unknown";
#define CASESTRRET(s) \
case s: \
name = #s; \
break
#define CASESTRRET2(s) \ #define CASESTRRET2(s) \
case XCB_##s: name = #s; break case s: name = #s; break
// TODO separate error code out from session_t // TODO separate error code out from session_t
o = error_code - ps->xfixes_error; o = error_code - ps->xfixes_error;
switch (o) { CASESTRRET2(XFIXES_BAD_REGION); } switch (o) { CASESTRRET2(XCB_XFIXES_BAD_REGION); }
o = error_code - ps->damage_error; o = error_code - ps->damage_error;
switch (o) { CASESTRRET2(DAMAGE_BAD_DAMAGE); } switch (o) { CASESTRRET2(XCB_DAMAGE_BAD_DAMAGE); }
o = error_code - ps->render_error; o = error_code - ps->render_error;
switch (o) { switch (o) {
CASESTRRET2(RENDER_PICT_FORMAT); CASESTRRET2(XCB_RENDER_PICT_FORMAT);
CASESTRRET2(RENDER_PICTURE); CASESTRRET2(XCB_RENDER_PICTURE);
CASESTRRET2(RENDER_PICT_OP); CASESTRRET2(XCB_RENDER_PICT_OP);
CASESTRRET2(RENDER_GLYPH_SET); CASESTRRET2(XCB_RENDER_GLYPH_SET);
CASESTRRET2(RENDER_GLYPH); CASESTRRET2(XCB_RENDER_GLYPH);
} }
#ifdef CONFIG_OPENGL
if (ps->glx_exists) { if (ps->glx_exists) {
o = error_code - ps->glx_error; o = error_code - ps->glx_error;
switch (o) { switch (o) {
CASESTRRET2(GLX_BAD_SCREEN);
CASESTRRET2(GLX_BAD_ATTRIBUTE);
CASESTRRET2(GLX_NO_EXTENSION);
CASESTRRET2(GLX_BAD_VISUAL);
CASESTRRET2(GLX_BAD_CONTEXT); CASESTRRET2(GLX_BAD_CONTEXT);
CASESTRRET2(GLX_BAD_CONTEXT_STATE); CASESTRRET2(GLX_BAD_VALUE);
CASESTRRET2(GLX_BAD_DRAWABLE); CASESTRRET2(GLX_BAD_ENUM);
CASESTRRET2(GLX_BAD_PIXMAP);
CASESTRRET2(GLX_BAD_CONTEXT_TAG);
CASESTRRET2(GLX_BAD_CURRENT_WINDOW);
CASESTRRET2(GLX_BAD_RENDER_REQUEST);
CASESTRRET2(GLX_BAD_LARGE_REQUEST);
CASESTRRET2(GLX_UNSUPPORTED_PRIVATE_REQUEST);
CASESTRRET2(GLX_BAD_FB_CONFIG);
CASESTRRET2(GLX_BAD_PBUFFER);
CASESTRRET2(GLX_BAD_CURRENT_DRAWABLE);
CASESTRRET2(GLX_BAD_WINDOW);
CASESTRRET2(GLX_GLX_BAD_PROFILE_ARB);
} }
} }
#endif
if (ps->xsync_exists) { if (ps->xsync_exists) {
o = error_code - ps->xsync_error; o = error_code - ps->xsync_error;
switch (o) { switch (o) {
CASESTRRET(XSyncBadCounter); CASESTRRET2(XSyncBadCounter);
CASESTRRET(XSyncBadAlarm); CASESTRRET2(XSyncBadAlarm);
CASESTRRET(XSyncBadFence); CASESTRRET2(XSyncBadFence);
} }
} }
switch (error_code) { switch (error_code) {
CASESTRRET2(ACCESS); CASESTRRET2(BadAccess);
CASESTRRET2(ALLOC); CASESTRRET2(BadAlloc);
CASESTRRET2(ATOM); CASESTRRET2(BadAtom);
CASESTRRET2(COLORMAP); CASESTRRET2(BadColor);
CASESTRRET2(CURSOR); CASESTRRET2(BadCursor);
CASESTRRET2(DRAWABLE); CASESTRRET2(BadDrawable);
CASESTRRET2(FONT); CASESTRRET2(BadFont);
CASESTRRET2(G_CONTEXT); CASESTRRET2(BadGC);
CASESTRRET2(ID_CHOICE); CASESTRRET2(BadIDChoice);
CASESTRRET2(IMPLEMENTATION); CASESTRRET2(BadImplementation);
CASESTRRET2(LENGTH); CASESTRRET2(BadLength);
CASESTRRET2(MATCH); CASESTRRET2(BadMatch);
CASESTRRET2(NAME); CASESTRRET2(BadName);
CASESTRRET2(PIXMAP); CASESTRRET2(BadPixmap);
CASESTRRET2(REQUEST); CASESTRRET2(BadRequest);
CASESTRRET2(VALUE); CASESTRRET2(BadValue);
CASESTRRET2(WINDOW); CASESTRRET2(BadWindow);
} }
#undef CASESTRRET
#undef CASESTRRET2 #undef CASESTRRET2
thread_local static char buffer[256]; thread_local static char buffer[256];
@ -434,9 +442,6 @@ void x_print_error(unsigned long serial, uint8_t major, uint16_t minor, uint8_t
* for multiple calls to this function, * for multiple calls to this function,
*/ */
const char *x_strerror(xcb_generic_error_t *e) { const char *x_strerror(xcb_generic_error_t *e) {
if (!e) {
return "No error";
}
return _x_strerror(e->full_sequence, e->major_code, e->minor_code, e->error_code); return _x_strerror(e->full_sequence, e->major_code, e->minor_code, e->error_code);
} }

View File

@ -172,6 +172,12 @@ x_create_picture_with_standard_and_pixmap(xcb_connection_t *, xcb_pict_standard_
const xcb_render_create_picture_value_list_t *attr) const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1); attr_nonnull(1);
xcb_render_picture_t
x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
xcb_pict_standard_t standard, uint32_t valuemask,
const xcb_render_create_picture_value_list_t *attr)
attr_nonnull(1);
/** /**
* Create an picture. * Create an picture.
*/ */
@ -261,6 +267,8 @@ struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual
xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std); xcb_visualid_t x_get_visual_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);
xcb_render_pictformat_t x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);
xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen); xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen);
uint32_t attr_deprecated xcb_generate_id(xcb_connection_t *c); uint32_t attr_deprecated xcb_generate_id(xcb_connection_t *c);

View File

@ -1,6 +0,0 @@
fading = true
fade-in-step = 0.01
fade-out-step = 0.01
inactive-opacity = 0
blur-background = true
force-win-blend = true

View File

@ -1,3 +0,0 @@
fading = true;
fade-in-step = 1;
fade-out-step = 0.01;

View File

@ -7,7 +7,7 @@ fi
echo "Running test $2" echo "Running test $2"
# TODO keep the log file, and parse it to see if test is successful # TODO keep the log file, and parse it to see if test is successful
($1 --dbus --experimental-backends --backend dummy --log-level=debug --log-file=$PWD/log --config=$2) & ($1 --experimental-backends --backend dummy --log-level=debug --log-file=$PWD/log --config=$2) &
main_pid=$! main_pid=$!
$3 $3

View File

@ -3,15 +3,8 @@ set -e
exe=$(realpath $1) exe=$(realpath $1)
cd $(dirname $0) cd $(dirname $0)
eval `dbus-launch --sh-syntax`
./run_one_test.sh $exe configs/empty.conf testcases/basic.py ./run_one_test.sh $exe configs/empty.conf testcases/basic.py
./run_one_test.sh $exe configs/issue357.conf testcases/issue357.py
./run_one_test.sh $exe configs/issue239.conf testcases/issue239.py ./run_one_test.sh $exe configs/issue239.conf testcases/issue239.py
./run_one_test.sh $exe configs/issue239_2.conf testcases/issue239_2.py ./run_one_test.sh $exe configs/issue239_2.conf testcases/issue239_2.py
./run_one_test.sh $exe configs/issue239_3.conf testcases/issue239_3.py ./run_one_test.sh $exe configs/issue239_3.conf testcases/issue239_3.py
./run_one_test.sh $exe configs/issue239_3.conf testcases/issue239_3_norefresh.py ./run_one_test.sh $exe configs/issue239_3.conf testcases/issue239_3_norefresh.py
./run_one_test.sh $exe configs/issue314.conf testcases/issue314.py
./run_one_test.sh $exe configs/issue314.conf testcases/issue314_2.py
./run_one_test.sh $exe configs/issue314.conf testcases/issue314_3.py
./run_one_test.sh $exe /dev/null testcases/issue299.py

View File

@ -1,35 +1,18 @@
import xcffib.xproto as xproto import xcffib.xproto as xproto
import xcffib.randr as randr import xcffib.randr as randr
import xcffib
import time import time
import random import random
import string import string
def to_atom(conn, string):
return conn.core.InternAtom(False, len(string), string).reply().atom
def set_window_name(conn, wid, name): def set_window_name(conn, wid, name):
prop_name = to_atom(conn, "_NET_WM_NAME") prop_name = "_NET_WM_NAME"
str_type = to_atom(conn, "UTF8_STRING") prop_name = conn.core.InternAtom(True, len(prop_name), prop_name).reply().atom
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid, prop_name, str_type, 8, len(name), name).check() str_type = "STRING"
prop_name = to_atom(conn, "WM_NAME") str_type = conn.core.InternAtom(True, len(str_type), str_type).reply().atom
str_type = to_atom(conn, "STRING")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid, prop_name, str_type, 8, len(name), name).check()
def set_window_state(conn, wid, state):
prop_name = to_atom(conn, "WM_STATE")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid, prop_name, prop_name, 32, 2, [state, 0]).check()
def set_window_class(conn, wid, name):
if not isinstance(name, bytearray):
name = name.encode()
name = name+b"\0"+name+b"\0"
prop_name = to_atom(conn, "WM_CLASS")
str_type = to_atom(conn, "STRING")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid, prop_name, str_type, 8, len(name), name).check() conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid, prop_name, str_type, 8, len(name), name).check()
def find_picom_window(conn): def find_picom_window(conn):
prop_name = to_atom(conn, "WM_NAME") prop_name = "WM_NAME"
prop_name = conn.core.InternAtom(True, len(prop_name), prop_name).reply().atom
setup = conn.get_setup() setup = conn.get_setup()
root = setup.roots[0].root root = setup.roots[0].root
windows = conn.core.QueryTree(root).reply() windows = conn.core.QueryTree(root).reply()
@ -58,19 +41,3 @@ def trigger_root_configure(conn):
rr.AddOutputModeChecked(output, mode).check() rr.AddOutputModeChecked(output, mode).check()
rr.SetCrtcConfig(reply.crtcs[0], reply.timestamp, reply.config_timestamp, 0, 0, mode, randr.Rotation.Rotate_0, 1, [output]).reply() rr.SetCrtcConfig(reply.crtcs[0], reply.timestamp, reply.config_timestamp, 0, 0, mode, randr.Rotation.Rotate_0, 1, [output]).reply()
def find_32bit_visual(conn):
setup = conn.get_setup()
render = conn(xcffib.render.key)
r = render.QueryPictFormats().reply()
pictfmt_ids = set()
for pictform in r.formats:
if (pictform.depth == 32 and
pictform.type == xcffib.render.PictType.Direct and
pictform.direct.alpha_mask != 0):
pictfmt_ids.add(pictform.id)
print(pictfmt_ids)
for screen in r.screens:
for depth in screen.depths:
for pv in depth.visuals:
if pv.format in pictfmt_ids:
return pv.visual

View File

@ -1,112 +0,0 @@
#!/usr/bin/env python3
import xcffib.xproto as xproto
import xcffib
import time
import os
import subprocess
import asyncio
from dbus_next.aio import MessageBus
from dbus_next.message import Message, MessageType
from common import *
display = os.environ["DISPLAY"].replace(":", "_")
conn = xcffib.connect()
setup = conn.get_setup()
root = setup.roots[0].root
visual = setup.roots[0].root_visual
depth = setup.roots[0].root_depth
x = xproto.xprotoExtension(conn)
visual32 = find_32bit_visual(conn)
async def get_client_win_async(wid):
message = await bus.call(Message(destination='com.github.chjj.compton.'+display,
path='/',
interface='com.github.chjj.compton',
member='win_get',
signature='us',
body=[wid, 'client_win']))
return message.body[0]
def get_client_win(wid):
return loop.run_until_complete(get_client_win_async(wid))
def wait():
time.sleep(0.5)
def create_client_window(name):
client_win = conn.generate_id()
print("Window : ", hex(client_win))
conn.core.CreateWindowChecked(depth, client_win, root, 0, 0, 100, 100, 0,
xproto.WindowClass.InputOutput, visual, 0, []).check()
set_window_name(conn, client_win, "Test window "+name)
set_window_class(conn, client_win, "Test windows")
set_window_state(conn, client_win, 1)
conn.core.MapWindowChecked(client_win).check()
return client_win
loop = asyncio.get_event_loop()
bus = loop.run_until_complete(MessageBus().connect())
cmid = conn.generate_id()
colormap = conn.core.CreateColormapChecked(xproto.ColormapAlloc._None, cmid, root, visual32).check()
# Create window
client_wins = []
for i in range(0,2):
client_wins.append(create_client_window(str(i)))
# Create frame window
frame_win = conn.generate_id()
print("Window : ", hex(frame_win))
conn.core.CreateWindowChecked(depth, frame_win, root, 0, 0, 200, 200, 0,
xproto.WindowClass.InputOutput, visual, 0, []).check()
set_window_name(conn, frame_win, "Frame")
conn.core.MapWindowChecked(frame_win).check()
# Scenario 1.1
# 1. reparent placeholder to frame
conn.core.ReparentWindowChecked(client_wins[0], frame_win, 0, 0).check()
wait()
# 2. reparent real client to frame
conn.core.ReparentWindowChecked(client_wins[1], frame_win, 0, 0).check()
wait()
# 3. detach the placeholder
conn.core.ReparentWindowChecked(client_wins[0], root, 0, 0).check()
wait()
assert get_client_win(frame_win) == client_wins[1]
# Scenario 1.2
# 1. reparent placeholder to frame
conn.core.ReparentWindowChecked(client_wins[0], frame_win, 0, 0).check()
wait()
# 2. reparent real client to frame
conn.core.ReparentWindowChecked(client_wins[1], frame_win, 0, 0).check()
wait()
# 3. destroy the placeholder
conn.core.DestroyWindowChecked(client_wins[0]).check()
wait()
assert get_client_win(frame_win) == client_wins[1]
client_wins[0] = create_client_window("0")
# Scenario 2
# 1. frame is unmapped
conn.core.UnmapWindowChecked(frame_win).check()
wait()
# 2. reparent placeholder to frame
conn.core.ReparentWindowChecked(client_wins[0], frame_win, 0, 0).check()
wait()
# 3. destroy placeholder, map frame and reparent real client to frame
conn.core.DestroyWindowChecked(client_wins[0]).check()
conn.core.MapWindowChecked(frame_win).check()
conn.core.ReparentWindowChecked(client_wins[1], frame_win, 0, 0).check()
wait()
assert get_client_win(frame_win) == client_wins[1]
client_wins[0] = create_client_window("0")
# Destroy the windows
for wid in client_wins:
conn.core.DestroyWindowChecked(wid).check()
conn.core.DestroyWindowChecked(frame_win).check()

View File

@ -1,44 +0,0 @@
#!/usr/bin/env python
import xcffib.xproto as xproto
import xcffib
import time
from common import set_window_name, trigger_root_configure
conn = xcffib.connect()
setup = conn.get_setup()
root = setup.roots[0].root
visual = setup.roots[0].root_visual
depth = setup.roots[0].root_depth
x = xproto.xprotoExtension(conn)
# issue 314 is caused by changing a windows target opacity during its fade-in/-out transition
wid1 = conn.generate_id()
print("Window 1: ", hex(wid1))
wid2 = conn.generate_id()
print("Window 2: ", hex(wid2))
# Create windows
conn.core.CreateWindowChecked(depth, wid1, root, 0, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
conn.core.CreateWindowChecked(depth, wid2, root, 0, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
# Set Window names
set_window_name(conn, wid1, "Test window 1")
set_window_name(conn, wid2, "Test window 2")
# Check updating opacity while UNMAPPING/DESTROYING windows
print("Mapping 1")
conn.core.MapWindowChecked(wid1).check()
print("Mapping 2")
conn.core.MapWindowChecked(wid2).check()
time.sleep(0.5)
x.SetInputFocusChecked(0, wid1, xproto.Time.CurrentTime).check()
time.sleep(0.5)
# Destroy the windows
print("Destroy 1 while fading out")
conn.core.DestroyWindowChecked(wid1).check()
x.SetInputFocusChecked(0, wid2, xproto.Time.CurrentTime).check()
time.sleep(1)
conn.core.DestroyWindowChecked(wid2).check()

View File

@ -1,46 +0,0 @@
#!/usr/bin/env python
import xcffib.xproto as xproto
import xcffib
import time
from common import set_window_name, trigger_root_configure
conn = xcffib.connect()
setup = conn.get_setup()
root = setup.roots[0].root
visual = setup.roots[0].root_visual
depth = setup.roots[0].root_depth
x = xproto.xprotoExtension(conn)
opacity_80 = [int(0xffffffff * 0.8), ]
opacity_single = [int(0xffffffff * 0.002), ]
# issue 314 is caused by changing a windows target opacity during its fade-in/-out transition
wid1 = conn.generate_id()
print("Window 1: ", hex(wid1))
atom = "_NET_WM_WINDOW_OPACITY"
opacity_atom = conn.core.InternAtom(False, len(atom), atom).reply().atom
# Create windows
conn.core.CreateWindowChecked(depth, wid1, root, 0, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
# Set Window names
set_window_name(conn, wid1, "Test window 1")
# Check updating opacity while MAPPING windows
print("Mapping window")
conn.core.MapWindowChecked(wid1).check()
time.sleep(0.5)
print("Update opacity while fading in")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_80).check()
time.sleep(0.2)
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_single).check()
time.sleep(1)
conn.core.DeletePropertyChecked(wid1, opacity_atom).check()
time.sleep(0.5)
# Destroy the windows
conn.core.DestroyWindowChecked(wid1).check()

View File

@ -1,63 +0,0 @@
#!/usr/bin/env python
import xcffib.xproto as xproto
import xcffib
import time
from common import set_window_name, trigger_root_configure
conn = xcffib.connect()
setup = conn.get_setup()
root = setup.roots[0].root
visual = setup.roots[0].root_visual
depth = setup.roots[0].root_depth
x = xproto.xprotoExtension(conn)
opacity_100 = [0xffffffff, ]
opacity_80 = [int(0xffffffff * 0.8), ]
opacity_single = [int(0xffffffff * 0.002), ]
opacity_0 = [0, ]
# issue 314 is caused by changing a windows target opacity during its fade-in/-out transition
wid1 = conn.generate_id()
print("Window 1: ", hex(wid1))
atom = "_NET_WM_WINDOW_OPACITY"
opacity_atom = conn.core.InternAtom(False, len(atom), atom).reply().atom
# Create windows
conn.core.CreateWindowChecked(depth, wid1, root, 0, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
# Set Window names
set_window_name(conn, wid1, "Test window 1")
# Check updating opacity while FADING windows
print("Mapping window")
conn.core.MapWindowChecked(wid1).check()
time.sleep(1.2)
print("Update opacity while fading out")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_single).check()
time.sleep(0.2)
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_0).check()
time.sleep(1)
print("Change from fading in to fading out")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_80).check()
time.sleep(0.5)
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_0).check()
time.sleep(1)
print("Update opacity while fading in")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_80).check()
time.sleep(0.2)
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_100).check()
time.sleep(1)
print("Change from fading out to fading in")
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_0).check()
time.sleep(0.5)
conn.core.ChangePropertyChecked(xproto.PropMode.Replace, wid1, opacity_atom, xproto.Atom.CARDINAL, 32, 1, opacity_80).check()
time.sleep(1)
# Destroy the windows
conn.core.DestroyWindowChecked(wid1).check()

View File

@ -1,33 +0,0 @@
#!/usr/bin/env python
import xcffib.xproto as xproto
import xcffib
import time
from common import set_window_name, trigger_root_configure
conn = xcffib.connect()
setup = conn.get_setup()
root = setup.roots[0].root
visual = setup.roots[0].root_visual
depth = setup.roots[0].root_depth
# issue 357 is triggered when a window is destroyed right after configure_root
wid = conn.generate_id()
print("Window 1: ", hex(wid))
# Create a window
conn.core.CreateWindowChecked(depth, wid, root, 0, 0, 100, 100, 0, xproto.WindowClass.InputOutput, visual, 0, []).check()
# Set Window name
set_window_name(conn, wid, "Test window 1")
print("mapping 1")
conn.core.MapWindowChecked(wid).check()
time.sleep(0.5)
trigger_root_configure(conn)
# Destroy the windows
conn.core.DestroyWindowChecked(wid).check()
time.sleep(1)