Compare commits
145 Commits
pacing-fix
...
next
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df4c6a3d9b | ||
|
|
3b9e2c5812 | ||
|
|
b99c7db73e | ||
|
|
7c15a1438a | ||
|
|
2b52edd9e3 | ||
|
|
9175489f65 | ||
|
|
45ffdf9849 | ||
|
|
96de4f07ca | ||
|
|
a93bbc30e5 | ||
|
|
6315faed20 | ||
|
|
580aef939f | ||
|
|
071b77c49f | ||
|
|
0dcca2228e | ||
|
|
bb097730c7 | ||
|
|
8d0284da1b | ||
|
|
9b6b6855d7 | ||
|
|
2d98518b7d | ||
|
|
7f3f8b37b5 | ||
|
|
42782689e8 | ||
|
|
216bfefd9f | ||
|
|
bef1240b41 | ||
|
|
7e7c2b0cef | ||
|
|
f509008cb9 | ||
|
|
8c96fbebc4 | ||
|
|
05b1fbff9e | ||
|
|
613d179f2d | ||
|
|
84b9ff3148 | ||
|
|
7da8b7afeb | ||
|
|
5b81ea2c58 | ||
|
|
2238cf1e54 | ||
|
|
85bb56e8a6 | ||
|
|
241d7f1d03 | ||
|
|
e8d42885fa | ||
|
|
e948b74363 | ||
|
|
64f6c4885a | ||
|
|
23b0c5a1d5 | ||
|
|
c7591982b6 | ||
|
|
1aa90f6466 | ||
|
|
b0dfcf4a32 | ||
|
|
28cb220b5b | ||
|
|
c6db632d9d | ||
|
|
f28905b62d | ||
|
|
53dd8a4e66 | ||
|
|
f179119d84 | ||
|
|
466fb4c9e0 | ||
|
|
4f792243c1 | ||
|
|
9392829d84 | ||
|
|
a4ec70982c | ||
|
|
0ab3e0740e | ||
|
|
7ada6db4a3 | ||
|
|
75d0b7ba1e | ||
|
|
a5826b6fb0 | ||
|
|
bdc0943399 | ||
|
|
baeafb3a3b | ||
|
|
037be5cca2 | ||
|
|
0e1628e031 | ||
|
|
642a43acbb | ||
|
|
755996a42c | ||
|
|
eb723eee29 | ||
|
|
fcd51e7373 | ||
|
|
dff77aae27 | ||
|
|
709f0168d9 | ||
|
|
726b8d0e28 | ||
|
|
023103c620 | ||
|
|
39534b98f0 | ||
|
|
0f22b70705 | ||
|
|
f265e049a8 | ||
|
|
a39cd94e1f | ||
|
|
7fad0d51d7 | ||
|
|
9204426caf | ||
|
|
0d3d18cd88 | ||
|
|
c738400058 | ||
|
|
eb39426b08 | ||
|
|
77c4ebaafc | ||
|
|
e8ca1912cd | ||
|
|
0ed8d0cadf | ||
|
|
bbc657e4bc | ||
|
|
839a101527 | ||
|
|
5c640ac452 | ||
|
|
4a79e7b777 | ||
|
|
a655730e49 | ||
|
|
4401666cfb | ||
|
|
90f5f4ca29 | ||
|
|
c1b4ede06e | ||
|
|
238c3cc833 | ||
|
|
0638de5c56 | ||
|
|
fdbcd15975 | ||
|
|
bacdb919c7 | ||
|
|
733113c691 | ||
|
|
14a345a817 | ||
|
|
5fba210ad6 | ||
|
|
1bfb129985 | ||
|
|
8ca66f8a00 | ||
|
|
f3bdd01dc8 | ||
|
|
71e29c4128 | ||
|
|
606844c4bb | ||
|
|
39de1e49cf | ||
|
|
fffc7ce535 | ||
|
|
fe5b416ed6 | ||
|
|
2417084628 | ||
|
|
4299336146 | ||
|
|
702e30df4a | ||
|
|
d87dd41f4c | ||
|
|
ce205d1591 | ||
|
|
4c4df9b918 | ||
|
|
56da153f1c | ||
|
|
81d137a3cc | ||
|
|
6e3c86226b | ||
|
|
197b4bd396 | ||
|
|
a3f50e8a3b | ||
|
|
4c34944d76 | ||
|
|
a73ca2dc8d | ||
|
|
1b8d321c45 | ||
|
|
30e37dbf09 | ||
|
|
dc2d7b2876 | ||
|
|
5e119123a7 | ||
|
|
6d4eaec811 | ||
|
|
eb3a58a6b0 | ||
|
|
3390494bfe | ||
|
|
148e61a0b2 | ||
|
|
d8f303761b | ||
|
|
217284642a | ||
|
|
496452cfce | ||
|
|
cacb45fbcd | ||
|
|
7336e4142b | ||
|
|
b368072e12 | ||
|
|
359d004b99 | ||
|
|
6e0bad0034 | ||
|
|
a28e221b83 | ||
|
|
b582d2989e | ||
|
|
db9b808bfb | ||
|
|
db6ed75b60 | ||
|
|
dd83f550e1 | ||
|
|
c42e6cef0a | ||
|
|
25d16b0352 | ||
|
|
3838b45e2c | ||
|
|
4b57e3aa4c | ||
|
|
1430d28116 | ||
|
|
147561a313 | ||
|
|
23eb3d5f52 | ||
|
|
2e1c4e51b3 | ||
|
|
827380e1e3 | ||
|
|
8d98b7d639 | ||
|
|
e76cf43f02 | ||
|
|
332501aef7 |
@@ -12,6 +12,7 @@ packages:
|
|||||||
- uthash
|
- uthash
|
||||||
- libconfig
|
- libconfig
|
||||||
- libglvnd
|
- libglvnd
|
||||||
|
- libepoxy
|
||||||
- dbus
|
- dbus
|
||||||
- pcre
|
- pcre
|
||||||
sources:
|
sources:
|
||||||
@@ -19,7 +20,7 @@ sources:
|
|||||||
tasks:
|
tasks:
|
||||||
- setup: |
|
- setup: |
|
||||||
cd picom
|
cd picom
|
||||||
CPPFLAGS="-I/usr/local/include" meson -Dunittest=true build
|
CPPFLAGS="-I/usr/local/include" meson setup -Dunittest=true --werror build
|
||||||
- build: |
|
- build: |
|
||||||
cd picom
|
cd picom
|
||||||
ninja -C build
|
ninja -C build
|
||||||
|
|||||||
23
.builds/openbsd.yml
Normal file
23
.builds/openbsd.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
image: openbsd/latest
|
||||||
|
packages:
|
||||||
|
- libev
|
||||||
|
- xcb
|
||||||
|
- meson
|
||||||
|
- pkgconf
|
||||||
|
- cmake
|
||||||
|
- uthash
|
||||||
|
- libconfig
|
||||||
|
- dbus
|
||||||
|
- pcre2
|
||||||
|
sources:
|
||||||
|
- https://github.com/yshui/picom
|
||||||
|
tasks:
|
||||||
|
- setup: |
|
||||||
|
cd picom
|
||||||
|
CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" meson setup -Dunittest=true --werror build
|
||||||
|
- build: |
|
||||||
|
cd picom
|
||||||
|
ninja -C build
|
||||||
|
- unittest: |
|
||||||
|
cd picom
|
||||||
|
ninja -C build test
|
||||||
@@ -29,7 +29,7 @@ commands:
|
|||||||
- ".git"
|
- ".git"
|
||||||
- run:
|
- run:
|
||||||
name: config
|
name: config
|
||||||
command: CC=<< parameters.cc >> meson << parameters.build-config >> -Dunittest=true --werror . build
|
command: CC=<< parameters.cc >> meson setup << parameters.build-config >> -Dunittest=true --werror . build
|
||||||
- run:
|
- run:
|
||||||
name: build
|
name: build
|
||||||
command: ninja -vC build
|
command: ninja -vC build
|
||||||
|
|||||||
@@ -23,5 +23,9 @@ CheckOptions:
|
|||||||
value: 255.0;1.0;
|
value: 255.0;1.0;
|
||||||
- key: readability-function-cognitive-complexity.IgnoreMacros
|
- key: readability-function-cognitive-complexity.IgnoreMacros
|
||||||
value: true
|
value: true
|
||||||
|
- key: readability-function-cognitive-complexity.Threshold
|
||||||
|
value: 50
|
||||||
- key: readability-function-cognitive-complexity.DescribeBasicIncrements
|
- key: readability-function-cognitive-complexity.DescribeBasicIncrements
|
||||||
value: true
|
value: true
|
||||||
|
- key: bugprone-signed-char-misuse.CharTypdefsToIgnore
|
||||||
|
value: int8_t
|
||||||
|
|||||||
@@ -3,3 +3,6 @@ root = true
|
|||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 8
|
indent_size = 8
|
||||||
max_line_length = 90
|
max_line_length = 90
|
||||||
|
[*.nix]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|||||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -18,11 +18,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
|
|
||||||
# Autobuild
|
# Autobuild
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|||||||
4
.github/workflows/coding-style-pr.yml
vendored
4
.github/workflows/coding-style-pr.yml
vendored
@@ -6,8 +6,8 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }}
|
- run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }}
|
||||||
- uses: yshui/git-clang-format-lint@v1.14
|
- uses: yshui/git-clang-format-lint@v1.15
|
||||||
with:
|
with:
|
||||||
base: ${{ github.event.pull_request.base.sha }}
|
base: ${{ github.event.pull_request.base.sha }}
|
||||||
|
|||||||
4
.github/workflows/coding-style.yml
vendored
4
.github/workflows/coding-style.yml
vendored
@@ -6,9 +6,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
- uses: yshui/git-clang-format-lint@v1.14
|
- uses: yshui/git-clang-format-lint@v1.15
|
||||||
with:
|
with:
|
||||||
base: ${{ github.event.ref }}~1
|
base: ${{ github.event.ref }}~1
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
# Build files
|
# Build files
|
||||||
.deps
|
.deps
|
||||||
|
.direnv
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
autom4te.cache
|
autom4te.cache
|
||||||
config.log
|
config.log
|
||||||
|
|||||||
74
CHANGELOG.md
Normal file
74
CHANGELOG.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# Unreleased
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* Allow `corner-radius-rules` to override `corner-radius = 0`. Previously setting corner radius to 0 globally disables rounded corners. (#1170)
|
||||||
|
|
||||||
|
## Notable changes
|
||||||
|
|
||||||
|
* Marginally improve performance when resizing/opening/closing windows. (#1190)
|
||||||
|
|
||||||
|
# v11.2 (2024-Feb-13)
|
||||||
|
|
||||||
|
## Build changes
|
||||||
|
|
||||||
|
* `picom` now depends on `libepoxy` for OpenGL symbol management.
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Workaround a NVIDIA problem that causes high CPU usage after suspend/resume (#1172, #1168)
|
||||||
|
* Fix building on OpenBSD (#1189, #1188)
|
||||||
|
* Fix occasional freezes (#1040, #1145, #1166)
|
||||||
|
* Fix `corner-radius-rules` not applying sometimes (#1177)
|
||||||
|
* Fix window shader not having an effect when frame opacity is enabled (#1174)
|
||||||
|
* Fix binding root pixmap in case of depth mismatch (#984)
|
||||||
|
|
||||||
|
# v11.1 (2024-Jan-28)
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Fix missing fading on window close for some window managers. (#704)
|
||||||
|
|
||||||
|
# v11 (2024-Jan-20)
|
||||||
|
|
||||||
|
## Build changes
|
||||||
|
|
||||||
|
* Due to some caveats discovered related to setting the `CAP_SYS_NICE` capability, it is now recommended to **NOT** set this capability for picom.
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
* Uses of `--sw-opti`, and `--respect-prop-shadow` are now hard errors.
|
||||||
|
* `-F` has been removed completely. It was deprecated before the picom fork.
|
||||||
|
|
||||||
|
# v11-rc1 (2024-Jan-14)
|
||||||
|
|
||||||
|
## Notable features
|
||||||
|
|
||||||
|
* picom now uses dithering to prevent banding. Banding is most notable when using a strong background blur. (#952)
|
||||||
|
* Frame pacing. picom uses present feedback information to schedule new frames when it makes sense to do so. This improves latency, and replaces the `glFlush` and `GL_MaxFramesAllowed=1` hacks we used to do for NVIDIA. (#968 #1156)
|
||||||
|
* Some missing features have been implemented for the EGL backend (#1004 #1007)
|
||||||
|
|
||||||
|
## Bug fixes
|
||||||
|
|
||||||
|
* Many memory/resource leak fixes thanks to @absolutelynothelix . (#977 #978 #979 #980 #982 #985 #992 #1009 #1022)
|
||||||
|
* Fix tiling of wallpaper. (#1002)
|
||||||
|
* Fix some blur artifacts (#1095)
|
||||||
|
* Fix shadow color for transparent shadows (#1124)
|
||||||
|
* Don't spam logs when another compositor is running (#1104)
|
||||||
|
* Fix rounded corners showing as black with the xrender backend (#1003)
|
||||||
|
* Fix blur with rounded windows (#950)
|
||||||
|
|
||||||
|
## Build changes
|
||||||
|
|
||||||
|
* Dependency `pcre` has been replaced by `pcre2`.
|
||||||
|
* New dependency `xcb-util`.
|
||||||
|
* `xinerama` is no longer used.
|
||||||
|
* `picom` now tries to give itself a real-time scheduling priority. ~~Please consider giving `picom` the `CAP_SYS_NICE` capability when packaging it.~~
|
||||||
|
|
||||||
|
## Deprecations
|
||||||
|
|
||||||
|
* The `kawase` blur method is removed. Note this is just an alias to the `dual_kawase` method, which is still available. (#1102)
|
||||||
|
|
||||||
|
# Earlier versions
|
||||||
|
|
||||||
|
Please see the GitHub releases page.
|
||||||
@@ -34,6 +34,7 @@ hasufell <julian.ospald at googlemail.com>
|
|||||||
i-c-u-p
|
i-c-u-p
|
||||||
Ignacio Taranto <ignacio.taranto at eclypsium.com>
|
Ignacio Taranto <ignacio.taranto at eclypsium.com>
|
||||||
Istvan Petres
|
Istvan Petres
|
||||||
|
Ivan Malison <ivanmalison at gmail.com>
|
||||||
Jake <jakeroggenbuck2 at gmail.com>
|
Jake <jakeroggenbuck2 at gmail.com>
|
||||||
James Cloos <cloos at jhcloos.com>
|
James Cloos <cloos at jhcloos.com>
|
||||||
Jamey Sharp <jamey at minilop.net>
|
Jamey Sharp <jamey at minilop.net>
|
||||||
@@ -43,6 +44,7 @@ Javeed Shaikh <syscrash2k at gmail.com>
|
|||||||
Jerónimo Navarro <jnavarro at ancasrl.com.ar>
|
Jerónimo Navarro <jnavarro at ancasrl.com.ar>
|
||||||
jialeens <jialeadmin at 163.com>
|
jialeens <jialeadmin at 163.com>
|
||||||
Johnny Pribyl <pribylsnbits at gmail.com>
|
Johnny Pribyl <pribylsnbits at gmail.com>
|
||||||
|
Jose Maldonado aka Yukiteru <josemald89 at gmail.com>
|
||||||
Keith Packard <keithp at keithp.com>
|
Keith Packard <keithp at keithp.com>
|
||||||
Kevin Kelley <kelleyk at kelleyk.net>
|
Kevin Kelley <kelleyk at kelleyk.net>
|
||||||
ktprograms <ktprograms at gmail.com>
|
ktprograms <ktprograms at gmail.com>
|
||||||
@@ -68,6 +70,7 @@ Peter Mattern <matternp at arcor.de>
|
|||||||
Phil Blundell <pb at reciva.com>
|
Phil Blundell <pb at reciva.com>
|
||||||
Que Quotion <quequotion at bugmenot.com>
|
Que Quotion <quequotion at bugmenot.com>
|
||||||
Rafael Kitover <rkitover at gmail.com>
|
Rafael Kitover <rkitover at gmail.com>
|
||||||
|
Reith
|
||||||
Richard Grenville <pyxlcy at gmail.com>
|
Richard Grenville <pyxlcy at gmail.com>
|
||||||
Rytis Karpuska <rytis.karpuska at gmail.com>
|
Rytis Karpuska <rytis.karpuska at gmail.com>
|
||||||
Samuel Hand <samuel.d.hand at gmail.com>
|
Samuel Hand <samuel.d.hand at gmail.com>
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -41,7 +41,7 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth
|
|||||||
* 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)
|
||||||
* libGL, libEGL (optional, disable with the `-Dopengl=false` meson configure flag)
|
* libGL, libEGL, libepoxy (optional, disable with the `-Dopengl=false` meson configure flag)
|
||||||
* libpcre2 (optional, disable with the `-Dregex=false` meson configure flag)
|
* libpcre2 (optional, disable with the `-Dregex=false` meson configure flag)
|
||||||
* libev
|
* libev
|
||||||
* uthash
|
* uthash
|
||||||
@@ -49,13 +49,13 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth
|
|||||||
On Debian based distributions (e.g. Ubuntu), the needed packages are
|
On Debian based distributions (e.g. Ubuntu), the needed packages are
|
||||||
|
|
||||||
```
|
```
|
||||||
libconfig-dev libdbus-1-dev libegl-dev libev-dev libgl-dev libpcre2-dev libpixman-1-dev libx11-xcb-dev libxcb1-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-dpms0-dev libxcb-glx0-dev libxcb-image0-dev libxcb-present-dev libxcb-randr0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev libxcb-util-dev libxcb-xfixes0-dev libxext-dev meson ninja-build uthash-dev
|
libconfig-dev libdbus-1-dev libegl-dev libev-dev libgl-dev libepoxy-dev libpcre2-dev libpixman-1-dev libx11-xcb-dev libxcb1-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-dpms0-dev libxcb-glx0-dev libxcb-image0-dev libxcb-present-dev libxcb-randr0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-shape0-dev libxcb-util-dev libxcb-xfixes0-dev libxext-dev meson ninja-build uthash-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
On Fedora, the needed packages are
|
On Fedora, the needed packages are
|
||||||
|
|
||||||
```
|
```
|
||||||
dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel meson pcre2-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel
|
dbus-devel gcc git libconfig-devel libdrm-devel libev-devel libX11-devel libX11-xcb libXext-devel libxcb-devel libGL-devel libEGL-devel libepoxy-devel meson pcre2-devel pixman-devel uthash-devel xcb-util-image-devel xcb-util-renderutil-devel xorg-x11-proto-devel xcb-util-devel
|
||||||
```
|
```
|
||||||
|
|
||||||
To build the documents, you need `asciidoc`
|
To build the documents, you need `asciidoc`
|
||||||
@@ -96,6 +96,12 @@ $ ninja -C build install
|
|||||||
|
|
||||||
Default install prefix is `/usr/local`, you can change it with `meson configure -Dprefix=<path> build`
|
Default install prefix is `/usr/local`, you can change it with `meson configure -Dprefix=<path> build`
|
||||||
|
|
||||||
|
## Running
|
||||||
|
To launch with all animations as a background process you can use:
|
||||||
|
`picom --animations -b`
|
||||||
|
|
||||||
|
To only have specific animations, enable them with cli flags (see `picom --help`) or add them to your picom config.
|
||||||
|
|
||||||
## How to Contribute
|
## How to Contribute
|
||||||
|
|
||||||
All contributions are welcome!
|
All contributions are welcome!
|
||||||
|
|||||||
23
flake.lock
generated
23
flake.lock
generated
@@ -5,11 +5,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1689068808,
|
"lastModified": 1705309234,
|
||||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -25,11 +25,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1660459072,
|
"lastModified": 1703887061,
|
||||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
"narHash": "sha256-gGPa9qWNc6eCXT/+Z5/zMkyYOuRZqeFZBDbopNZQkuY=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "gitignore.nix",
|
"repo": "gitignore.nix",
|
||||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
"rev": "43e1aa1308018f37118e34d3a9cb4f5e75dc11d5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -41,11 +41,12 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1691186842,
|
"lastModified": 1705856552,
|
||||||
"narHash": "sha256-wxBVCvZUwq+XS4N4t9NqsHV4E64cPVqQ2fdDISpjcw0=",
|
"narHash": "sha256-JXfnuEf5Yd6bhMs/uvM67/joxYKoysyE3M2k6T3eWbg=",
|
||||||
"path": "/nix/store/d42v5grfq77vr10r336kks0qjp0wij8d-source",
|
"owner": "NixOS",
|
||||||
"rev": "18036c0be90f4e308ae3ebcab0e14aae0336fe42",
|
"repo": "nixpkgs",
|
||||||
"type": "path"
|
"rev": "612f97239e2cc474c13c9dafa0df378058c5ad8d",
|
||||||
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"id": "nixpkgs",
|
||||||
|
|||||||
75
flake.nix
75
flake.nix
@@ -7,28 +7,55 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
outputs = {
|
outputs = {
|
||||||
self, flake-utils, nixpkgs, git-ignore-nix, ...
|
self,
|
||||||
}: flake-utils.lib.eachDefaultSystem (system: let
|
flake-utils,
|
||||||
overlay = self: super: {
|
nixpkgs,
|
||||||
picom = super.picom.overrideAttrs (oldAttrs: rec {
|
git-ignore-nix,
|
||||||
pname = "picom";
|
...
|
||||||
buildInputs = [
|
}:
|
||||||
self.pcre2 self.xorg.xcbutil
|
flake-utils.lib.eachDefaultSystem (system: let
|
||||||
] ++ self.lib.remove self.xorg.libXinerama (
|
overlay = self: super: {
|
||||||
self.lib.remove self.pcre oldAttrs.buildInputs
|
picom = super.picom.overrideAttrs (oldAttrs: rec {
|
||||||
);
|
version = "11";
|
||||||
src = git-ignore-nix.lib.gitignoreSource ./.;
|
pname = "picom";
|
||||||
});
|
buildInputs =
|
||||||
};
|
[
|
||||||
pkgs = import nixpkgs { inherit system overlays; config.allowBroken = true; };
|
self.pcre2
|
||||||
overlays = [ overlay ];
|
self.xorg.xcbutil
|
||||||
in rec {
|
self.libepoxy
|
||||||
inherit overlay overlays;
|
]
|
||||||
defaultPackage = pkgs.picom;
|
++ self.lib.remove self.xorg.libXinerama (
|
||||||
devShell = defaultPackage.overrideAttrs {
|
self.lib.remove self.pcre oldAttrs.buildInputs
|
||||||
buildInputs = defaultPackage.buildInputs ++ [
|
);
|
||||||
pkgs.clang-tools
|
src = git-ignore-nix.lib.gitignoreSource ./.;
|
||||||
];
|
});
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system overlays;
|
||||||
|
config.allowBroken = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
overlays = [overlay];
|
||||||
|
in rec {
|
||||||
|
inherit
|
||||||
|
overlay
|
||||||
|
overlays
|
||||||
|
;
|
||||||
|
defaultPackage = pkgs.picom;
|
||||||
|
devShell = defaultPackage.overrideAttrs {
|
||||||
|
buildInputs =
|
||||||
|
defaultPackage.buildInputs
|
||||||
|
++ (with pkgs; [
|
||||||
|
clang-tools_17
|
||||||
|
llvmPackages_17.clang-unwrapped.python
|
||||||
|
]);
|
||||||
|
hardeningDisable = ["fortify"];
|
||||||
|
shellHook = ''
|
||||||
|
# Workaround a NixOS limitation on sanitizers:
|
||||||
|
# See: https://github.com/NixOS/nixpkgs/issues/287763
|
||||||
|
export LD_LIBRARY_PATH+=":/run/opengl-driver/lib"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,9 +49,6 @@ OPTIONS
|
|||||||
*-f*, *--fading*::
|
*-f*, *--fading*::
|
||||||
Fade windows in/out when opening/closing and when opacity changes, unless *--no-fading-openclose* is used.
|
Fade windows in/out when opening/closing and when opacity changes, unless *--no-fading-openclose* is used.
|
||||||
|
|
||||||
*-F*::
|
|
||||||
Equals to *-f*. Deprecated.
|
|
||||||
|
|
||||||
*-i*, *--inactive-opacity*='OPACITY'::
|
*-i*, *--inactive-opacity*='OPACITY'::
|
||||||
Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
|
Opacity of inactive windows. (0.1 - 1.0, defaults to 1.0)
|
||||||
|
|
||||||
@@ -393,6 +390,7 @@ uniform float corner_radius; // corner radius of the window (pixels)
|
|||||||
uniform float border_width; // estimated border width of the window (pixels)
|
uniform float border_width; // estimated border width of the window (pixels)
|
||||||
uniform bool invert_color; // whether to invert the color of the window
|
uniform bool invert_color; // whether to invert the color of the window
|
||||||
uniform sampler2D tex; // texture of the window
|
uniform sampler2D tex; // texture of the window
|
||||||
|
uniform vec2 effective_size; // effective dimensions of the texture (repeats pixels if larger than tex)
|
||||||
uniform sampler2D brightness; // estimated brightness of the window, 1x1 texture
|
uniform sampler2D brightness; // estimated brightness of the window, 1x1 texture
|
||||||
uniform float max_brightness; // configured maximum brightness of the window (0.0 - 1.0)
|
uniform float max_brightness; // configured maximum brightness of the window (0.0 - 1.0)
|
||||||
uniform float time; // time in milliseconds, counting from an unspecified starting point
|
uniform float time; // time in milliseconds, counting from an unspecified starting point
|
||||||
@@ -418,7 +416,8 @@ vec4 default_post_processing(vec4 c);
|
|||||||
// 1) fetch the specified pixel
|
// 1) fetch the specified pixel
|
||||||
// 2) apply default post-processing
|
// 2) apply default post-processing
|
||||||
vec4 window_shader() {
|
vec4 window_shader() {
|
||||||
vec4 c = texelFetch(tex, ivec2(texcoord), 0);
|
vec2 texsize = textureSize(tex, 0);
|
||||||
|
vec4 c = texture2D(tex, texcoord / texsize, 0);
|
||||||
return default_post_processing(c);
|
return default_post_processing(c);
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
project('picom', 'c', version: '10',
|
project('picom', 'c', version: '11',
|
||||||
default_options: ['c_std=c11', 'warning_level=1'])
|
default_options: ['c_std=c11', 'warning_level=1'])
|
||||||
|
|
||||||
cc = meson.get_compiler('c')
|
cc = meson.get_compiler('c')
|
||||||
|
|||||||
50
src/atom.c
50
src/atom.c
@@ -2,12 +2,20 @@
|
|||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#include "atom.h"
|
#include "atom.h"
|
||||||
|
#include "cache.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "utils.h"
|
#include "compiler.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
|
struct atom_entry {
|
||||||
xcb_connection_t *c = ud;
|
struct cache_handle entry;
|
||||||
|
xcb_atom_t atom;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int atom_getter(struct cache *cache attr_unused, const char *atom_name,
|
||||||
|
struct cache_handle **value, void *user_data) {
|
||||||
|
xcb_connection_t *c = user_data;
|
||||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
||||||
c, xcb_intern_atom(c, 0, to_u16_checked(strlen(atom_name)), atom_name), NULL);
|
c, xcb_intern_atom(c, 0, to_u16_checked(strlen(atom_name)), atom_name), NULL);
|
||||||
|
|
||||||
@@ -18,9 +26,32 @@ static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
|
|||||||
free(reply);
|
free(reply);
|
||||||
} else {
|
} else {
|
||||||
log_error("Failed to intern atoms");
|
log_error("Failed to intern atoms");
|
||||||
*err = 1;
|
return -1;
|
||||||
}
|
}
|
||||||
return (void *)(intptr_t)atom;
|
|
||||||
|
struct atom_entry *entry = ccalloc(1, struct atom_entry);
|
||||||
|
entry->atom = atom;
|
||||||
|
*value = &entry->entry;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
atom_entry_free(struct cache *cache attr_unused, struct cache_handle *handle) {
|
||||||
|
struct atom_entry *entry = cache_entry(handle, struct atom_entry, entry);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_atom_t get_atom(struct atom *a, const char *key, xcb_connection_t *c) {
|
||||||
|
struct cache_handle *entry = NULL;
|
||||||
|
if (cache_get_or_fetch(&a->c, key, &entry, c, atom_getter) < 0) {
|
||||||
|
log_error("Failed to get atom %s", key);
|
||||||
|
return XCB_NONE;
|
||||||
|
}
|
||||||
|
return cache_entry(entry, struct atom_entry, entry)->atom;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_atom_t get_atom_cached(struct atom *a, const char *key) {
|
||||||
|
return cache_entry(cache_get(&a->c, key), struct atom_entry, entry)->atom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,10 +59,15 @@ static inline void *atom_getter(void *ud, const char *atom_name, int *err) {
|
|||||||
*/
|
*/
|
||||||
struct atom *init_atoms(xcb_connection_t *c) {
|
struct atom *init_atoms(xcb_connection_t *c) {
|
||||||
auto atoms = ccalloc(1, struct atom);
|
auto atoms = ccalloc(1, struct atom);
|
||||||
atoms->c = new_cache((void *)c, atom_getter, NULL);
|
atoms->c = CACHE_INIT;
|
||||||
#define ATOM_GET(x) atoms->a##x = (xcb_atom_t)(intptr_t)cache_get(atoms->c, #x, NULL)
|
#define ATOM_GET(x) atoms->a##x = get_atom(atoms, #x, c)
|
||||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
|
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST1);
|
||||||
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
|
LIST_APPLY(ATOM_GET, SEP_COLON, ATOM_LIST2);
|
||||||
#undef ATOM_GET
|
#undef ATOM_GET
|
||||||
return atoms;
|
return atoms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void destroy_atoms(struct atom *a) {
|
||||||
|
cache_invalidate_all(&a->c, atom_entry_free);
|
||||||
|
free(a);
|
||||||
|
}
|
||||||
|
|||||||
20
src/atom.h
20
src/atom.h
@@ -1,6 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
@@ -23,6 +21,7 @@
|
|||||||
WM_CLIENT_MACHINE, \
|
WM_CLIENT_MACHINE, \
|
||||||
_NET_ACTIVE_WINDOW, \
|
_NET_ACTIVE_WINDOW, \
|
||||||
_COMPTON_SHADOW, \
|
_COMPTON_SHADOW, \
|
||||||
|
COMPTON_VERSION, \
|
||||||
_NET_WM_WINDOW_TYPE, \
|
_NET_WM_WINDOW_TYPE, \
|
||||||
_XROOTPMAP_ID, \
|
_XROOTPMAP_ID, \
|
||||||
ESETROOT_PMAP_ID, \
|
ESETROOT_PMAP_ID, \
|
||||||
@@ -52,19 +51,18 @@
|
|||||||
|
|
||||||
#define ATOM_DEF(x) xcb_atom_t a##x
|
#define ATOM_DEF(x) xcb_atom_t a##x
|
||||||
|
|
||||||
|
struct atom_entry;
|
||||||
struct atom {
|
struct atom {
|
||||||
struct cache *c;
|
struct cache c;
|
||||||
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST1);
|
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST1);
|
||||||
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST2);
|
LIST_APPLY(ATOM_DEF, SEP_COLON, ATOM_LIST2);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct atom *init_atoms(xcb_connection_t *);
|
/// Create a new atom object with a xcb connection. `struct atom` does not hold
|
||||||
|
/// a reference to the connection.
|
||||||
|
struct atom *init_atoms(xcb_connection_t *c);
|
||||||
|
|
||||||
static inline xcb_atom_t get_atom(struct atom *a, const char *key) {
|
xcb_atom_t get_atom(struct atom *a, const char *key, xcb_connection_t *c);
|
||||||
return (xcb_atom_t)(intptr_t)cache_get(a->c, key, NULL);
|
xcb_atom_t get_atom_cached(struct atom *a, const char *key);
|
||||||
}
|
|
||||||
|
|
||||||
static inline void destroy_atoms(struct atom *a) {
|
void destroy_atoms(struct atom *a);
|
||||||
cache_free(a->c);
|
|
||||||
free(a);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
|||||||
auto after_damage_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
|
auto after_damage_us = (uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
|
||||||
log_trace("Getting damage took %" PRIu64 " us", after_damage_us - after_sync_fence_us);
|
log_trace("Getting damage took %" PRIu64 " us", after_damage_us - after_sync_fence_us);
|
||||||
if (ps->next_render > 0) {
|
if (ps->next_render > 0) {
|
||||||
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %ld",
|
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %" PRIu64,
|
||||||
labs((long)after_damage_us - (long)ps->next_render),
|
labs((long)after_damage_us - (long)ps->next_render),
|
||||||
after_damage_us < ps->next_render ? "early" : "late",
|
after_damage_us < ps->next_render ? "early" : "late",
|
||||||
after_damage_us, ps->next_render);
|
after_damage_us, ps->next_render);
|
||||||
@@ -527,7 +527,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
|||||||
®_paint_in_bound, ®_visible, true);
|
®_paint_in_bound, ®_visible, true);
|
||||||
} else {
|
} else {
|
||||||
if (is_animating && w->old_win_image) {
|
if (is_animating && w->old_win_image) {
|
||||||
bool is_focused = win_is_focused_raw(ps, w);
|
bool is_focused = win_is_focused_raw(w);
|
||||||
if (!is_focused && w->focused && w->opacity_is_set)
|
if (!is_focused && w->focused && w->opacity_is_set)
|
||||||
is_focused = true;
|
is_focused = true;
|
||||||
assert(w->old_win_image);
|
assert(w->old_win_image);
|
||||||
|
|||||||
@@ -115,6 +115,10 @@ struct dual_kawase_blur_args {
|
|||||||
int strength;
|
int strength;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Intentionally left blank
|
||||||
|
} *image_handle;
|
||||||
|
|
||||||
struct backend_operations {
|
struct backend_operations {
|
||||||
// =========== Initialization ===========
|
// =========== Initialization ===========
|
||||||
|
|
||||||
@@ -167,31 +171,27 @@ struct backend_operations {
|
|||||||
* Paint the content of an image onto the rendering buffer.
|
* Paint the content of an image onto the rendering buffer.
|
||||||
*
|
*
|
||||||
* @param backend_data the backend data
|
* @param backend_data the backend data
|
||||||
* @param image_data the image to paint
|
* @param image the image to paint, cannot be NULL
|
||||||
* @param dst_x, dst_y the top left corner of the image in the target
|
* @param dst_x, dst_y the top left corner of the image in the target
|
||||||
* @param mask the mask image, the top left of the mask is aligned with
|
* @param mask the mask image, the top left of the mask is aligned with
|
||||||
* the top left of the image
|
* the top left of the image. Optional, can be
|
||||||
|
* NULL.
|
||||||
* @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, coord_t image_dst,
|
void (*compose)(backend_t *backend_data, image_handle image, coord_t image_dst,
|
||||||
void *mask, coord_t mask_dst, const region_t *reg_paint,
|
image_handle mask, coord_t mask_dst, const region_t *reg_paint,
|
||||||
const region_t *reg_visible, bool lerp);
|
const region_t *reg_visible, bool lerp) attr_nonnull(1, 2, 6, 7);
|
||||||
|
|
||||||
void (*_compose)(backend_t *backend_data, void *image_data,
|
|
||||||
int dst_x1, int dst_y1, int dst_x2, int dst_y2,
|
|
||||||
const region_t *reg_paint, const region_t *reg_visible);
|
|
||||||
|
|
||||||
|
|
||||||
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
|
/// Fill rectangle of the rendering buffer, mostly for debug purposes, optional.
|
||||||
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
void (*fill)(backend_t *backend_data, struct color, const region_t *clip);
|
||||||
|
|
||||||
/// Blur a given region of the rendering buffer.
|
/// Blur a given region of the rendering buffer.
|
||||||
///
|
///
|
||||||
/// The blur is limited by `mask`. `mask_dst` specifies the top left corner of the
|
/// The blur can be limited by `mask`. `mask_dst` specifies the top left corner of
|
||||||
/// mask is.
|
/// the mask. `mask` can be NULL.
|
||||||
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx, void *mask,
|
bool (*blur)(backend_t *backend_data, double opacity, void *blur_ctx,
|
||||||
coord_t mask_dst, const region_t *reg_blur,
|
image_handle mask, coord_t mask_dst, const region_t *reg_blur,
|
||||||
const region_t *reg_visible) attr_nonnull(1, 3, 6, 7);
|
const region_t *reg_visible) attr_nonnull(1, 3, 6, 7);
|
||||||
|
|
||||||
/// 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
|
||||||
@@ -207,13 +207,15 @@ struct backend_operations {
|
|||||||
* Bind a X pixmap to the backend's internal image data structure.
|
* Bind a X pixmap to the backend's internal image data structure.
|
||||||
*
|
*
|
||||||
* @param backend_data backend data
|
* @param backend_data backend data
|
||||||
* @param pixmap X pixmap to bind
|
* @param pixmap X pixmap to bind
|
||||||
* @param fmt information of the pixmap's visual
|
* @param fmt information of the pixmap's visual
|
||||||
* @param owned whether the ownership of the pixmap is transfered to the backend
|
* @param owned whether the ownership of the pixmap is transferred to the
|
||||||
* @return backend internal data structure bound with this pixmap
|
* backend.
|
||||||
|
* @return backend specific image handle for the pixmap. May be
|
||||||
|
* NULL.
|
||||||
*/
|
*/
|
||||||
void *(*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
|
image_handle (*bind_pixmap)(backend_t *backend_data, xcb_pixmap_t pixmap,
|
||||||
struct xvisual_info fmt, bool owned);
|
struct xvisual_info fmt, bool owned);
|
||||||
|
|
||||||
/// Create a shadow context for rendering shadows with radius `radius`.
|
/// Create a shadow context for rendering shadows with radius `radius`.
|
||||||
/// Default implementation: default_create_shadow_context
|
/// Default implementation: default_create_shadow_context
|
||||||
@@ -225,21 +227,27 @@ struct backend_operations {
|
|||||||
struct backend_shadow_context *ctx);
|
struct backend_shadow_context *ctx);
|
||||||
|
|
||||||
/// Create a shadow image based on the parameters. Resulting image should have a
|
/// Create a shadow image based on the parameters. Resulting image should have a
|
||||||
/// size of `width + radisu * 2` x `height + radius * 2`. Radius is set when the
|
/// size of `width + radius * 2` x `height + radius * 2`. Radius is set when the
|
||||||
/// shadow context is created.
|
/// shadow context is created.
|
||||||
/// Default implementation: default_render_shadow
|
/// Default implementation: default_render_shadow
|
||||||
///
|
///
|
||||||
|
/// @return the shadow image, may be NULL.
|
||||||
|
///
|
||||||
/// Required.
|
/// Required.
|
||||||
void *(*render_shadow)(backend_t *backend_data, int width, int height,
|
image_handle (*render_shadow)(backend_t *backend_data, int width, int height,
|
||||||
struct backend_shadow_context *ctx, struct color color);
|
struct backend_shadow_context *ctx, struct color color);
|
||||||
|
|
||||||
/// Create a shadow by blurring a mask. `size` is the size of the blur. The
|
/// Create a shadow by blurring a mask. `size` is the size of the blur. The
|
||||||
/// backend can use whichever blur method is the fastest. The shadow produced
|
/// backend can use whichever blur method is the fastest. The shadow produced
|
||||||
/// shoule be consistent with `render_shadow`.
|
/// shoule be consistent with `render_shadow`.
|
||||||
///
|
///
|
||||||
|
/// @param mask the input mask, must not be NULL.
|
||||||
|
/// @return the shadow image, may be NULL.
|
||||||
|
///
|
||||||
/// Optional.
|
/// Optional.
|
||||||
void *(*shadow_from_mask)(backend_t *backend_data, void *mask,
|
image_handle (*shadow_from_mask)(backend_t *backend_data, image_handle mask,
|
||||||
struct backend_shadow_context *ctx, struct color color);
|
struct backend_shadow_context *ctx,
|
||||||
|
struct color color);
|
||||||
|
|
||||||
/// Create a mask image from region `reg`. This region can be used to create
|
/// Create a mask image from region `reg`. This region can be used to create
|
||||||
/// shadow, or used as a mask for composing. When used as a mask, it should mask
|
/// shadow, or used as a mask for composing. When used as a mask, it should mask
|
||||||
@@ -250,13 +258,18 @@ struct backend_operations {
|
|||||||
/// and outside of the mask. Corner radius should exclude the corners from the
|
/// and outside of the mask. Corner radius should exclude the corners from the
|
||||||
/// mask. Corner radius should be applied before the inversion.
|
/// mask. Corner radius should be applied before the inversion.
|
||||||
///
|
///
|
||||||
|
/// @return the mask image, may be NULL.
|
||||||
|
///
|
||||||
/// Required.
|
/// Required.
|
||||||
void *(*make_mask)(backend_t *backend_data, geometry_t size, const region_t *reg);
|
image_handle (*make_mask)(backend_t *backend_data, geometry_t size,
|
||||||
|
const region_t *reg);
|
||||||
|
|
||||||
// ============ Resource management ===========
|
// ============ Resource management ===========
|
||||||
|
|
||||||
/// Free resources associated with an image data structure
|
/// Free resources associated with an image data structure
|
||||||
void (*release_image)(backend_t *backend_data, void *img_data) attr_nonnull(1, 2);
|
///
|
||||||
|
/// @param image the image to be released, cannot be NULL.
|
||||||
|
void (*release_image)(backend_t *backend_data, image_handle image) attr_nonnull(1, 2);
|
||||||
|
|
||||||
/// Create a shader object from a shader source.
|
/// Create a shader object from a shader source.
|
||||||
///
|
///
|
||||||
@@ -281,12 +294,14 @@ struct backend_operations {
|
|||||||
/// This function is needed because some backend might change the content of the
|
/// This function is needed because some backend might change the content of the
|
||||||
/// window (e.g. when using a custom shader with the glx backend), so only the
|
/// window (e.g. when using a custom shader with the glx backend), so only the
|
||||||
/// backend knows if an image is transparent.
|
/// backend knows if an image is transparent.
|
||||||
bool (*is_image_transparent)(backend_t *backend_data, void *image_data)
|
///
|
||||||
|
/// @param image the image to be checked, must not be NULL.
|
||||||
|
bool (*is_image_transparent)(backend_t *backend_data, image_handle image)
|
||||||
attr_nonnull(1, 2);
|
attr_nonnull(1, 2);
|
||||||
|
|
||||||
/// Get the age of the buffer content we are currently rendering ontop
|
/// Get the age of the buffer content we are currently rendering on top
|
||||||
/// of. The buffer that has just been `present`ed has a buffer age of 1.
|
/// of. The buffer that has just been `present`ed has a buffer age of 1.
|
||||||
/// Everytime `present` is called, buffers get older. Return -1 if the
|
/// Every time `present` is called, buffers get older. Return -1 if the
|
||||||
/// buffer is empty.
|
/// buffer is empty.
|
||||||
///
|
///
|
||||||
/// Optional
|
/// Optional
|
||||||
@@ -295,7 +310,7 @@ struct backend_operations {
|
|||||||
/// Get the render time of the last frame. If the render is still in progress,
|
/// Get the render time of the last frame. If the render is still in progress,
|
||||||
/// returns false. The time is returned in `ts`. Frames are delimited by the
|
/// returns false. The time is returned in `ts`. Frames are delimited by the
|
||||||
/// present() calls. i.e. after a present() call, last_render_time() should start
|
/// present() calls. i.e. after a present() call, last_render_time() should start
|
||||||
/// reporting the time of the just presen1ted frame.
|
/// reporting the time of the just presented frame.
|
||||||
///
|
///
|
||||||
/// Optional, if not available, the most conservative estimation will be used.
|
/// Optional, if not available, the most conservative estimation will be used.
|
||||||
bool (*last_render_time)(backend_t *backend_data, struct timespec *ts);
|
bool (*last_render_time)(backend_t *backend_data, struct timespec *ts);
|
||||||
@@ -316,35 +331,39 @@ struct backend_operations {
|
|||||||
*
|
*
|
||||||
* @param backend_data backend data
|
* @param backend_data backend data
|
||||||
* @param prop the property to change
|
* @param prop the property to change
|
||||||
* @param image_data an image data structure returned by the backend
|
* @param image an image handle, cannot be NULL.
|
||||||
* @param args property value
|
* @param args property value
|
||||||
* @return whether the operation is successful
|
* @return whether the operation is successful
|
||||||
*/
|
*/
|
||||||
bool (*set_image_property)(backend_t *backend_data, enum image_properties prop,
|
bool (*set_image_property)(backend_t *backend_data, enum image_properties prop,
|
||||||
void *image_data, void *args);
|
image_handle image, void *args) attr_nonnull(1, 3);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manipulate an image. Image properties are untouched.
|
* Manipulate an image. Image properties are untouched.
|
||||||
*
|
*
|
||||||
* @param backend_data backend data
|
* @param backend_data backend data
|
||||||
* @param op the operation to perform
|
* @param op the operation to perform
|
||||||
* @param image_data an image data structure returned by the backend
|
* @param image an image handle, cannot be NULL.
|
||||||
* @param reg_op the clip region, define the part of the image to be
|
* @param reg_op the clip region, define the part of the image to be
|
||||||
* operated on.
|
* operated on.
|
||||||
* @param reg_visible define the part of the image that will eventually
|
* @param reg_visible define the part of the image that will eventually
|
||||||
* be visible on target. this is a hint to the backend
|
* be visible on target. this is a hint to the backend
|
||||||
* for optimization purposes.
|
* for optimization purposes.
|
||||||
* @param args extra arguments, operation specific
|
* @param args extra arguments, operation specific
|
||||||
* @return whether the operation is successful
|
* @return whether the operation is successful
|
||||||
*/
|
*/
|
||||||
bool (*image_op)(backend_t *backend_data, enum image_operations op, void *image_data,
|
bool (*image_op)(backend_t *backend_data, enum image_operations op,
|
||||||
const region_t *reg_op, const region_t *reg_visible, void *args);
|
image_handle image, const region_t *reg_op,
|
||||||
|
const region_t *reg_visible, void *args) attr_nonnull(1, 3, 4, 5);
|
||||||
|
|
||||||
/// Create another instance of the `image_data`. All `image_op` and
|
/// Create another instance of the `image`. The newly created image
|
||||||
/// `set_image_property` calls on the returned image should not affect the
|
/// inherits its content and all image properties from the image being
|
||||||
/// original image
|
/// cloned. All `image_op` and `set_image_property` calls on the
|
||||||
void *(*clone_image)(backend_t *base, const void *image_data,
|
/// returned image should not affect the original image.
|
||||||
const region_t *reg_visible);
|
///
|
||||||
|
/// @param image the image to be cloned, must not be NULL.
|
||||||
|
image_handle (*clone_image)(backend_t *base, image_handle image,
|
||||||
|
const region_t *reg_visible) attr_nonnull_all;
|
||||||
|
|
||||||
/// Create a blur context that can be used to call `blur`
|
/// Create a blur context that can be used to call `blur`
|
||||||
void *(*create_blur_context)(backend_t *base, enum blur_method, void *args);
|
void *(*create_blur_context)(backend_t *base, enum blur_method, void *args);
|
||||||
|
|||||||
@@ -293,8 +293,8 @@ shadow_picture_err:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *default_render_shadow(backend_t *backend_data, int width, int height,
|
image_handle default_render_shadow(backend_t *backend_data, int width, int height,
|
||||||
struct backend_shadow_context *sctx, struct color color) {
|
struct backend_shadow_context *sctx, struct color color) {
|
||||||
const conv *kernel = (void *)sctx;
|
const conv *kernel = (void *)sctx;
|
||||||
xcb_render_picture_t shadow_pixel =
|
xcb_render_picture_t shadow_pixel =
|
||||||
solid_picture(backend_data->c, true, 1, color.red, color.green, color.blue);
|
solid_picture(backend_data->c, true, 1, color.red, color.green, color.blue);
|
||||||
@@ -308,7 +308,7 @@ void *default_render_shadow(backend_t *backend_data, int width, int height,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32);
|
auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32);
|
||||||
void *ret = backend_data->ops->bind_pixmap(
|
auto ret = backend_data->ops->bind_pixmap(
|
||||||
backend_data, shadow, x_get_visual_info(backend_data->c, visual), true);
|
backend_data, shadow, x_get_visual_info(backend_data->c, visual), true);
|
||||||
x_free_picture(backend_data->c, pict);
|
x_free_picture(backend_data->c, pict);
|
||||||
x_free_picture(backend_data->c, shadow_pixel);
|
x_free_picture(backend_data->c, shadow_pixel);
|
||||||
@@ -316,16 +316,16 @@ void *default_render_shadow(backend_t *backend_data, int width, int height,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Implement render_shadow with shadow_from_mask
|
/// Implement render_shadow with shadow_from_mask
|
||||||
void *
|
image_handle
|
||||||
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
|
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
|
||||||
struct backend_shadow_context *sctx, struct color color) {
|
struct backend_shadow_context *sctx, struct color color) {
|
||||||
region_t reg;
|
region_t reg;
|
||||||
pixman_region32_init_rect(®, 0, 0, (unsigned int)width, (unsigned int)height);
|
pixman_region32_init_rect(®, 0, 0, (unsigned int)width, (unsigned int)height);
|
||||||
void *mask = backend_data->ops->make_mask(
|
auto mask = backend_data->ops->make_mask(
|
||||||
backend_data, (geometry_t){.width = width, .height = height}, ®);
|
backend_data, (geometry_t){.width = width, .height = height}, ®);
|
||||||
pixman_region32_fini(®);
|
pixman_region32_fini(®);
|
||||||
|
|
||||||
void *shadow = backend_data->ops->shadow_from_mask(backend_data, mask, sctx, color);
|
auto shadow = backend_data->ops->shadow_from_mask(backend_data, mask, sctx, color);
|
||||||
backend_data->ops->release_image(backend_data, mask);
|
backend_data->ops->release_image(backend_data, mask);
|
||||||
return shadow;
|
return shadow;
|
||||||
}
|
}
|
||||||
@@ -458,17 +458,17 @@ struct dual_kawase_params *generate_dual_kawase_params(void *args) {
|
|||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *default_clone_image(backend_t *base attr_unused, const void *image_data,
|
image_handle default_clone_image(backend_t *base attr_unused, image_handle image,
|
||||||
const region_t *reg_visible attr_unused) {
|
const region_t *reg_visible attr_unused) {
|
||||||
auto new_img = ccalloc(1, struct backend_image);
|
auto new_img = ccalloc(1, struct backend_image);
|
||||||
*new_img = *(struct backend_image *)image_data;
|
*new_img = *(struct backend_image *)image;
|
||||||
new_img->inner->refcount++;
|
new_img->inner->refcount++;
|
||||||
return new_img;
|
return (image_handle)new_img;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
|
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
|
||||||
void *image_data, void *arg) {
|
image_handle image, void *arg) {
|
||||||
struct backend_image *tex = image_data;
|
auto tex = (struct backend_image *)image;
|
||||||
int *iargs = arg;
|
int *iargs = arg;
|
||||||
bool *bargs = arg;
|
bool *bargs = arg;
|
||||||
double *dargs = arg;
|
double *dargs = arg;
|
||||||
@@ -490,8 +490,8 @@ bool default_set_image_property(backend_t *base attr_unused, enum image_properti
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool default_is_image_transparent(backend_t *base attr_unused, void *image_data) {
|
bool default_is_image_transparent(backend_t *base attr_unused, image_handle image) {
|
||||||
struct backend_image *img = image_data;
|
auto img = (struct backend_image *)image;
|
||||||
return img->opacity < 1 || img->inner->has_alpha;
|
return img->opacity < 1 || img->inner->has_alpha;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ solid_picture(struct x_connection *, bool argb, double a, double r, double g, do
|
|||||||
xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opacity,
|
xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opacity,
|
||||||
int width, int height);
|
int width, int height);
|
||||||
|
|
||||||
void *default_render_shadow(backend_t *backend_data, int width, int height,
|
image_handle default_render_shadow(backend_t *backend_data, int width, int height,
|
||||||
struct backend_shadow_context *sctx, struct color color);
|
struct backend_shadow_context *sctx, struct color color);
|
||||||
|
|
||||||
/// Implement `render_shadow` with `shadow_from_mask`.
|
/// Implement `render_shadow` with `shadow_from_mask`.
|
||||||
void *
|
image_handle
|
||||||
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
|
backend_render_shadow_from_mask(backend_t *backend_data, int width, int height,
|
||||||
struct backend_shadow_context *sctx, struct color color);
|
struct backend_shadow_context *sctx, struct color color);
|
||||||
struct backend_shadow_context *
|
struct backend_shadow_context *
|
||||||
@@ -72,8 +72,8 @@ 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);
|
struct dual_kawase_params *generate_dual_kawase_params(void *args);
|
||||||
|
|
||||||
void *default_clone_image(backend_t *base, const void *image_data, const region_t *reg);
|
image_handle default_clone_image(backend_t *base, image_handle image, const region_t *reg);
|
||||||
bool default_is_image_transparent(backend_t *base attr_unused, void *image_data);
|
bool default_is_image_transparent(backend_t *base attr_unused, image_handle image);
|
||||||
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
|
bool default_set_image_property(backend_t *base attr_unused, enum image_properties op,
|
||||||
void *image_data, void *arg);
|
image_handle image, void *arg);
|
||||||
struct backend_image *default_new_backend_image(int w, int h);
|
struct backend_image *default_new_backend_image(int w, int h);
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ void apply_driver_workarounds(struct session *ps, enum driver driver) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver) {
|
enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver attr_unused) {
|
||||||
|
enum vblank_scheduler_type type = VBLANK_SCHEDULER_PRESENT;
|
||||||
|
#ifdef CONFIG_OPENGL
|
||||||
if (driver & DRIVER_NVIDIA) {
|
if (driver & DRIVER_NVIDIA) {
|
||||||
return VBLANK_SCHEDULER_SGI_VIDEO_SYNC;
|
type = VBLANK_SCHEDULER_SGI_VIDEO_SYNC;
|
||||||
}
|
}
|
||||||
return VBLANK_SCHEDULER_PRESENT;
|
#endif
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|||||||
@@ -50,8 +50,9 @@ void dummy_deinit(struct backend_base *data) {
|
|||||||
free(dummy);
|
free(dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dummy_check_image(struct backend_base *base, const struct dummy_image *img) {
|
static void dummy_check_image(struct backend_base *base, image_handle image) {
|
||||||
auto dummy = (struct dummy_data *)base;
|
auto dummy = (struct dummy_data *)base;
|
||||||
|
auto img = (struct dummy_image *)image;
|
||||||
if (img == (struct dummy_image *)&dummy->mask) {
|
if (img == (struct dummy_image *)&dummy->mask) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,13 +65,13 @@ 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, coord_t dst attr_unused,
|
void dummy_compose(struct backend_base *base, image_handle image, coord_t dst attr_unused,
|
||||||
void *mask attr_unused, coord_t mask_dst attr_unused,
|
image_handle mask attr_unused, coord_t mask_dst attr_unused,
|
||||||
const region_t *reg_paint attr_unused,
|
const region_t *reg_paint attr_unused,
|
||||||
const region_t *reg_visible attr_unused, bool lerp attr_unused) {
|
const region_t *reg_visible attr_unused, bool lerp attr_unused) {
|
||||||
auto dummy attr_unused = (struct dummy_data *)base;
|
auto dummy attr_unused = (struct dummy_data *)base;
|
||||||
dummy_check_image(base, image);
|
dummy_check_image(base, image);
|
||||||
assert(mask == NULL || mask == &dummy->mask);
|
assert(mask == NULL || (struct backend_image *)mask == &dummy->mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dummy_fill(struct backend_base *backend_data attr_unused, struct color c attr_unused,
|
void dummy_fill(struct backend_base *backend_data attr_unused, struct color c attr_unused,
|
||||||
@@ -78,20 +79,20 @@ void dummy_fill(struct backend_base *backend_data attr_unused, struct color c at
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity attr_unused,
|
bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity attr_unused,
|
||||||
void *blur_ctx attr_unused, void *mask attr_unused,
|
void *blur_ctx attr_unused, image_handle mask attr_unused,
|
||||||
coord_t mask_dst attr_unused, const region_t *reg_blur attr_unused,
|
coord_t mask_dst attr_unused, const region_t *reg_blur attr_unused,
|
||||||
const region_t *reg_visible attr_unused) {
|
const region_t *reg_visible attr_unused) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
|
image_handle dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
|
||||||
struct xvisual_info fmt, bool owned) {
|
struct xvisual_info fmt, bool owned) {
|
||||||
auto dummy = (struct dummy_data *)base;
|
auto dummy = (struct dummy_data *)base;
|
||||||
struct dummy_image *img = NULL;
|
struct dummy_image *img = NULL;
|
||||||
HASH_FIND_INT(dummy->images, &pixmap, img);
|
HASH_FIND_INT(dummy->images, &pixmap, img);
|
||||||
if (img) {
|
if (img) {
|
||||||
(*img->refcount)++;
|
(*img->refcount)++;
|
||||||
return img;
|
return (image_handle)img;
|
||||||
}
|
}
|
||||||
|
|
||||||
img = ccalloc(1, struct dummy_image);
|
img = ccalloc(1, struct dummy_image);
|
||||||
@@ -102,12 +103,12 @@ void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
|
|||||||
img->owned = owned;
|
img->owned = owned;
|
||||||
|
|
||||||
HASH_ADD_INT(dummy->images, pixmap, img);
|
HASH_ADD_INT(dummy->images, pixmap, img);
|
||||||
return (void *)img;
|
return (image_handle)img;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dummy_release_image(backend_t *base, void *image) {
|
void dummy_release_image(backend_t *base, image_handle image) {
|
||||||
auto dummy = (struct dummy_data *)base;
|
auto dummy = (struct dummy_data *)base;
|
||||||
if (image == &dummy->mask) {
|
if ((struct backend_image *)image == &dummy->mask) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto img = (struct dummy_image *)image;
|
auto img = (struct dummy_image *)image;
|
||||||
@@ -123,10 +124,9 @@ void dummy_release_image(backend_t *base, void *image) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dummy_is_image_transparent(struct backend_base *base, void *image) {
|
bool dummy_is_image_transparent(struct backend_base *base, image_handle image) {
|
||||||
auto img = (struct dummy_image *)image;
|
dummy_check_image(base, image);
|
||||||
dummy_check_image(base, img);
|
return ((struct dummy_image *)image)->transparent;
|
||||||
return img->transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int dummy_buffer_age(struct backend_base *base attr_unused) {
|
int dummy_buffer_age(struct backend_base *base attr_unused) {
|
||||||
@@ -134,29 +134,31 @@ int dummy_buffer_age(struct backend_base *base attr_unused) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unused,
|
bool dummy_image_op(struct backend_base *base, enum image_operations op attr_unused,
|
||||||
void *image, const region_t *reg_op attr_unused,
|
image_handle image, const region_t *reg_op attr_unused,
|
||||||
const region_t *reg_visible attr_unused, void *args attr_unused) {
|
const region_t *reg_visible attr_unused, void *args attr_unused) {
|
||||||
dummy_check_image(base, image);
|
dummy_check_image(base, image);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *dummy_make_mask(struct backend_base *base, geometry_t size attr_unused,
|
image_handle dummy_make_mask(struct backend_base *base, geometry_t size attr_unused,
|
||||||
const region_t *reg attr_unused) {
|
const region_t *reg attr_unused) {
|
||||||
return &(((struct dummy_data *)base)->mask);
|
auto dummy = (struct dummy_data *)base;
|
||||||
|
auto mask = &dummy->mask;
|
||||||
|
return (image_handle)mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dummy_set_image_property(struct backend_base *base, enum image_properties prop attr_unused,
|
bool dummy_set_image_property(struct backend_base *base, enum image_properties prop attr_unused,
|
||||||
void *image, void *arg attr_unused) {
|
image_handle image, void *arg attr_unused) {
|
||||||
dummy_check_image(base, image);
|
dummy_check_image(base, image);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *dummy_clone_image(struct backend_base *base, const void *image,
|
image_handle dummy_clone_image(struct backend_base *base, image_handle image,
|
||||||
const region_t *reg_visible attr_unused) {
|
const region_t *reg_visible attr_unused) {
|
||||||
auto img = (const struct dummy_image *)image;
|
dummy_check_image(base, image);
|
||||||
dummy_check_image(base, img);
|
auto image_impl = (struct dummy_image *)image;
|
||||||
(*img->refcount)++;
|
(*image_impl->refcount)++;
|
||||||
return (void *)img;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *dummy_create_blur_context(struct backend_base *base attr_unused,
|
void *dummy_create_blur_context(struct backend_base *base attr_unused,
|
||||||
|
|||||||
@@ -256,10 +256,11 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst,
|
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx,
|
||||||
const region_t *reg_blur, const region_t *reg_visible attr_unused,
|
struct backend_image *mask, coord_t mask_dst, const region_t *reg_blur,
|
||||||
GLuint source_texture, geometry_t source_size, GLuint target_fbo,
|
const region_t *reg_visible attr_unused, GLuint source_texture,
|
||||||
GLuint default_mask, bool high_precision) {
|
geometry_t source_size, GLuint target_fbo, GLuint default_mask,
|
||||||
|
bool high_precision) {
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
if (source_size.width != bctx->fb_width || source_size.height != bctx->fb_height) {
|
if (source_size.width != bctx->fb_width || source_size.height != bctx->fb_height) {
|
||||||
@@ -400,12 +401,12 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coor
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mask_dst,
|
bool gl_blur(backend_t *base, double opacity, void *ctx, image_handle mask, coord_t mask_dst,
|
||||||
const region_t *reg_blur, const region_t *reg_visible attr_unused) {
|
const region_t *reg_blur, const region_t *reg_visible attr_unused) {
|
||||||
auto gd = (struct gl_data *)base;
|
auto gd = (struct gl_data *)base;
|
||||||
auto bctx = (struct gl_blur_context *)ctx;
|
auto bctx = (struct gl_blur_context *)ctx;
|
||||||
return gl_blur_impl(opacity, bctx, mask, mask_dst, reg_blur, reg_visible,
|
return gl_blur_impl(opacity, bctx, (struct backend_image *)mask, mask_dst,
|
||||||
gd->back_texture,
|
reg_blur, reg_visible, gd->back_texture,
|
||||||
(geometry_t){.width = gd->width, .height = gd->height},
|
(geometry_t){.width = gd->width, .height = gd->height},
|
||||||
gd->back_fbo, gd->default_mask_texture, gd->dithered_present);
|
gd->back_fbo, gd->default_mask_texture, gd->dithered_present);
|
||||||
}
|
}
|
||||||
@@ -604,9 +605,9 @@ bool gl_create_kernel_blur_context(void *blur_context, GLfloat *projection,
|
|||||||
bind_uniform(pass, mask_offset);
|
bind_uniform(pass, mask_offset);
|
||||||
bind_uniform(pass, mask_inverted);
|
bind_uniform(pass, mask_inverted);
|
||||||
bind_uniform(pass, mask_corner_radius);
|
bind_uniform(pass, mask_corner_radius);
|
||||||
log_info("Uniform locations: %d %d %d %d %d", pass->uniform_mask_tex,
|
log_debug("Uniform locations: %d %d %d %d %d", pass->uniform_mask_tex,
|
||||||
pass->uniform_mask_offset, pass->uniform_mask_inverted,
|
pass->uniform_mask_offset, pass->uniform_mask_inverted,
|
||||||
pass->uniform_mask_corner_radius, pass->uniform_opacity);
|
pass->uniform_mask_corner_radius, pass->uniform_opacity);
|
||||||
pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
|
pass->texorig_loc = glGetUniformLocationChecked(pass->prog, "texorig");
|
||||||
|
|
||||||
// Setup projection matrix
|
// Setup projection matrix
|
||||||
|
|||||||
@@ -36,12 +36,6 @@ struct egl_data {
|
|||||||
EGLContext ctx;
|
EGLContext ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
static PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC glEGLImageTargetTexStorage = NULL;
|
|
||||||
static PFNEGLCREATEIMAGEKHRPROC eglCreateImageProc = NULL;
|
|
||||||
static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageProc = NULL;
|
|
||||||
static PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplayProc = NULL;
|
|
||||||
static PFNEGLCREATEPLATFORMWINDOWSURFACEPROC eglCreatePlatformWindowSurfaceProc = NULL;
|
|
||||||
|
|
||||||
const char *eglGetErrorString(EGLint error) {
|
const char *eglGetErrorString(EGLint error) {
|
||||||
#define CASE_STR(value) \
|
#define CASE_STR(value) \
|
||||||
case value: return #value;
|
case value: return #value;
|
||||||
@@ -74,7 +68,7 @@ static void egl_release_image(backend_t *base, struct gl_texture *tex) {
|
|||||||
struct egl_pixmap *p = tex->user_data;
|
struct egl_pixmap *p = tex->user_data;
|
||||||
// Release binding
|
// Release binding
|
||||||
if (p->image != EGL_NO_IMAGE) {
|
if (p->image != EGL_NO_IMAGE) {
|
||||||
eglDestroyImageProc(gd->display, p->image);
|
eglDestroyImage(gd->display, p->image);
|
||||||
p->image = EGL_NO_IMAGE;
|
p->image = EGL_NO_IMAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,18 +128,6 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
struct egl_data *gd = NULL;
|
struct egl_data *gd = NULL;
|
||||||
|
|
||||||
#define get_proc(name, type) \
|
|
||||||
name##Proc = (type)eglGetProcAddress(#name); \
|
|
||||||
if (!name##Proc) { \
|
|
||||||
log_error("Failed to get EGL function " #name); \
|
|
||||||
goto end; \
|
|
||||||
}
|
|
||||||
get_proc(eglCreateImage, PFNEGLCREATEIMAGEKHRPROC);
|
|
||||||
get_proc(eglDestroyImage, PFNEGLDESTROYIMAGEKHRPROC);
|
|
||||||
get_proc(eglGetPlatformDisplay, PFNEGLGETPLATFORMDISPLAYPROC);
|
|
||||||
get_proc(eglCreatePlatformWindowSurface, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC);
|
|
||||||
#undef get_proc
|
|
||||||
|
|
||||||
// Check if we have the X11 platform
|
// Check if we have the X11 platform
|
||||||
const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
const char *exts = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||||
if (strstr(exts, "EGL_EXT_platform_x11") == NULL) {
|
if (strstr(exts, "EGL_EXT_platform_x11") == NULL) {
|
||||||
@@ -154,12 +136,12 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gd = ccalloc(1, struct egl_data);
|
gd = ccalloc(1, struct egl_data);
|
||||||
gd->display = eglGetPlatformDisplayProc(EGL_PLATFORM_X11_EXT, ps->c.dpy,
|
gd->display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, ps->c.dpy,
|
||||||
(EGLAttrib[]){
|
(EGLint[]){
|
||||||
EGL_PLATFORM_X11_SCREEN_EXT,
|
EGL_PLATFORM_X11_SCREEN_EXT,
|
||||||
ps->c.screen,
|
ps->c.screen,
|
||||||
EGL_NONE,
|
EGL_NONE,
|
||||||
});
|
});
|
||||||
if (gd->display == EGL_NO_DISPLAY) {
|
if (gd->display == EGL_NO_DISPLAY) {
|
||||||
log_error("Failed to get EGL display.");
|
log_error("Failed to get EGL display.");
|
||||||
goto end;
|
goto end;
|
||||||
@@ -212,7 +194,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
gd->target_win =
|
gd->target_win =
|
||||||
eglCreatePlatformWindowSurfaceProc(gd->display, config, &target, NULL);
|
eglCreatePlatformWindowSurfaceEXT(gd->display, config, &target, NULL);
|
||||||
if (gd->target_win == EGL_NO_SURFACE) {
|
if (gd->target_win == EGL_NO_SURFACE) {
|
||||||
log_error("Failed to create EGL surface.");
|
log_error("Failed to create EGL surface.");
|
||||||
goto end;
|
goto end;
|
||||||
@@ -243,14 +225,6 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
glEGLImageTargetTexStorage =
|
|
||||||
(PFNGLEGLIMAGETARGETTEXSTORAGEEXTPROC)eglGetProcAddress("glEGLImageTargetTexS"
|
|
||||||
"torageEXT");
|
|
||||||
if (glEGLImageTargetTexStorage == NULL) {
|
|
||||||
log_error("Failed to get glEGLImageTargetTexStorageEXT.");
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
gd->gl.decouple_texture_user_data = egl_decouple_user_data;
|
gd->gl.decouple_texture_user_data = egl_decouple_user_data;
|
||||||
gd->gl.release_user_data = egl_release_image;
|
gd->gl.release_user_data = egl_release_image;
|
||||||
|
|
||||||
@@ -275,7 +249,7 @@ end:
|
|||||||
return &gd->gl.base;
|
return &gd->gl.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static image_handle
|
||||||
egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
|
egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
|
||||||
struct egl_data *gd = (void *)base;
|
struct egl_data *gd = (void *)base;
|
||||||
struct egl_pixmap *eglpixmap = NULL;
|
struct egl_pixmap *eglpixmap = NULL;
|
||||||
@@ -302,9 +276,8 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
|||||||
|
|
||||||
eglpixmap = cmalloc(struct egl_pixmap);
|
eglpixmap = cmalloc(struct egl_pixmap);
|
||||||
eglpixmap->pixmap = pixmap;
|
eglpixmap->pixmap = pixmap;
|
||||||
eglpixmap->image =
|
eglpixmap->image = eglCreateImage(gd->display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
|
||||||
eglCreateImageProc(gd->display, EGL_NO_CONTEXT, EGL_NATIVE_PIXMAP_KHR,
|
(EGLClientBuffer)(uintptr_t)pixmap, NULL);
|
||||||
(EGLClientBuffer)(uintptr_t)pixmap, NULL);
|
|
||||||
eglpixmap->owned = owned;
|
eglpixmap->owned = owned;
|
||||||
|
|
||||||
if (eglpixmap->image == EGL_NO_IMAGE) {
|
if (eglpixmap->image == EGL_NO_IMAGE) {
|
||||||
@@ -324,14 +297,14 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
|||||||
wd->dim = 0;
|
wd->dim = 0;
|
||||||
wd->inner->refcount = 1;
|
wd->inner->refcount = 1;
|
||||||
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
||||||
glEGLImageTargetTexStorage(GL_TEXTURE_2D, eglpixmap->image, NULL);
|
glEGLImageTargetTexStorageEXT(GL_TEXTURE_2D, eglpixmap->image, NULL);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
gl_check_err();
|
gl_check_err();
|
||||||
return wd;
|
return (image_handle)wd;
|
||||||
err:
|
err:
|
||||||
if (eglpixmap && eglpixmap->image) {
|
if (eglpixmap && eglpixmap->image) {
|
||||||
eglDestroyImageProc(gd->display, eglpixmap->image);
|
eglDestroyImage(gd->display, eglpixmap->image);
|
||||||
}
|
}
|
||||||
free(eglpixmap);
|
free(eglpixmap);
|
||||||
|
|
||||||
@@ -422,41 +395,6 @@ struct backend_operations egl_ops = {
|
|||||||
.max_buffer_age = 5, // Why?
|
.max_buffer_age = 5, // Why?
|
||||||
};
|
};
|
||||||
|
|
||||||
PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName;
|
|
||||||
/**
|
|
||||||
* Check if a EGL extension exists.
|
|
||||||
*/
|
|
||||||
static inline bool egl_has_extension(EGLDisplay dpy, const char *ext) {
|
|
||||||
const char *egl_exts = eglQueryString(dpy, EGL_EXTENSIONS);
|
|
||||||
if (!egl_exts) {
|
|
||||||
log_error("Failed get EGL extension list.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto inlen = strlen(ext);
|
|
||||||
const char *curr = egl_exts;
|
|
||||||
bool match = false;
|
|
||||||
while (curr && !match) {
|
|
||||||
const char *end = strchr(curr, ' ');
|
|
||||||
if (!end) {
|
|
||||||
// Last extension string
|
|
||||||
match = strcmp(ext, curr) == 0;
|
|
||||||
} else if (curr + inlen == end) {
|
|
||||||
// Length match, do match string
|
|
||||||
match = strncmp(ext, curr, (unsigned long)(end - curr)) == 0;
|
|
||||||
}
|
|
||||||
curr = end ? end + 1 : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
log_info("Missing EGL extension %s.", ext);
|
|
||||||
} else {
|
|
||||||
log_info("Found EGL extension %s.", ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct eglext_info eglext = {0};
|
struct eglext_info eglext = {0};
|
||||||
|
|
||||||
void eglext_init(EGLDisplay dpy) {
|
void eglext_init(EGLDisplay dpy) {
|
||||||
@@ -464,7 +402,10 @@ void eglext_init(EGLDisplay dpy) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
eglext.initialized = true;
|
eglext.initialized = true;
|
||||||
#define check_ext(name) eglext.has_##name = egl_has_extension(dpy, #name)
|
#define check_ext(name) \
|
||||||
|
eglext.has_##name = epoxy_has_egl_extension(dpy, #name); \
|
||||||
|
log_info("Extension " #name " - %s", eglext.has_##name ? "present" : "absent")
|
||||||
|
|
||||||
check_ext(EGL_EXT_buffer_age);
|
check_ext(EGL_EXT_buffer_age);
|
||||||
check_ext(EGL_EXT_create_context_robustness);
|
check_ext(EGL_EXT_create_context_robustness);
|
||||||
check_ext(EGL_KHR_image_pixmap);
|
check_ext(EGL_KHR_image_pixmap);
|
||||||
@@ -472,16 +413,4 @@ void eglext_init(EGLDisplay dpy) {
|
|||||||
check_ext(EGL_MESA_query_driver);
|
check_ext(EGL_MESA_query_driver);
|
||||||
#endif
|
#endif
|
||||||
#undef check_ext
|
#undef check_ext
|
||||||
|
|
||||||
// Checking if the returned function pointer is NULL is not really necessary,
|
|
||||||
// or maybe not even useful, since eglGetProcAddress might always return
|
|
||||||
// something. We are doing it just for completeness' sake.
|
|
||||||
|
|
||||||
#ifdef EGL_MESA_query_driver
|
|
||||||
eglGetDisplayDriverName =
|
|
||||||
(PFNEGLGETDISPLAYDRIVERNAMEPROC)eglGetProcAddress("eglGetDisplayDriverName");
|
|
||||||
if (!eglGetDisplayDriverName) {
|
|
||||||
eglext.has_EGL_MESA_query_driver = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <EGL/egl.h>
|
#include <epoxy/egl.h>
|
||||||
#include <EGL/eglext.h>
|
#include <epoxy/gl.h>
|
||||||
#include <GL/gl.h>
|
|
||||||
#include <GL/glext.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <xcb/render.h>
|
#include <xcb/render.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
@@ -24,8 +22,4 @@ struct eglext_info {
|
|||||||
|
|
||||||
extern struct eglext_info eglext;
|
extern struct eglext_info eglext;
|
||||||
|
|
||||||
#ifdef EGL_MESA_query_driver
|
|
||||||
extern PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void eglext_init(EGLDisplay);
|
void eglext_init(EGLDisplay);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
#include <GL/gl.h>
|
#include <epoxy/gl.h>
|
||||||
#include <GL/glext.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -185,7 +184,7 @@ void gl_destroy_window_shader(backend_t *backend_data attr_unused, void *shader)
|
|||||||
* @note In order to reduce number of textures which needs to be
|
* @note In order to reduce number of textures which needs to be
|
||||||
* allocated and deleted during this recursive render
|
* allocated and deleted during this recursive render
|
||||||
* we reuse the same two textures for render source and
|
* we reuse the same two textures for render source and
|
||||||
* destination simply by alterating between them.
|
* destination simply by alternating between them.
|
||||||
* Unfortunately on first iteration source_texture might
|
* Unfortunately on first iteration source_texture might
|
||||||
* be read-only. In this case we will select auxiliary_texture as
|
* be read-only. In this case we will select auxiliary_texture as
|
||||||
* destination_texture in order not to touch that read-only source
|
* destination_texture in order not to touch that read-only source
|
||||||
@@ -253,7 +252,7 @@ _gl_average_texture_color(backend_t *base, GLuint source_texture, GLuint destina
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @brief Builds a 1x1 texture which has color corresponding to the average of all
|
* @brief Builds a 1x1 texture which has color corresponding to the average of all
|
||||||
* pixels of img by recursively rendering into texture of quorter the size (half
|
* pixels of img by recursively rendering into texture of quarter the size (half
|
||||||
* width and half height).
|
* width and half height).
|
||||||
* Returned texture must not be deleted, since it's owned by the gl_image. It will be
|
* Returned texture must not be deleted, since it's owned by the gl_image. It will be
|
||||||
* deleted when the gl_image is released.
|
* deleted when the gl_image is released.
|
||||||
@@ -390,6 +389,10 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
|
|||||||
if (win_shader->uniform_tex >= 0) {
|
if (win_shader->uniform_tex >= 0) {
|
||||||
glUniform1i(win_shader->uniform_tex, 0);
|
glUniform1i(win_shader->uniform_tex, 0);
|
||||||
}
|
}
|
||||||
|
if (win_shader->uniform_effective_size >= 0) {
|
||||||
|
glUniform2f(win_shader->uniform_effective_size, (float)img->ewidth,
|
||||||
|
(float)img->eheight);
|
||||||
|
}
|
||||||
if (win_shader->uniform_dim >= 0) {
|
if (win_shader->uniform_dim >= 0) {
|
||||||
glUniform1f(win_shader->uniform_dim, (float)img->dim);
|
glUniform1f(win_shader->uniform_dim, (float)img->dim);
|
||||||
}
|
}
|
||||||
@@ -549,11 +552,12 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(yshui) make use of reg_visible
|
// TODO(yshui) make use of reg_visible
|
||||||
void gl_compose(backend_t *base, void *image_data, coord_t image_dst, void *mask,
|
void gl_compose(backend_t *base, image_handle image_data, coord_t image_dst, image_handle mask_,
|
||||||
coord_t mask_dst, const region_t *reg_tgt,
|
coord_t mask_dst, const region_t *reg_tgt,
|
||||||
const region_t *reg_visible attr_unused, bool lerp) {
|
const region_t *reg_visible attr_unused, bool lerp) {
|
||||||
auto gd = (struct gl_data *)base;
|
auto gd = (struct gl_data *)base;
|
||||||
struct backend_image *img = image_data;
|
auto img = (struct backend_image *)image_data;
|
||||||
|
auto mask = (struct backend_image *)mask_;
|
||||||
auto inner = (struct gl_texture *)img->inner;
|
auto inner = (struct gl_texture *)img->inner;
|
||||||
|
|
||||||
// Painting
|
// Painting
|
||||||
@@ -608,6 +612,7 @@ static bool gl_win_shader_from_stringv(const char **vshader_strv,
|
|||||||
bind_uniform(ret, opacity);
|
bind_uniform(ret, opacity);
|
||||||
bind_uniform(ret, invert_color);
|
bind_uniform(ret, invert_color);
|
||||||
bind_uniform(ret, tex);
|
bind_uniform(ret, tex);
|
||||||
|
bind_uniform(ret, effective_size);
|
||||||
bind_uniform(ret, dim);
|
bind_uniform(ret, dim);
|
||||||
bind_uniform(ret, brightness);
|
bind_uniform(ret, brightness);
|
||||||
bind_uniform(ret, max_brightness);
|
bind_uniform(ret, max_brightness);
|
||||||
@@ -714,7 +719,7 @@ void gl_fill(backend_t *base, struct color c, const region_t *clip) {
|
|||||||
return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
|
return _gl_fill(base, c, clip, gd->back_fbo, gd->height, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
image_handle gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
||||||
auto tex = ccalloc(1, struct gl_texture);
|
auto tex = ccalloc(1, struct gl_texture);
|
||||||
auto img = default_new_backend_image(size.width, size.height);
|
auto img = default_new_backend_image(size.width, size.height);
|
||||||
tex->width = size.width;
|
tex->width = size.width;
|
||||||
@@ -745,7 +750,7 @@ void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
|||||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
glDeleteFramebuffers(1, &fbo);
|
glDeleteFramebuffers(1, &fbo);
|
||||||
return img;
|
return (image_handle)img;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
|
static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
|
||||||
@@ -761,8 +766,8 @@ static void gl_release_image_inner(backend_t *base, struct gl_texture *inner) {
|
|||||||
gl_check_err();
|
gl_check_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
void gl_release_image(backend_t *base, void *image_data) {
|
void gl_release_image(backend_t *base, image_handle image) {
|
||||||
struct backend_image *wd = image_data;
|
auto wd = (struct backend_image *)image;
|
||||||
auto inner = (struct gl_texture *)wd->inner;
|
auto inner = (struct gl_texture *)wd->inner;
|
||||||
inner->refcount--;
|
inner->refcount--;
|
||||||
assert(inner->refcount >= 0);
|
assert(inner->refcount >= 0);
|
||||||
@@ -889,24 +894,35 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
|||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
|
|
||||||
gd->dithered_present = ps->o.dithered_present;
|
gd->dithered_present = ps->o.dithered_present;
|
||||||
|
gd->dummy_prog =
|
||||||
|
gl_create_program_from_strv((const char *[]){present_vertex_shader, NULL},
|
||||||
|
(const char *[]){dummy_frag, NULL});
|
||||||
|
if (!gd->dummy_prog) {
|
||||||
|
log_error("Failed to create the dummy shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (gd->dithered_present) {
|
if (gd->dithered_present) {
|
||||||
gd->present_prog = gl_create_program_from_strv(
|
gd->present_prog = gl_create_program_from_strv(
|
||||||
(const char *[]){present_vertex_shader, NULL},
|
(const char *[]){present_vertex_shader, NULL},
|
||||||
(const char *[]){present_frag, dither_glsl, NULL});
|
(const char *[]){present_frag, dither_glsl, NULL});
|
||||||
} else {
|
} else {
|
||||||
gd->present_prog = gl_create_program_from_strv(
|
gd->present_prog = gd->dummy_prog;
|
||||||
(const char *[]){present_vertex_shader, NULL},
|
|
||||||
(const char *[]){dummy_frag, NULL});
|
|
||||||
}
|
}
|
||||||
if (!gd->present_prog) {
|
if (!gd->present_prog) {
|
||||||
log_error("Failed to create the present shader");
|
log_error("Failed to create the present shader");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pml = glGetUniformLocationChecked(gd->present_prog, "projection");
|
|
||||||
glUseProgram(gd->present_prog);
|
pml = glGetUniformLocationChecked(gd->dummy_prog, "projection");
|
||||||
glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
|
glUseProgram(gd->dummy_prog);
|
||||||
|
glUniform1i(glGetUniformLocationChecked(gd->dummy_prog, "tex"), 0);
|
||||||
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||||
glUseProgram(0);
|
if (gd->present_prog != gd->dummy_prog) {
|
||||||
|
pml = glGetUniformLocationChecked(gd->present_prog, "projection");
|
||||||
|
glUseProgram(gd->present_prog);
|
||||||
|
glUniform1i(glGetUniformLocationChecked(gd->present_prog, "tex"), 0);
|
||||||
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||||
|
}
|
||||||
|
|
||||||
gd->shadow_shader.prog =
|
gd->shadow_shader.prog =
|
||||||
gl_create_program_from_str(present_vertex_shader, shadow_colorization_frag);
|
gl_create_program_from_str(present_vertex_shader, shadow_colorization_frag);
|
||||||
@@ -916,7 +932,6 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
|||||||
glUseProgram(gd->shadow_shader.prog);
|
glUseProgram(gd->shadow_shader.prog);
|
||||||
glUniform1i(glGetUniformLocationChecked(gd->shadow_shader.prog, "tex"), 0);
|
glUniform1i(glGetUniformLocationChecked(gd->shadow_shader.prog, "tex"), 0);
|
||||||
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||||
glUseProgram(0);
|
|
||||||
glBindFragDataLocation(gd->shadow_shader.prog, 0, "out_color");
|
glBindFragDataLocation(gd->shadow_shader.prog, 0, "out_color");
|
||||||
|
|
||||||
gd->brightness_shader.prog =
|
gd->brightness_shader.prog =
|
||||||
@@ -929,6 +944,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
|||||||
glUseProgram(gd->brightness_shader.prog);
|
glUseProgram(gd->brightness_shader.prog);
|
||||||
glUniform1i(glGetUniformLocationChecked(gd->brightness_shader.prog, "tex"), 0);
|
glUniform1i(glGetUniformLocationChecked(gd->brightness_shader.prog, "tex"), 0);
|
||||||
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
glUniformMatrix4fv(pml, 1, false, projection_matrix[0]);
|
||||||
|
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
|
|
||||||
// Set up the size and format of the back texture
|
// Set up the size and format of the back texture
|
||||||
@@ -965,8 +981,8 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
|||||||
} else {
|
} else {
|
||||||
gd->is_nvidia = false;
|
gd->is_nvidia = false;
|
||||||
}
|
}
|
||||||
gd->has_robustness = gl_has_extension("GL_ARB_robustness");
|
gd->has_robustness = epoxy_has_gl_extension("GL_ARB_robustness");
|
||||||
gd->has_egl_image_storage = gl_has_extension("GL_EXT_EGL_image_storage");
|
gd->has_egl_image_storage = epoxy_has_gl_extension("GL_EXT_EGL_image_storage");
|
||||||
gl_check_err();
|
gl_check_err();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -982,10 +998,25 @@ void gl_deinit(struct gl_data *gd) {
|
|||||||
gl_destroy_window_shader(&gd->base, gd->default_shader);
|
gl_destroy_window_shader(&gd->base, gd->default_shader);
|
||||||
gd->default_shader = NULL;
|
gd->default_shader = NULL;
|
||||||
}
|
}
|
||||||
|
glDeleteProgram(gd->dummy_prog);
|
||||||
|
if (gd->present_prog != gd->dummy_prog) {
|
||||||
|
glDeleteProgram(gd->present_prog);
|
||||||
|
}
|
||||||
|
gd->dummy_prog = 0;
|
||||||
|
gd->present_prog = 0;
|
||||||
|
|
||||||
|
glDeleteProgram(gd->fill_shader.prog);
|
||||||
|
glDeleteProgram(gd->brightness_shader.prog);
|
||||||
|
glDeleteProgram(gd->shadow_shader.prog);
|
||||||
|
gd->fill_shader.prog = 0;
|
||||||
|
gd->brightness_shader.prog = 0;
|
||||||
|
gd->shadow_shader.prog = 0;
|
||||||
|
|
||||||
glDeleteTextures(1, &gd->default_mask_texture);
|
glDeleteTextures(1, &gd->default_mask_texture);
|
||||||
glDeleteTextures(1, &gd->back_texture);
|
glDeleteTextures(1, &gd->back_texture);
|
||||||
|
|
||||||
|
glDeleteQueries(2, gd->frame_timing);
|
||||||
|
|
||||||
gl_check_err();
|
gl_check_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1017,9 +1048,11 @@ static inline void gl_image_decouple(backend_t *base, struct backend_image *img)
|
|||||||
auto new_tex = ccalloc(1, struct gl_texture);
|
auto new_tex = ccalloc(1, struct gl_texture);
|
||||||
|
|
||||||
new_tex->texture = gl_new_texture(GL_TEXTURE_2D);
|
new_tex->texture = gl_new_texture(GL_TEXTURE_2D);
|
||||||
new_tex->y_inverted = true;
|
new_tex->y_inverted = inner->y_inverted;
|
||||||
|
new_tex->has_alpha = inner->has_alpha;
|
||||||
new_tex->height = inner->height;
|
new_tex->height = inner->height;
|
||||||
new_tex->width = inner->width;
|
new_tex->width = inner->width;
|
||||||
|
new_tex->shader = inner->shader;
|
||||||
new_tex->refcount = 1;
|
new_tex->refcount = 1;
|
||||||
new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data);
|
new_tex->user_data = gd->decouple_texture_user_data(base, inner->user_data);
|
||||||
|
|
||||||
@@ -1028,8 +1061,7 @@ static inline void gl_image_decouple(backend_t *base, struct backend_image *img)
|
|||||||
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
GL_BGRA, GL_UNSIGNED_BYTE, NULL);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
assert(gd->present_prog);
|
glUseProgram(gd->dummy_prog);
|
||||||
glUseProgram(gd->present_prog);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
||||||
|
|
||||||
GLuint fbo;
|
GLuint fbo;
|
||||||
@@ -1199,9 +1231,9 @@ bool gl_last_render_time(backend_t *base, struct timespec *ts) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
bool gl_image_op(backend_t *base, enum image_operations op, image_handle image,
|
||||||
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
|
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
|
||||||
struct backend_image *tex = image_data;
|
auto tex = (struct backend_image *)image;
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case IMAGE_OP_APPLY_ALPHA:
|
case IMAGE_OP_APPLY_ALPHA:
|
||||||
gl_image_decouple(base, tex);
|
gl_image_decouple(base, tex);
|
||||||
@@ -1214,12 +1246,12 @@ bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
|
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
|
||||||
void *image_data, void *args) {
|
image_handle image, void *args) {
|
||||||
if (prop != IMAGE_PROPERTY_CUSTOM_SHADER) {
|
if (prop != IMAGE_PROPERTY_CUSTOM_SHADER) {
|
||||||
return default_set_image_property(backend_data, prop, image_data, args);
|
return default_set_image_property(backend_data, prop, image, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct backend_image *img = image_data;
|
auto img = (struct backend_image *)image;
|
||||||
auto inner = (struct gl_texture *)img->inner;
|
auto inner = (struct gl_texture *)img->inner;
|
||||||
inner->shader = args;
|
inner->shader = args;
|
||||||
return true;
|
return true;
|
||||||
@@ -1258,12 +1290,12 @@ void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shado
|
|||||||
free(ctx_);
|
free(ctx_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *gl_shadow_from_mask(backend_t *base, void *mask,
|
image_handle gl_shadow_from_mask(backend_t *base, image_handle mask_,
|
||||||
struct backend_shadow_context *sctx, struct color color) {
|
struct backend_shadow_context *sctx, struct color color) {
|
||||||
log_debug("Create shadow from mask");
|
log_debug("Create shadow from mask");
|
||||||
auto gd = (struct gl_data *)base;
|
auto gd = (struct gl_data *)base;
|
||||||
auto img = (struct backend_image *)mask;
|
auto mask = (struct backend_image *)mask_;
|
||||||
auto inner = (struct gl_texture *)img->inner;
|
auto inner = (struct gl_texture *)mask->inner;
|
||||||
auto gsctx = (struct gl_shadow_context *)sctx;
|
auto gsctx = (struct gl_shadow_context *)sctx;
|
||||||
int radius = (int)gsctx->radius;
|
int radius = (int)gsctx->radius;
|
||||||
|
|
||||||
@@ -1293,7 +1325,7 @@ void *gl_shadow_from_mask(backend_t *base, void *mask,
|
|||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||||
source_texture, 0);
|
source_texture, 0);
|
||||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||||
if (img->color_inverted) {
|
if (mask->color_inverted) {
|
||||||
// If the mask is inverted, clear the source_texture to white, so the
|
// If the mask is inverted, clear the source_texture to white, so the
|
||||||
// "outside" of the mask would be correct
|
// "outside" of the mask would be correct
|
||||||
glClearColor(1, 1, 1, 1);
|
glClearColor(1, 1, 1, 1);
|
||||||
@@ -1402,7 +1434,7 @@ void *gl_shadow_from_mask(backend_t *base, void *mask,
|
|||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
glDeleteFramebuffers(1, &fbo);
|
glDeleteFramebuffers(1, &fbo);
|
||||||
gl_check_err();
|
gl_check_err();
|
||||||
return new_img;
|
return (image_handle)new_img;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum device_status gl_device_status(backend_t *base) {
|
enum device_status gl_device_status(backend_t *base) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <GL/gl.h>
|
#include <epoxy/gl.h>
|
||||||
#include <GL/glext.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "backend/backend.h"
|
#include "backend/backend.h"
|
||||||
|
#include "backend/backend_common.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "region.h"
|
#include "region.h"
|
||||||
|
|
||||||
@@ -29,12 +29,11 @@ static inline GLint glGetUniformLocationChecked(GLuint p, const char *name) {
|
|||||||
|
|
||||||
// Program and uniforms for window shader
|
// Program and uniforms for window shader
|
||||||
typedef struct {
|
typedef struct {
|
||||||
UT_hash_handle hh;
|
|
||||||
uint32_t id;
|
|
||||||
GLuint prog;
|
GLuint prog;
|
||||||
GLint uniform_opacity;
|
GLint uniform_opacity;
|
||||||
GLint uniform_invert_color;
|
GLint uniform_invert_color;
|
||||||
GLint uniform_tex;
|
GLint uniform_tex;
|
||||||
|
GLint uniform_effective_size;
|
||||||
GLint uniform_dim;
|
GLint uniform_dim;
|
||||||
GLint uniform_brightness;
|
GLint uniform_brightness;
|
||||||
GLint uniform_max_brightness;
|
GLint uniform_max_brightness;
|
||||||
@@ -77,7 +76,7 @@ typedef struct {
|
|||||||
GLint color_loc;
|
GLint color_loc;
|
||||||
} gl_fill_shader_t;
|
} gl_fill_shader_t;
|
||||||
|
|
||||||
/// @brief Wrapper of a binded GL texture.
|
/// @brief Wrapper of a bound GL texture.
|
||||||
struct gl_texture {
|
struct gl_texture {
|
||||||
int refcount;
|
int refcount;
|
||||||
bool has_alpha;
|
bool has_alpha;
|
||||||
@@ -111,6 +110,7 @@ struct gl_data {
|
|||||||
GLuint frame_timing[2];
|
GLuint frame_timing[2];
|
||||||
int current_frame_timing;
|
int current_frame_timing;
|
||||||
GLuint present_prog;
|
GLuint present_prog;
|
||||||
|
GLuint dummy_prog;
|
||||||
|
|
||||||
bool dithered_present;
|
bool dithered_present;
|
||||||
|
|
||||||
@@ -144,13 +144,13 @@ void *gl_create_window_shader(backend_t *backend_data, const char *source);
|
|||||||
void gl_destroy_window_shader(backend_t *backend_data, void *shader);
|
void gl_destroy_window_shader(backend_t *backend_data, void *shader);
|
||||||
uint64_t gl_get_shader_attributes(backend_t *backend_data, void *shader);
|
uint64_t gl_get_shader_attributes(backend_t *backend_data, void *shader);
|
||||||
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
|
bool gl_set_image_property(backend_t *backend_data, enum image_properties prop,
|
||||||
void *image_data, void *args);
|
image_handle image, void *args);
|
||||||
bool gl_last_render_time(backend_t *backend_data, struct timespec *time);
|
bool gl_last_render_time(backend_t *backend_data, struct timespec *time);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Render a region with texture data.
|
* @brief Render a region with texture data.
|
||||||
*/
|
*/
|
||||||
void gl_compose(backend_t *, void *image_data, coord_t image_dst, void *mask,
|
void gl_compose(backend_t *, image_handle image_data, coord_t image_dst, image_handle mask_,
|
||||||
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible, bool lerp);
|
coord_t mask_dst, const region_t *reg_tgt, const region_t *reg_visible, bool lerp);
|
||||||
|
|
||||||
void gl_resize(struct gl_data *, int width, int height);
|
void gl_resize(struct gl_data *, int width, int height);
|
||||||
@@ -160,32 +160,32 @@ void gl_deinit(struct gl_data *gd);
|
|||||||
|
|
||||||
GLuint gl_new_texture(GLenum target);
|
GLuint gl_new_texture(GLenum target);
|
||||||
|
|
||||||
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
bool gl_image_op(backend_t *base, enum image_operations op, image_handle image,
|
||||||
const region_t *reg_op, const region_t *reg_visible, void *arg);
|
const region_t *reg_op, const region_t *reg_visible, void *arg);
|
||||||
|
|
||||||
void gl_release_image(backend_t *base, void *image_data);
|
void gl_release_image(backend_t *base, image_handle image);
|
||||||
void *gl_make_mask(backend_t *base, geometry_t size, const region_t *reg);
|
image_handle gl_make_mask(backend_t *base, geometry_t size, const region_t *reg);
|
||||||
|
|
||||||
void *gl_clone(backend_t *base, const void *image_data, const region_t *reg_visible);
|
image_handle gl_clone(backend_t *base, image_handle image, const region_t *reg_visible);
|
||||||
|
|
||||||
bool gl_blur(backend_t *base, double opacity, void *ctx, void *mask, coord_t mask_dst,
|
bool gl_blur(backend_t *base, double opacity, void *ctx, image_handle mask,
|
||||||
const region_t *reg_blur, const region_t *reg_visible);
|
coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible);
|
||||||
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coord_t mask_dst,
|
bool gl_blur_impl(double opacity, struct gl_blur_context *bctx,
|
||||||
const region_t *reg_blur, const region_t *reg_visible attr_unused,
|
struct backend_image *mask, coord_t mask_dst, const region_t *reg_blur,
|
||||||
GLuint source_texture, geometry_t source_size, GLuint target_fbo,
|
const region_t *reg_visible attr_unused, GLuint source_texture,
|
||||||
GLuint default_mask, bool high_precision);
|
geometry_t source_size, GLuint target_fbo, GLuint default_mask,
|
||||||
|
bool high_precision);
|
||||||
void *gl_create_blur_context(backend_t *base, enum blur_method, void *args);
|
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);
|
||||||
struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius);
|
struct backend_shadow_context *gl_create_shadow_context(backend_t *base, double radius);
|
||||||
void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx);
|
void gl_destroy_shadow_context(backend_t *base attr_unused, struct backend_shadow_context *ctx);
|
||||||
void *gl_shadow_from_mask(backend_t *base, void *mask,
|
image_handle gl_shadow_from_mask(backend_t *base, image_handle mask,
|
||||||
struct backend_shadow_context *sctx, struct color color);
|
struct backend_shadow_context *sctx, struct color color);
|
||||||
void gl_get_blur_size(void *blur_context, int *width, int *height);
|
void gl_get_blur_size(void *blur_context, int *width, int *height);
|
||||||
|
|
||||||
void gl_fill(backend_t *base, struct color, const region_t *clip);
|
void gl_fill(backend_t *base, struct color, const region_t *clip);
|
||||||
|
|
||||||
void gl_present(backend_t *base, const region_t *);
|
void gl_present(backend_t *base, const region_t *);
|
||||||
bool gl_read_pixel(backend_t *base, void *image_data, int x, int y, struct color *output);
|
|
||||||
enum device_status gl_device_status(backend_t *base);
|
enum device_status gl_device_status(backend_t *base);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -264,26 +264,6 @@ static inline bool gl_check_fb_complete_(const char *func, int line, GLenum fb)
|
|||||||
|
|
||||||
#define gl_check_fb_complete(fb) gl_check_fb_complete_(__func__, __LINE__, (fb))
|
#define gl_check_fb_complete(fb) gl_check_fb_complete_(__func__, __LINE__, (fb))
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a GL extension exists.
|
|
||||||
*/
|
|
||||||
static inline bool gl_has_extension(const char *ext) {
|
|
||||||
int nexts = 0;
|
|
||||||
glGetIntegerv(GL_NUM_EXTENSIONS, &nexts);
|
|
||||||
for (int i = 0; i < nexts || !nexts; i++) {
|
|
||||||
const char *exti = (const char *)glGetStringi(GL_EXTENSIONS, (GLuint)i);
|
|
||||||
if (exti == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (strcmp(ext, exti) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gl_clear_err();
|
|
||||||
log_info("Missing GL extension %s.", ext);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const GLuint vert_coord_loc = 0;
|
static const GLuint vert_coord_loc = 0;
|
||||||
static const GLuint vert_in_texcoord_loc = 1;
|
static const GLuint vert_in_texcoord_loc = 1;
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,10 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <uthash.h>
|
||||||
#include <xcb/composite.h>
|
#include <xcb/composite.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/xcb_aux.h>
|
||||||
|
|
||||||
#include "backend/backend.h"
|
#include "backend/backend.h"
|
||||||
#include "backend/backend_common.h"
|
#include "backend/backend_common.h"
|
||||||
@@ -44,6 +46,13 @@ struct _glx_data {
|
|||||||
struct gl_data gl;
|
struct gl_data gl;
|
||||||
xcb_window_t target_win;
|
xcb_window_t target_win;
|
||||||
GLXContext ctx;
|
GLXContext ctx;
|
||||||
|
struct glx_fbconfig_cache *cached_fbconfigs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct glx_fbconfig_cache {
|
||||||
|
UT_hash_handle hh;
|
||||||
|
struct xvisual_info visual_info;
|
||||||
|
struct glx_fbconfig_info info;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define glXGetFBConfigAttribChecked(a, b, attr, c) \
|
#define glXGetFBConfigAttribChecked(a, b, attr, c) \
|
||||||
@@ -54,9 +63,12 @@ struct _glx_data {
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisual_info m) {
|
bool glx_find_fbconfig(struct x_connection *c, struct xvisual_info m,
|
||||||
log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size,
|
struct glx_fbconfig_info *info) {
|
||||||
m.blue_size, m.green_size, m.alpha_size, m.visual_depth);
|
log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth: %d, visual id: %#x", m.red_size,
|
||||||
|
m.blue_size, m.green_size, m.alpha_size, m.visual_depth, m.visual);
|
||||||
|
|
||||||
|
info->cfg = NULL;
|
||||||
|
|
||||||
int ncfg;
|
int ncfg;
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -114,7 +126,8 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
|
|||||||
int visual;
|
int visual;
|
||||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_VISUAL_ID, &visual);
|
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_VISUAL_ID, &visual);
|
||||||
if (m.visual_depth != -1 &&
|
if (m.visual_depth != -1 &&
|
||||||
x_get_visual_depth(c, (xcb_visualid_t)visual) != m.visual_depth) {
|
xcb_aux_get_depth_of_visual(c->screen_info, (xcb_visualid_t)visual) !=
|
||||||
|
m.visual_depth) {
|
||||||
// FBConfig and the correspondent X Visual might not have the same
|
// FBConfig and the correspondent X Visual might not have the same
|
||||||
// depth. (e.g. 32 bit FBConfig with a 24 bit Visual). This is
|
// depth. (e.g. 32 bit FBConfig with a 24 bit Visual). This is
|
||||||
// quite common, seen in both open source and proprietary drivers.
|
// quite common, seen in both open source and proprietary drivers.
|
||||||
@@ -143,16 +156,13 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
|
|||||||
min_cost = depthbuf + stencil + bufsize * (doublebuf + 1);
|
min_cost = depthbuf + stencil + bufsize * (doublebuf + 1);
|
||||||
}
|
}
|
||||||
free(cfg);
|
free(cfg);
|
||||||
if (!found) {
|
if (found) {
|
||||||
return NULL;
|
info->cfg = ret;
|
||||||
|
info->texture_tgts = texture_tgts;
|
||||||
|
info->texture_fmt = texture_fmt;
|
||||||
|
info->y_inverted = y_inverted;
|
||||||
}
|
}
|
||||||
|
return found;
|
||||||
auto info = cmalloc(struct glx_fbconfig_info);
|
|
||||||
info->cfg = ret;
|
|
||||||
info->texture_tgts = texture_tgts;
|
|
||||||
info->texture_fmt = texture_fmt;
|
|
||||||
info->y_inverted = y_inverted;
|
|
||||||
return info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -197,6 +207,12 @@ void glx_deinit(backend_t *base) {
|
|||||||
gd->ctx = 0;
|
gd->ctx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct glx_fbconfig_cache *cached_fbconfig = NULL, *tmp = NULL;
|
||||||
|
HASH_ITER(hh, gd->cached_fbconfigs, cached_fbconfig, tmp) {
|
||||||
|
HASH_DEL(gd->cached_fbconfigs, cached_fbconfig);
|
||||||
|
free(cached_fbconfig);
|
||||||
|
}
|
||||||
|
|
||||||
free(gd);
|
free(gd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,9 +380,10 @@ end:
|
|||||||
return &gd->gl.base;
|
return &gd->gl.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static image_handle
|
||||||
glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
|
glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
|
||||||
struct _glx_pixmap *glxpixmap = NULL;
|
struct _glx_pixmap *glxpixmap = NULL;
|
||||||
|
auto gd = (struct _glx_data *)base;
|
||||||
// Retrieve pixmap parameters, if they aren't provided
|
// Retrieve pixmap parameters, if they aren't provided
|
||||||
if (fmt.visual_depth > OPENGL_MAX_DEPTH) {
|
if (fmt.visual_depth > OPENGL_MAX_DEPTH) {
|
||||||
log_error("Requested depth %d higher than max possible depth %d.",
|
log_error("Requested depth %d higher than max possible depth %d.",
|
||||||
@@ -394,38 +411,52 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
|||||||
wd->inner = (struct backend_image_inner_base *)inner;
|
wd->inner = (struct backend_image_inner_base *)inner;
|
||||||
free(r);
|
free(r);
|
||||||
|
|
||||||
auto fbcfg = glx_find_fbconfig(base->c, fmt);
|
struct glx_fbconfig_cache *cached_fbconfig = NULL;
|
||||||
if (!fbcfg) {
|
HASH_FIND(hh, gd->cached_fbconfigs, &fmt, sizeof(fmt), cached_fbconfig);
|
||||||
log_error("Couldn't find FBConfig with requested visual %x", fmt.visual);
|
if (!cached_fbconfig) {
|
||||||
goto err;
|
struct glx_fbconfig_info fbconfig;
|
||||||
|
if (!glx_find_fbconfig(base->c, fmt, &fbconfig)) {
|
||||||
|
log_error("Couldn't find FBConfig with requested visual %#x",
|
||||||
|
fmt.visual);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
cached_fbconfig = cmalloc(struct glx_fbconfig_cache);
|
||||||
|
cached_fbconfig->visual_info = fmt;
|
||||||
|
cached_fbconfig->info = fbconfig;
|
||||||
|
HASH_ADD(hh, gd->cached_fbconfigs, visual_info, sizeof(fmt), cached_fbconfig);
|
||||||
|
} else {
|
||||||
|
log_debug("Found cached FBConfig for RGBA%d%d%d%d, depth: %d, visual id: "
|
||||||
|
"%#x",
|
||||||
|
fmt.red_size, fmt.blue_size, fmt.green_size, fmt.alpha_size,
|
||||||
|
fmt.visual_depth, fmt.visual);
|
||||||
}
|
}
|
||||||
|
struct glx_fbconfig_info *fbconfig = &cached_fbconfig->info;
|
||||||
|
|
||||||
// Choose a suitable texture target for our pixmap.
|
// Choose a suitable texture target for our pixmap.
|
||||||
// Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
|
// Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
|
||||||
// of the bits in texture_tgts
|
// of the bits in texture_tgts
|
||||||
if (!(fbcfg->texture_tgts & GLX_TEXTURE_2D_BIT_EXT)) {
|
if (!(fbconfig->texture_tgts & GLX_TEXTURE_2D_BIT_EXT)) {
|
||||||
log_error("Cannot bind pixmap to GL_TEXTURE_2D, giving up");
|
log_error("Cannot bind pixmap to GL_TEXTURE_2D, giving up");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("depth %d, rgba %d", fmt.visual_depth,
|
log_debug("depth %d, rgba %d", fmt.visual_depth,
|
||||||
(fbcfg->texture_fmt == GLX_TEXTURE_FORMAT_RGBA_EXT));
|
(fbconfig->texture_fmt == GLX_TEXTURE_FORMAT_RGBA_EXT));
|
||||||
|
|
||||||
GLint attrs[] = {
|
GLint attrs[] = {
|
||||||
GLX_TEXTURE_FORMAT_EXT,
|
GLX_TEXTURE_FORMAT_EXT,
|
||||||
fbcfg->texture_fmt,
|
fbconfig->texture_fmt,
|
||||||
GLX_TEXTURE_TARGET_EXT,
|
GLX_TEXTURE_TARGET_EXT,
|
||||||
GLX_TEXTURE_2D_EXT,
|
GLX_TEXTURE_2D_EXT,
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
|
|
||||||
inner->y_inverted = fbcfg->y_inverted;
|
inner->y_inverted = fbconfig->y_inverted;
|
||||||
|
|
||||||
glxpixmap = cmalloc(struct _glx_pixmap);
|
glxpixmap = cmalloc(struct _glx_pixmap);
|
||||||
glxpixmap->pixmap = pixmap;
|
glxpixmap->pixmap = pixmap;
|
||||||
glxpixmap->glpixmap = glXCreatePixmap(base->c->dpy, fbcfg->cfg, pixmap, attrs);
|
glxpixmap->glpixmap = glXCreatePixmap(base->c->dpy, fbconfig->cfg, pixmap, attrs);
|
||||||
glxpixmap->owned = owned;
|
glxpixmap->owned = owned;
|
||||||
free(fbcfg);
|
|
||||||
|
|
||||||
if (!glxpixmap->glpixmap) {
|
if (!glxpixmap->glpixmap) {
|
||||||
log_error("Failed to create glpixmap for pixmap %#010x", pixmap);
|
log_error("Failed to create glpixmap for pixmap %#010x", pixmap);
|
||||||
@@ -444,7 +475,7 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
|||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
gl_check_err();
|
gl_check_err();
|
||||||
return wd;
|
return (image_handle)wd;
|
||||||
err:
|
err:
|
||||||
if (glxpixmap && glxpixmap->glpixmap) {
|
if (glxpixmap && glxpixmap->glpixmap) {
|
||||||
glXDestroyPixmap(base->c->dpy, glxpixmap->glpixmap);
|
glXDestroyPixmap(base->c->dpy, glxpixmap->glpixmap);
|
||||||
@@ -545,62 +576,17 @@ struct backend_operations glx_ops = {
|
|||||||
.max_buffer_age = 5, // Why?
|
.max_buffer_age = 5, // Why?
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a GLX extension exists.
|
|
||||||
*/
|
|
||||||
static inline bool glx_has_extension(Display *dpy, int screen, const char *ext) {
|
|
||||||
const char *glx_exts = glXQueryExtensionsString(dpy, screen);
|
|
||||||
if (!glx_exts) {
|
|
||||||
log_error("Failed get GLX extension list.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto inlen = strlen(ext);
|
|
||||||
const char *curr = glx_exts;
|
|
||||||
bool match = false;
|
|
||||||
while (curr && !match) {
|
|
||||||
const char *end = strchr(curr, ' ');
|
|
||||||
if (!end) {
|
|
||||||
// Last extension string
|
|
||||||
match = strcmp(ext, curr) == 0;
|
|
||||||
} else if (curr + inlen == end) {
|
|
||||||
// Length match, do match string
|
|
||||||
match = strncmp(ext, curr, (unsigned long)(end - curr)) == 0;
|
|
||||||
}
|
|
||||||
curr = end ? end + 1 : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match) {
|
|
||||||
log_info("Missing GLX extension %s.", ext);
|
|
||||||
} else {
|
|
||||||
log_info("Found GLX extension %s.", ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct glxext_info glxext = {0};
|
struct glxext_info glxext = {0};
|
||||||
PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
|
|
||||||
PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
|
|
||||||
PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
|
|
||||||
PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
|
|
||||||
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
|
|
||||||
PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
|
|
||||||
PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
|
|
||||||
PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
|
|
||||||
PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
|
|
||||||
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
|
|
||||||
|
|
||||||
#ifdef GLX_MESA_query_renderer
|
|
||||||
PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void glxext_init(Display *dpy, int screen) {
|
void glxext_init(Display *dpy, int screen) {
|
||||||
if (glxext.initialized) {
|
if (glxext.initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
glxext.initialized = true;
|
glxext.initialized = true;
|
||||||
#define check_ext(name) glxext.has_##name = glx_has_extension(dpy, screen, #name)
|
#define check_ext(name) \
|
||||||
|
glxext.has_##name = epoxy_has_glx_extension(dpy, screen, #name); \
|
||||||
|
log_info("Extension " #name " - %s", glxext.has_##name ? "present" : "absent")
|
||||||
|
|
||||||
check_ext(GLX_SGI_video_sync);
|
check_ext(GLX_SGI_video_sync);
|
||||||
check_ext(GLX_SGI_swap_control);
|
check_ext(GLX_SGI_swap_control);
|
||||||
check_ext(GLX_OML_sync_control);
|
check_ext(GLX_OML_sync_control);
|
||||||
@@ -614,36 +600,4 @@ void glxext_init(Display *dpy, int screen) {
|
|||||||
check_ext(GLX_MESA_query_renderer);
|
check_ext(GLX_MESA_query_renderer);
|
||||||
#endif
|
#endif
|
||||||
#undef check_ext
|
#undef check_ext
|
||||||
|
|
||||||
#define lookup(name) ((name) = (__typeof__(name))glXGetProcAddress((GLubyte *)#name))
|
|
||||||
// Checking if the returned function pointer is NULL is not really necessary,
|
|
||||||
// or maybe not even useful, since glXGetProcAddress might always return
|
|
||||||
// something. We are doing it just for completeness' sake.
|
|
||||||
if (!lookup(glXGetVideoSyncSGI) || !lookup(glXWaitVideoSyncSGI)) {
|
|
||||||
glxext.has_GLX_SGI_video_sync = false;
|
|
||||||
}
|
|
||||||
if (!lookup(glXSwapIntervalEXT)) {
|
|
||||||
glxext.has_GLX_EXT_swap_control = false;
|
|
||||||
}
|
|
||||||
if (!lookup(glXSwapIntervalMESA)) {
|
|
||||||
glxext.has_GLX_MESA_swap_control = false;
|
|
||||||
}
|
|
||||||
if (!lookup(glXSwapIntervalSGI)) {
|
|
||||||
glxext.has_GLX_SGI_swap_control = false;
|
|
||||||
}
|
|
||||||
if (!lookup(glXWaitForMscOML) || !lookup(glXGetSyncValuesOML)) {
|
|
||||||
glxext.has_GLX_OML_sync_control = false;
|
|
||||||
}
|
|
||||||
if (!lookup(glXBindTexImageEXT) || !lookup(glXReleaseTexImageEXT)) {
|
|
||||||
glxext.has_GLX_EXT_texture_from_pixmap = false;
|
|
||||||
}
|
|
||||||
if (!lookup(glXCreateContextAttribsARB)) {
|
|
||||||
glxext.has_GLX_ARB_create_context = false;
|
|
||||||
}
|
|
||||||
#ifdef GLX_MESA_query_renderer
|
|
||||||
if (!lookup(glXQueryCurrentRendererIntegerMESA)) {
|
|
||||||
glxext.has_GLX_MESA_query_renderer = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#undef lookup
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stdbool.h>
|
|
||||||
// Older version of glx.h defines function prototypes for these extensions...
|
|
||||||
// Rename them to avoid conflicts
|
|
||||||
#define glXSwapIntervalMESA glXSwapIntervalMESA_
|
|
||||||
#define glXBindTexImageEXT glXBindTexImageEXT_
|
|
||||||
#define glXReleaseTexImageEXT glXReleaseTexImageEXT
|
|
||||||
#include <GL/glx.h>
|
|
||||||
#undef glXSwapIntervalMESA
|
|
||||||
#undef glXBindTexImageEXT
|
|
||||||
#undef glXReleaseTexImageEXT
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
#include <epoxy/glx.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <xcb/render.h>
|
#include <xcb/render.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
@@ -27,21 +19,8 @@ struct glx_fbconfig_info {
|
|||||||
int y_inverted;
|
int y_inverted;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The search criteria for glx_find_fbconfig
|
bool glx_find_fbconfig(struct x_connection *c, struct xvisual_info m,
|
||||||
struct glx_fbconfig_criteria {
|
struct glx_fbconfig_info *info);
|
||||||
/// Bit width of the red component
|
|
||||||
int red_size;
|
|
||||||
/// Bit width of the green component
|
|
||||||
int green_size;
|
|
||||||
/// Bit width of the blue component
|
|
||||||
int blue_size;
|
|
||||||
/// Bit width of the alpha component
|
|
||||||
int alpha_size;
|
|
||||||
/// The depth of X visual
|
|
||||||
int visual_depth;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *, struct xvisual_info);
|
|
||||||
|
|
||||||
struct glxext_info {
|
struct glxext_info {
|
||||||
bool initialized;
|
bool initialized;
|
||||||
@@ -59,19 +38,4 @@ struct glxext_info {
|
|||||||
|
|
||||||
extern struct glxext_info glxext;
|
extern struct glxext_info glxext;
|
||||||
|
|
||||||
extern PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
|
|
||||||
extern PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
|
|
||||||
extern PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
|
|
||||||
extern PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
|
|
||||||
extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
|
|
||||||
extern PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
|
|
||||||
extern PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
|
|
||||||
extern PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
|
|
||||||
extern PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
|
|
||||||
extern PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
|
|
||||||
|
|
||||||
#ifdef GLX_MESA_query_renderer
|
|
||||||
extern PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glXQueryCurrentRendererIntegerMESA;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void glxext_init(Display *, int screen);
|
void glxext_init(Display *, int screen);
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ const char win_shader_glsl[] = GLSL(330,
|
|||||||
uniform bool invert_color;
|
uniform bool invert_color;
|
||||||
in vec2 texcoord;
|
in vec2 texcoord;
|
||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
|
uniform vec2 effective_size;
|
||||||
uniform sampler2D brightness;
|
uniform sampler2D brightness;
|
||||||
uniform float max_brightness;
|
uniform float max_brightness;
|
||||||
// Signed distance field for rectangle center at (0, 0), with size of
|
// Signed distance field for rectangle center at (0, 0), with size of
|
||||||
@@ -130,7 +131,7 @@ const char win_shader_glsl[] = GLSL(330,
|
|||||||
// Using mix() to avoid a branch here.
|
// Using mix() to avoid a branch here.
|
||||||
vec4 rim_color = mix(c, border_color, clamp(border_width, 0.0f, 1.0f));
|
vec4 rim_color = mix(c, border_color, clamp(border_width, 0.0f, 1.0f));
|
||||||
|
|
||||||
vec2 outer_size = vec2(textureSize(tex, 0));
|
vec2 outer_size = effective_size;
|
||||||
vec2 inner_size = outer_size - vec2(corner_radius) * 2.0f;
|
vec2 inner_size = outer_size - vec2(corner_radius) * 2.0f;
|
||||||
float rect_distance = rectangle_sdf(texcoord - outer_size / 2.0f,
|
float rect_distance = rectangle_sdf(texcoord - outer_size / 2.0f,
|
||||||
inner_size / 2.0f) - corner_radius;
|
inner_size / 2.0f) - corner_radius;
|
||||||
@@ -157,7 +158,8 @@ const char win_shader_default[] = GLSL(330,
|
|||||||
uniform sampler2D tex;
|
uniform sampler2D tex;
|
||||||
vec4 default_post_processing(vec4 c);
|
vec4 default_post_processing(vec4 c);
|
||||||
vec4 window_shader() {
|
vec4 window_shader() {
|
||||||
vec4 c = texelFetch(tex, ivec2(texcoord), 0);
|
vec2 texsize = textureSize(tex, 0);
|
||||||
|
vec4 c = texture2D(tex, texcoord / texsize, 0);
|
||||||
return default_post_processing(c);
|
return default_post_processing(c);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "backend/backend.h"
|
#include "backend/backend.h"
|
||||||
#include "backend/backend_common.h"
|
#include "backend/backend_common.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "compiler.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
@@ -56,6 +57,10 @@ typedef struct _xrender_data {
|
|||||||
int target_width, target_height;
|
int target_width, target_height;
|
||||||
|
|
||||||
xcb_special_event_t *present_event;
|
xcb_special_event_t *present_event;
|
||||||
|
|
||||||
|
/// Cache an X region to avoid creating and destroying it every frame. A
|
||||||
|
/// workaround for yshui/picom#1166.
|
||||||
|
xcb_xfixes_region_t present_region;
|
||||||
} xrender_data;
|
} xrender_data;
|
||||||
|
|
||||||
struct _xrender_blur_context {
|
struct _xrender_blur_context {
|
||||||
@@ -355,10 +360,12 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
|
|||||||
pixman_region32_fini(®);
|
pixman_region32_fini(®);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void compose(backend_t *base, void *img_data, coord_t dst, void *mask, coord_t mask_dst,
|
static void compose(backend_t *base, image_handle image_, coord_t dst, image_handle mask_,
|
||||||
const region_t *reg_paint, const region_t *reg_visible, bool lerp attr_unused) {
|
coord_t mask_dst, const region_t *reg_paint, const region_t *reg_visible, attr_unused bool lerp) {
|
||||||
struct _xrender_data *xd = (void *)base;
|
struct _xrender_data *xd = (void *)base;
|
||||||
return compose_impl(xd, img_data, dst, mask, mask_dst, reg_paint, reg_visible,
|
auto image = (struct xrender_image *)image_;
|
||||||
|
auto mask = (struct xrender_image *)mask_;
|
||||||
|
return compose_impl(xd, image, dst, mask, mask_dst, reg_paint, reg_visible,
|
||||||
xd->back[2]);
|
xd->back[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,9 +387,10 @@ static void fill(backend_t *base, struct color c, const region_t *clip) {
|
|||||||
.height = to_u16_checked(extent->y2 - extent->y1)}});
|
.height = to_u16_checked(extent->y2 - extent->y1)}});
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask,
|
static bool blur(backend_t *backend_data, double opacity, void *ctx_, image_handle mask_,
|
||||||
coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible) {
|
coord_t mask_dst, const region_t *reg_blur, const region_t *reg_visible) {
|
||||||
struct _xrender_blur_context *bctx = ctx_;
|
auto bctx = (struct _xrender_blur_context *)ctx_;
|
||||||
|
auto mask = (struct xrender_image *)mask_;
|
||||||
if (bctx->method == BLUR_METHOD_NONE) {
|
if (bctx->method == BLUR_METHOD_NONE) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -513,7 +521,7 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static image_handle
|
||||||
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;
|
||||||
auto r = xcb_get_geometry_reply(base->c->c, xcb_get_geometry(base->c->c, pixmap), &e);
|
auto r = xcb_get_geometry_reply(base->c->c, xcb_get_geometry(base->c->c, pixmap), &e);
|
||||||
@@ -548,7 +556,7 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool
|
|||||||
free(img);
|
free(img);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return img;
|
return (image_handle)img;
|
||||||
}
|
}
|
||||||
static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) {
|
static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) {
|
||||||
x_free_picture(base->c, inner->pict);
|
x_free_picture(base->c, inner->pict);
|
||||||
@@ -572,13 +580,13 @@ release_rounded_corner_cache(backend_t *base, struct xrender_rounded_rectangle_c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void release_image(backend_t *base, void *image) {
|
static void release_image(backend_t *base, image_handle image) {
|
||||||
struct xrender_image *img = image;
|
auto img = (struct xrender_image *)image;
|
||||||
release_rounded_corner_cache(base, img->rounded_rectangle);
|
release_rounded_corner_cache(base, img->rounded_rectangle);
|
||||||
img->rounded_rectangle = NULL;
|
img->rounded_rectangle = NULL;
|
||||||
img->base.inner->refcount -= 1;
|
img->base.inner->refcount -= 1;
|
||||||
if (img->base.inner->refcount == 0) {
|
if (img->base.inner->refcount == 0) {
|
||||||
release_image_inner(base, (void *)img->base.inner);
|
release_image_inner(base, (struct _xrender_image_data_inner *)img->base.inner);
|
||||||
}
|
}
|
||||||
free(img);
|
free(img);
|
||||||
}
|
}
|
||||||
@@ -597,6 +605,7 @@ static void deinit(backend_t *backend_data) {
|
|||||||
xcb_free_pixmap(xd->base.c->c, xd->back_pixmap[i]);
|
xcb_free_pixmap(xd->base.c->c, xd->back_pixmap[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
x_destroy_region(xd->base.c, xd->present_region);
|
||||||
if (xd->present_event) {
|
if (xd->present_event) {
|
||||||
xcb_unregister_for_special_event(xd->base.c->c, xd->present_event);
|
xcb_unregister_for_special_event(xd->base.c->c, xd->present_event);
|
||||||
}
|
}
|
||||||
@@ -624,16 +633,15 @@ static void present(backend_t *base, const region_t *region) {
|
|||||||
XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0,
|
XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0,
|
||||||
0, orig_x, orig_y, region_width, region_height);
|
0, orig_x, orig_y, region_width, region_height);
|
||||||
|
|
||||||
auto xregion = x_create_region(base->c, region);
|
|
||||||
|
|
||||||
// Make sure we got reply from PresentPixmap before waiting for events,
|
// Make sure we got reply from PresentPixmap before waiting for events,
|
||||||
// to avoid deadlock
|
// to avoid deadlock
|
||||||
auto e = xcb_request_check(
|
auto e = xcb_request_check(
|
||||||
base->c->c, xcb_present_pixmap_checked(
|
base->c->c,
|
||||||
xd->base.c->c, xd->target_win,
|
xcb_present_pixmap_checked(
|
||||||
xd->back_pixmap[xd->curr_back], 0, XCB_NONE, xregion, 0,
|
base->c->c, xd->target_win, xd->back_pixmap[xd->curr_back], 0, XCB_NONE,
|
||||||
0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
|
x_set_region(base->c, xd->present_region, region) ? xd->present_region
|
||||||
x_destroy_region(base->c, xregion);
|
: XCB_NONE,
|
||||||
|
0, 0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
|
||||||
if (e) {
|
if (e) {
|
||||||
log_error("Failed to present pixmap");
|
log_error("Failed to present pixmap");
|
||||||
free(e);
|
free(e);
|
||||||
@@ -707,7 +715,7 @@ new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
|
|||||||
return new_inner;
|
return new_inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
static image_handle make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
||||||
struct _xrender_data *xd = (void *)base;
|
struct _xrender_data *xd = (void *)base;
|
||||||
// Give the mask a 1 pixel wide border to emulate the clamp to border behavior of
|
// Give the mask a 1 pixel wide border to emulate the clamp to border behavior of
|
||||||
// OpenGL textures.
|
// OpenGL textures.
|
||||||
@@ -750,7 +758,7 @@ static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
|||||||
img->base.dim = 0;
|
img->base.dim = 0;
|
||||||
img->base.inner = (struct backend_image_inner_base *)inner;
|
img->base.inner = (struct backend_image_inner_base *)inner;
|
||||||
img->rounded_rectangle = NULL;
|
img->rounded_rectangle = NULL;
|
||||||
return img;
|
return (image_handle)img;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) {
|
static bool decouple_image(backend_t *base, struct backend_image *img, const region_t *reg) {
|
||||||
@@ -778,10 +786,10 @@ static bool decouple_image(backend_t *base, struct backend_image *img, const reg
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool image_op(backend_t *base, enum image_operations op, void *image,
|
static bool image_op(backend_t *base, enum image_operations op, image_handle image,
|
||||||
const region_t *reg_op, const region_t *reg_visible, void *arg) {
|
const region_t *reg_op, const region_t *reg_visible, void *arg) {
|
||||||
struct _xrender_data *xd = (void *)base;
|
struct _xrender_data *xd = (void *)base;
|
||||||
struct backend_image *img = image;
|
auto img = (struct backend_image *)image;
|
||||||
region_t reg;
|
region_t reg;
|
||||||
double *dargs = arg;
|
double *dargs = arg;
|
||||||
|
|
||||||
@@ -929,6 +937,10 @@ static backend_t *backend_xrender_init(session_t *ps, xcb_window_t target) {
|
|||||||
xd->vsync = false;
|
xd->vsync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (xd->vsync) {
|
||||||
|
xd->present_region = x_create_region(&ps->c, &ps->screen_reg);
|
||||||
|
}
|
||||||
|
|
||||||
// We might need to do double buffering for vsync, and buffer 0 and 1 are for
|
// We might need to do double buffering for vsync, and buffer 0 and 1 are for
|
||||||
// double buffering.
|
// double buffering.
|
||||||
int first_buffer_index = xd->vsync ? 0 : 2;
|
int first_buffer_index = xd->vsync ? 0 : 2;
|
||||||
@@ -956,19 +968,19 @@ err:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *clone_image(backend_t *base attr_unused, const void *image_data,
|
image_handle clone_image(backend_t *base attr_unused, image_handle image,
|
||||||
const region_t *reg_visible attr_unused) {
|
const region_t *reg_visible attr_unused) {
|
||||||
auto new_img = ccalloc(1, struct xrender_image);
|
auto new_img = ccalloc(1, struct xrender_image);
|
||||||
*new_img = *(struct xrender_image *)image_data;
|
*new_img = *(struct xrender_image *)image;
|
||||||
new_img->base.inner->refcount++;
|
new_img->base.inner->refcount++;
|
||||||
if (new_img->rounded_rectangle) {
|
if (new_img->rounded_rectangle) {
|
||||||
new_img->rounded_rectangle->refcount++;
|
new_img->rounded_rectangle->refcount++;
|
||||||
}
|
}
|
||||||
return new_img;
|
return (image_handle)new_img;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool set_image_property(backend_t *base, enum image_properties op,
|
||||||
set_image_property(backend_t *base, enum image_properties op, void *image, void *args) {
|
image_handle image, void *args) {
|
||||||
auto xrimg = (struct xrender_image *)image;
|
auto xrimg = (struct xrender_image *)image;
|
||||||
if (op == IMAGE_PROPERTY_CORNER_RADIUS &&
|
if (op == IMAGE_PROPERTY_CORNER_RADIUS &&
|
||||||
((double *)args)[0] != xrimg->base.corner_radius) {
|
((double *)args)[0] != xrimg->base.corner_radius) {
|
||||||
|
|||||||
13
src/c2.c
13
src/c2.c
@@ -1047,7 +1047,7 @@ static bool c2_l_postprocess(session_t *ps, c2_l_t *pleaf) {
|
|||||||
|
|
||||||
// Get target atom if it's not a predefined one
|
// Get target atom if it's not a predefined one
|
||||||
if (pleaf->predef == C2_L_PUNDEFINED) {
|
if (pleaf->predef == C2_L_PUNDEFINED) {
|
||||||
pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt);
|
pleaf->tgtatom = get_atom(ps->atoms, pleaf->tgt, ps->c.c);
|
||||||
if (!pleaf->tgtatom) {
|
if (!pleaf->tgtatom) {
|
||||||
log_error("Failed to get atom for target \"%s\".", pleaf->tgt);
|
log_error("Failed to get atom for target \"%s\".", pleaf->tgt);
|
||||||
return false;
|
return false;
|
||||||
@@ -1380,14 +1380,10 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
|
|||||||
case C2_L_PWIDTHB: predef_target = w->widthb; break;
|
case C2_L_PWIDTHB: predef_target = w->widthb; break;
|
||||||
case C2_L_PHEIGHTB: predef_target = w->heightb; break;
|
case C2_L_PHEIGHTB: predef_target = w->heightb; break;
|
||||||
case C2_L_PBDW: predef_target = w->g.border_width; break;
|
case C2_L_PBDW: predef_target = w->g.border_width; break;
|
||||||
case C2_L_PFULLSCREEN:
|
case C2_L_PFULLSCREEN: predef_target = w->is_fullscreen; break;
|
||||||
predef_target = win_is_fullscreen(ps, w);
|
|
||||||
break;
|
|
||||||
case C2_L_POVREDIR: predef_target = w->a.override_redirect; break;
|
case C2_L_POVREDIR: predef_target = w->a.override_redirect; break;
|
||||||
case C2_L_PARGB: predef_target = win_has_alpha(w); break;
|
case C2_L_PARGB: predef_target = win_has_alpha(w); break;
|
||||||
case C2_L_PFOCUSED:
|
case C2_L_PFOCUSED: predef_target = win_is_focused_raw(w); break;
|
||||||
predef_target = win_is_focused_raw(ps, w);
|
|
||||||
break;
|
|
||||||
case C2_L_PWMWIN: predef_target = w->wmwin; break;
|
case C2_L_PWMWIN: predef_target = w->wmwin; break;
|
||||||
case C2_L_PBSHAPED: predef_target = w->bounding_shaped; break;
|
case C2_L_PBSHAPED: predef_target = w->bounding_shaped; break;
|
||||||
case C2_L_PROUNDED: predef_target = w->rounded_corners; break;
|
case C2_L_PROUNDED: predef_target = w->rounded_corners; break;
|
||||||
@@ -1513,7 +1509,8 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
|
|||||||
else {
|
else {
|
||||||
char **strlst = NULL;
|
char **strlst = NULL;
|
||||||
int nstr = 0;
|
int nstr = 0;
|
||||||
if (wid_get_text_prop(ps, wid, pleaf->tgtatom, &strlst, &nstr)) {
|
if (wid_get_text_prop(&ps->c, ps->atoms, wid, pleaf->tgtatom,
|
||||||
|
&strlst, &nstr)) {
|
||||||
if (pleaf->index < 0 && nstr > 0 && strlen(strlst[0]) > 0) {
|
if (pleaf->index < 0 && nstr > 0 && strlen(strlst[0]) > 0) {
|
||||||
ntargets = to_u32_checked(nstr);
|
ntargets = to_u32_checked(nstr);
|
||||||
targets = (const char **)strlst;
|
targets = (const char **)strlst;
|
||||||
|
|||||||
96
src/cache.c
96
src/cache.c
@@ -1,95 +1,43 @@
|
|||||||
#include <uthash.h>
|
#include <uthash.h>
|
||||||
|
|
||||||
#include "compiler.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
|
||||||
struct cache_entry {
|
struct cache_handle *cache_get(struct cache *c, const char *key) {
|
||||||
char *key;
|
struct cache_handle *e;
|
||||||
void *value;
|
|
||||||
UT_hash_handle hh;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cache {
|
|
||||||
cache_getter_t getter;
|
|
||||||
cache_free_t free;
|
|
||||||
void *user_data;
|
|
||||||
struct cache_entry *entries;
|
|
||||||
};
|
|
||||||
|
|
||||||
void cache_set(struct cache *c, const char *key, void *data) {
|
|
||||||
struct cache_entry *e = NULL;
|
|
||||||
HASH_FIND_STR(c->entries, key, e);
|
HASH_FIND_STR(c->entries, key, e);
|
||||||
CHECK(!e);
|
return e;
|
||||||
|
|
||||||
e = ccalloc(1, struct cache_entry);
|
|
||||||
e->key = strdup(key);
|
|
||||||
e->value = data;
|
|
||||||
HASH_ADD_STR(c->entries, key, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *cache_get(struct cache *c, const char *key, int *err) {
|
int cache_get_or_fetch(struct cache *c, const char *key, struct cache_handle **value,
|
||||||
struct cache_entry *e;
|
void *user_data, cache_getter_t getter) {
|
||||||
HASH_FIND_STR(c->entries, key, e);
|
*value = cache_get(c, key);
|
||||||
if (e) {
|
if (*value) {
|
||||||
return e->value;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tmperr;
|
int err = getter(c, key, value, user_data);
|
||||||
if (!err) {
|
assert(err <= 0);
|
||||||
err = &tmperr;
|
if (err < 0) {
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
(*value)->key = strdup(key);
|
||||||
|
|
||||||
*err = 0;
|
HASH_ADD_STR(c->entries, key, *value);
|
||||||
e = ccalloc(1, struct cache_entry);
|
return 1;
|
||||||
e->key = strdup(key);
|
|
||||||
e->value = c->getter(c->user_data, key, err);
|
|
||||||
if (*err) {
|
|
||||||
free(e->key);
|
|
||||||
free(e);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
HASH_ADD_STR(c->entries, key, e);
|
|
||||||
return e->value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _cache_invalidate(struct cache *c, struct cache_entry *e) {
|
static inline void
|
||||||
if (c->free) {
|
cache_invalidate_impl(struct cache *c, struct cache_handle *e, cache_free_t free_fn) {
|
||||||
c->free(c->user_data, e->value);
|
|
||||||
}
|
|
||||||
free(e->key);
|
free(e->key);
|
||||||
HASH_DEL(c->entries, e);
|
HASH_DEL(c->entries, e);
|
||||||
free(e);
|
if (free_fn) {
|
||||||
}
|
free_fn(c, e);
|
||||||
|
|
||||||
void cache_invalidate(struct cache *c, const char *key) {
|
|
||||||
struct cache_entry *e;
|
|
||||||
HASH_FIND_STR(c->entries, key, e);
|
|
||||||
|
|
||||||
if (e) {
|
|
||||||
_cache_invalidate(c, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cache_invalidate_all(struct cache *c) {
|
void cache_invalidate_all(struct cache *c, cache_free_t free_fn) {
|
||||||
struct cache_entry *e, *tmpe;
|
struct cache_handle *e, *tmpe;
|
||||||
HASH_ITER(hh, c->entries, e, tmpe) {
|
HASH_ITER(hh, c->entries, e, tmpe) {
|
||||||
_cache_invalidate(c, e);
|
cache_invalidate_impl(c, e, free_fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *cache_free(struct cache *c) {
|
|
||||||
void *ret = c->user_data;
|
|
||||||
cache_invalidate_all(c);
|
|
||||||
free(c);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cache *new_cache(void *ud, cache_getter_t getter, cache_free_t f) {
|
|
||||||
auto c = ccalloc(1, struct cache);
|
|
||||||
c->user_data = ud;
|
|
||||||
c->getter = getter;
|
|
||||||
c->free = f;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|||||||
57
src/cache.h
57
src/cache.h
@@ -1,32 +1,43 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <uthash.h>
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#define cache_entry(ptr, type, member) container_of(ptr, type, member)
|
||||||
|
|
||||||
struct cache;
|
struct cache;
|
||||||
|
struct cache_handle;
|
||||||
|
|
||||||
typedef void *(*cache_getter_t)(void *user_data, const char *key, int *err);
|
/// User-provided function to fetch a value for the cache, when it's not present.
|
||||||
typedef void (*cache_free_t)(void *user_data, void *data);
|
/// Should return 0 if the value is fetched successfully, and a negative number if the
|
||||||
|
/// value cannot be fetched. Getter doesn't need to initialize fields of `struct
|
||||||
|
/// cache_handle`.
|
||||||
|
typedef int (*cache_getter_t)(struct cache *, const char *key,
|
||||||
|
struct cache_handle **value, void *user_data);
|
||||||
|
typedef void (*cache_free_t)(struct cache *, struct cache_handle *value);
|
||||||
|
|
||||||
/// Create a cache with `getter`, and a free function `f` which is used to free the cache
|
struct cache {
|
||||||
/// value when they are invalidated.
|
struct cache_handle *entries;
|
||||||
///
|
};
|
||||||
/// `user_data` will be passed to `getter` and `f` when they are called.
|
|
||||||
struct cache *new_cache(void *user_data, cache_getter_t getter, cache_free_t f);
|
|
||||||
|
|
||||||
/// Fetch a value from the cache. If the value doesn't present in the cache yet, the
|
static const struct cache CACHE_INIT = {NULL};
|
||||||
|
|
||||||
|
struct cache_handle {
|
||||||
|
char *key;
|
||||||
|
UT_hash_handle hh;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get a value from the cache. If the value doesn't present in the cache yet, the
|
||||||
/// getter will be called, and the returned value will be stored into the cache.
|
/// getter will be called, and the returned value will be stored into the cache.
|
||||||
void *cache_get(struct cache *, const char *key, int *err);
|
/// Returns 0 if the value is already present in the cache, 1 if the value is fetched
|
||||||
|
/// successfully, and a negative number if the value cannot be fetched.
|
||||||
|
int cache_get_or_fetch(struct cache *, const char *key, struct cache_handle **value,
|
||||||
|
void *user_data, cache_getter_t getter);
|
||||||
|
|
||||||
/// Invalidate a value in the cache.
|
/// Get a value from the cache. If the value doesn't present in the cache, NULL will be
|
||||||
void cache_invalidate(struct cache *, const char *key);
|
/// returned.
|
||||||
|
struct cache_handle *cache_get(struct cache *, const char *key);
|
||||||
|
|
||||||
/// Invalidate all values in the cache.
|
/// Invalidate all values in the cache. After this call, `struct cache` holds no allocated
|
||||||
void cache_invalidate_all(struct cache *);
|
/// memory, and can be discarded.
|
||||||
|
void cache_invalidate_all(struct cache *, cache_free_t free_fn);
|
||||||
/// Invalidate all values in the cache and free it. Returns the user data passed to
|
|
||||||
/// `new_cache`
|
|
||||||
void *cache_free(struct cache *);
|
|
||||||
|
|
||||||
/// Insert a key-value pair into the cache. Only used for internal testing. Takes
|
|
||||||
/// ownership of `data`
|
|
||||||
///
|
|
||||||
/// If `key` already exists in the cache, this function will abort the program.
|
|
||||||
void cache_set(struct cache *c, const char *key, void *data);
|
|
||||||
|
|||||||
10
src/common.h
10
src/common.h
@@ -190,7 +190,7 @@ typedef struct session {
|
|||||||
/// Picture of the root window background.
|
/// Picture of the root window background.
|
||||||
paint_t root_tile_paint;
|
paint_t root_tile_paint;
|
||||||
/// The backend data the root pixmap bound to
|
/// The backend data the root pixmap bound to
|
||||||
void *root_image;
|
image_handle root_image;
|
||||||
/// A region of the size of the screen.
|
/// A region of the size of the screen.
|
||||||
region_t screen_reg;
|
region_t screen_reg;
|
||||||
/// Picture of root window. Destination of painting in no-DBE painting
|
/// Picture of root window. Destination of painting in no-DBE painting
|
||||||
@@ -208,7 +208,7 @@ 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;
|
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;
|
||||||
@@ -244,8 +244,8 @@ typedef struct session {
|
|||||||
/// Either the backend is currently rendering a frame, or a frame has been
|
/// Either the backend is currently rendering a frame, or a frame has been
|
||||||
/// rendered but has yet to be presented. In either case, we should not start
|
/// rendered but has yet to be presented. In either case, we should not start
|
||||||
/// another render right now. As if we start issuing rendering commands now, we
|
/// another render right now. As if we start issuing rendering commands now, we
|
||||||
/// will have to wait for either the the current render to finish, or the current
|
/// will have to wait for either the current render to finish, or the current
|
||||||
/// back buffer to be become available again. In either case, we will be wasting
|
/// back buffer to become available again. In either case, we will be wasting
|
||||||
/// time.
|
/// time.
|
||||||
bool backend_busy;
|
bool backend_busy;
|
||||||
/// Whether a render is queued. This generally means there are pending updates
|
/// Whether a render is queued. This generally means there are pending updates
|
||||||
@@ -279,7 +279,7 @@ typedef struct session {
|
|||||||
struct x_convolution_kernel **blur_kerns_cache;
|
struct x_convolution_kernel **blur_kerns_cache;
|
||||||
/// If we should quit
|
/// If we should quit
|
||||||
bool quit : 1;
|
bool quit : 1;
|
||||||
// TODO(yshui) use separate flags for dfferent kinds of updates so we don't
|
// TODO(yshui) use separate flags for different kinds of updates so we don't
|
||||||
// waste our time.
|
// waste our time.
|
||||||
/// Whether there are pending updates, like window creation, etc.
|
/// Whether there are pending updates, like window creation, etc.
|
||||||
bool pending_updates : 1;
|
bool pending_updates : 1;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
#else
|
#else
|
||||||
# define attr_warn_unused_result
|
# define attr_warn_unused_result
|
||||||
#endif
|
#endif
|
||||||
// An alias for conveninence
|
// An alias for convenience
|
||||||
#define must_use attr_warn_unused_result
|
#define must_use attr_warn_unused_result
|
||||||
|
|
||||||
#if __has_attribute(nonnull)
|
#if __has_attribute(nonnull)
|
||||||
@@ -105,9 +105,9 @@
|
|||||||
|
|
||||||
#ifndef unreachable
|
#ifndef unreachable
|
||||||
# if defined(__GNUC__) || defined(__clang__)
|
# if defined(__GNUC__) || defined(__clang__)
|
||||||
# define unreachable() __builtin_unreachable()
|
# define unreachable() assert(false); __builtin_unreachable()
|
||||||
# else
|
# else
|
||||||
# define unreachable() do {} while(0)
|
# define unreachable() assert(false); do {} while(0)
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
19
src/config.c
19
src/config.c
@@ -43,10 +43,7 @@ const char *xdg_config_home(void) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
xdgh = cvalloc(strlen(home) + strlen(default_dir) + 1);
|
xdgh = mstrjoin(home, default_dir);
|
||||||
|
|
||||||
strcpy(xdgh, home);
|
|
||||||
strcat(xdgh, default_dir);
|
|
||||||
} else {
|
} else {
|
||||||
xdgh = strdup(xdgh);
|
xdgh = strdup(xdgh);
|
||||||
}
|
}
|
||||||
@@ -563,7 +560,7 @@ static char *locate_auxiliary_file_at(const char *base, const char *scope, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a path of an auxiliary file to read, could be a shader file, or any supplimenrary
|
* Get a path of an auxiliary file to read, could be a shader file, or any supplementary
|
||||||
* file.
|
* file.
|
||||||
*
|
*
|
||||||
* Follows the XDG specification to search for the shader file in configuration locations.
|
* Follows the XDG specification to search for the shader file in configuration locations.
|
||||||
@@ -598,15 +595,17 @@ char *locate_auxiliary_file(const char *scope, const char *path, const char *inc
|
|||||||
// Fall back to searching in user config directory
|
// Fall back to searching in user config directory
|
||||||
scoped_charp picom_scope = mstrjoin("/picom/", scope);
|
scoped_charp picom_scope = mstrjoin("/picom/", scope);
|
||||||
scoped_charp config_home = (char *)xdg_config_home();
|
scoped_charp config_home = (char *)xdg_config_home();
|
||||||
char *ret = locate_auxiliary_file_at(config_home, picom_scope, path);
|
if (config_home) {
|
||||||
if (ret) {
|
char *ret = locate_auxiliary_file_at(config_home, picom_scope, path);
|
||||||
return ret;
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to searching in system config directory
|
// Fall back to searching in system config directory
|
||||||
auto config_dirs = xdg_config_dirs();
|
auto config_dirs = xdg_config_dirs();
|
||||||
for (int i = 0; config_dirs[i]; i++) {
|
for (int i = 0; config_dirs[i]; i++) {
|
||||||
ret = locate_auxiliary_file_at(config_dirs[i], picom_scope, path);
|
char *ret = locate_auxiliary_file_at(config_dirs[i], picom_scope, path);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
free(config_dirs);
|
free(config_dirs);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -614,7 +613,7 @@ char *locate_auxiliary_file(const char *scope, const char *path, const char *inc
|
|||||||
}
|
}
|
||||||
free(config_dirs);
|
free(config_dirs);
|
||||||
|
|
||||||
return ret;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct debug_options_entry {
|
struct debug_options_entry {
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ char **xdg_config_dirs(void);
|
|||||||
/// Parse a configuration file
|
/// Parse a configuration file
|
||||||
/// Returns the actually config_file name used, allocated on heap
|
/// Returns the actually config_file name used, allocated on heap
|
||||||
/// Outputs:
|
/// Outputs:
|
||||||
/// shadow_enable = whether shaodw is enabled globally
|
/// shadow_enable = whether shadow is enabled globally
|
||||||
/// fading_enable = whether fading is enabled globally
|
/// fading_enable = whether fading is enabled globally
|
||||||
/// win_option_mask = whether option overrides for specific window type is set for given
|
/// win_option_mask = whether option overrides for specific window type is set for given
|
||||||
/// options
|
/// options
|
||||||
|
|||||||
@@ -81,17 +81,19 @@ FILE *open_config_file(const char *cpath, char **ppath) {
|
|||||||
|
|
||||||
// First search for config file in user config directory
|
// First search for config file in user config directory
|
||||||
auto config_home = xdg_config_home();
|
auto config_home = xdg_config_home();
|
||||||
auto ret = open_config_file_at(config_home, ppath);
|
if (config_home) {
|
||||||
free((void *)config_home);
|
auto ret = open_config_file_at(config_home, ppath);
|
||||||
if (ret) {
|
free((void *)config_home);
|
||||||
return ret;
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to legacy config file in user home directory
|
// Fall back to legacy config file in user home directory
|
||||||
const char *home = getenv("HOME");
|
const char *home = getenv("HOME");
|
||||||
if (home && strlen(home)) {
|
if (home && strlen(home)) {
|
||||||
auto path = mstrjoin(home, config_filename_legacy);
|
auto path = mstrjoin(home, config_filename_legacy);
|
||||||
ret = fopen(path, "r");
|
auto ret = fopen(path, "r");
|
||||||
if (ret && ppath) {
|
if (ret && ppath) {
|
||||||
*ppath = path;
|
*ppath = path;
|
||||||
} else {
|
} else {
|
||||||
@@ -105,7 +107,7 @@ FILE *open_config_file(const char *cpath, char **ppath) {
|
|||||||
// Fall back to config file in system config directory
|
// Fall back to config file in system config directory
|
||||||
auto config_dirs = xdg_config_dirs();
|
auto config_dirs = xdg_config_dirs();
|
||||||
for (int i = 0; config_dirs[i]; i++) {
|
for (int i = 0; config_dirs[i]; i++) {
|
||||||
ret = open_config_file_at(config_dirs[i], ppath);
|
auto ret = open_config_file_at(config_dirs[i], ppath);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
free(config_dirs);
|
free(config_dirs);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -485,7 +487,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
|||||||
}
|
}
|
||||||
// --sw-opti
|
// --sw-opti
|
||||||
if (lcfg_lookup_bool(&cfg, "sw-opti", &bval)) {
|
if (lcfg_lookup_bool(&cfg, "sw-opti", &bval)) {
|
||||||
log_warn("The sw-opti %s", deprecation_message);
|
log_error("The sw-opti %s", deprecation_message);
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
// --use-ewmh-active-win
|
// --use-ewmh-active-win
|
||||||
lcfg_lookup_bool(&cfg, "use-ewmh-active-win", &opt->use_ewmh_active_win);
|
lcfg_lookup_bool(&cfg, "use-ewmh-active-win", &opt->use_ewmh_active_win);
|
||||||
|
|||||||
@@ -906,7 +906,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_
|
|||||||
}
|
}
|
||||||
if (!strcmp("RawFocused", target)) {
|
if (!strcmp("RawFocused", target)) {
|
||||||
cdbus_reply(ps, msg, cdbus_append_bool_variant,
|
cdbus_reply(ps, msg, cdbus_append_bool_variant,
|
||||||
(bool[]){win_is_focused_raw(ps, w)});
|
(bool[]){win_is_focused_raw(w)});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -976,7 +976,7 @@ static bool cdbus_process_win_get(session_t *ps, DBusMessage *msg) {
|
|||||||
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
|
cdbus_m_win_get_do(wmwin, cdbus_reply_bool);
|
||||||
cdbus_m_win_get_do(leader, cdbus_reply_wid);
|
cdbus_m_win_get_do(leader, cdbus_reply_wid);
|
||||||
if (!strcmp("focused_raw", target)) {
|
if (!strcmp("focused_raw", target)) {
|
||||||
cdbus_reply_bool(ps, msg, win_is_focused_raw(ps, w));
|
cdbus_reply_bool(ps, msg, win_is_focused_raw(w));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
cdbus_m_win_get_do(fade_force, cdbus_reply_enum);
|
cdbus_m_win_get_do(fade_force, cdbus_reply_enum);
|
||||||
|
|||||||
70
src/event.c
70
src/event.c
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
// Copyright (c) 2019, Yuxuan Shui <yshuiv7@gmail.com>
|
// Copyright (c) 2019, Yuxuan Shui <yshuiv7@gmail.com>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <X11/Xlibint.h>
|
#include <X11/Xlibint.h>
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
/// made the query when those events were already in the queue. so the reply you got is
|
/// made the query when those events were already in the queue. so the reply you got is
|
||||||
/// more up-to-date than the events). Also, handling events when other client are making
|
/// more up-to-date than the events). Also, handling events when other client are making
|
||||||
/// concurrent requests is not good. Because the server states are changing without you
|
/// concurrent requests is not good. Because the server states are changing without you
|
||||||
/// knowning them. This is super racy, and can cause lots of potential problems.
|
/// knowing them. This is super racy, and can cause lots of potential problems.
|
||||||
///
|
///
|
||||||
/// All of above mandates we do these things:
|
/// All of above mandates we do these things:
|
||||||
/// 1. Grab server when handling events
|
/// 1. Grab server when handling events
|
||||||
@@ -47,8 +48,14 @@
|
|||||||
/// When top half finished, we enter the render stage, where no server state should be
|
/// When top half finished, we enter the render stage, where no server state should be
|
||||||
/// queried. All rendering should be done with our internal knowledge of the server state.
|
/// queried. All rendering should be done with our internal knowledge of the server state.
|
||||||
///
|
///
|
||||||
|
/// P.S. There is another reason to avoid sending any request to the server as much as
|
||||||
|
/// possible. To make sure requests are sent, flushes are needed. And `xcb_flush`/`XFlush`
|
||||||
|
/// functions may read more events from the server into their queues. This is
|
||||||
|
/// undesirable, see the comments on `handle_queued_x_events` in picom.c for more details.
|
||||||
|
|
||||||
// TODO(yshui) the things described above
|
// TODO(yshui) the things described above. This is mostly done, maybe some of
|
||||||
|
// the functions here is still making unnecessary queries, we need
|
||||||
|
// to do some auditing to be sure.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a window's name from window ID.
|
* Get a window's name from window ID.
|
||||||
@@ -324,7 +331,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ev->parent == ps->c.screen_info->root) {
|
if (ev->parent == ps->c.screen_info->root) {
|
||||||
// X will generate reparent notifiy even if the parent didn't actually
|
// X will generate reparent notify even if the parent didn't actually
|
||||||
// change (i.e. reparent again to current parent). So we check if that's
|
// change (i.e. reparent again to current parent). So we check if that's
|
||||||
// the case
|
// the case
|
||||||
auto w = find_win(ps, ev->window);
|
auto w = find_win(ps, ev->window);
|
||||||
@@ -350,19 +357,14 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset event mask in case something wrong happens
|
// Reset event mask in case something wrong happens
|
||||||
xcb_change_window_attributes(
|
uint32_t evmask = determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN);
|
||||||
ps->c.c, ev->window, XCB_CW_EVENT_MASK,
|
|
||||||
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)});
|
|
||||||
|
|
||||||
if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
|
if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
|
||||||
log_debug("Window %#010x doesn't have WM_STATE property, it is "
|
log_debug("Window %#010x doesn't have WM_STATE property, it is "
|
||||||
"probably not a client window. But we will listen for "
|
"probably not a client window. But we will listen for "
|
||||||
"property change in case it gains one.",
|
"property change in case it gains one.",
|
||||||
ev->window);
|
ev->window);
|
||||||
xcb_change_window_attributes(
|
evmask |= XCB_EVENT_MASK_PROPERTY_CHANGE;
|
||||||
ps->c.c, ev->window, XCB_CW_EVENT_MASK,
|
|
||||||
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
|
|
||||||
XCB_EVENT_MASK_PROPERTY_CHANGE});
|
|
||||||
} else {
|
} else {
|
||||||
auto w_real_top = find_managed_window_or_parent(ps, ev->parent);
|
auto w_real_top = find_managed_window_or_parent(ps, ev->parent);
|
||||||
if (w_real_top && w_real_top->state != WSTATE_UNMAPPED &&
|
if (w_real_top && w_real_top->state != WSTATE_UNMAPPED &&
|
||||||
@@ -386,6 +388,8 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
|
||||||
|
XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +469,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unconcerned about any other proprties on root window
|
// Unconcerned about any other properties on root window
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,9 +479,10 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||||||
// Check whether it could be a client window
|
// Check whether it could be a client window
|
||||||
if (!find_toplevel(ps, ev->window)) {
|
if (!find_toplevel(ps, ev->window)) {
|
||||||
// Reset event mask anyway
|
// Reset event mask anyway
|
||||||
xcb_change_window_attributes(ps->c.c, ev->window, XCB_CW_EVENT_MASK,
|
const uint32_t evmask =
|
||||||
(const uint32_t[]){determine_evmask(
|
determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN);
|
||||||
ps, ev->window, WIN_EVMODE_UNKNOWN)});
|
XCB_AWAIT_VOID(xcb_change_window_attributes, ps->c.c, ev->window,
|
||||||
|
XCB_CW_EVENT_MASK, (const uint32_t[]){evmask});
|
||||||
|
|
||||||
auto w_top = find_managed_window_or_parent(ps, ev->window);
|
auto w_top = find_managed_window_or_parent(ps, ev->window);
|
||||||
// ev->window might have not been managed yet, in that case w_top
|
// ev->window might have not been managed yet, in that case w_top
|
||||||
@@ -492,14 +497,14 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||||||
// If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
|
// If _NET_WM_WINDOW_TYPE changes... God knows why this would happen, but
|
||||||
// there are always some stupid applications. (#144)
|
// there are always some stupid applications. (#144)
|
||||||
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) {
|
if (ev->atom == ps->atoms->a_NET_WM_WINDOW_TYPE) {
|
||||||
struct managed_win *w = NULL;
|
struct managed_win *w = find_toplevel(ps, ev->window);
|
||||||
if ((w = find_toplevel(ps, ev->window))) {
|
if (w) {
|
||||||
win_set_property_stale(w, ev->atom);
|
win_set_property_stale(w, ev->atom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
|
if (ev->atom == ps->atoms->a_NET_WM_BYPASS_COMPOSITOR) {
|
||||||
// Unnecessay until we remove the queue_redraw in ev_handle
|
// Unnecessary until we remove the queue_redraw in ev_handle
|
||||||
queue_redraw(ps);
|
queue_redraw(ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,6 +565,13 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ps->o.no_ewmh_fullscreen && ev->atom == ps->atoms->a_NET_WM_STATE) {
|
||||||
|
auto w = find_toplevel(ps, ev->window);
|
||||||
|
if (w) {
|
||||||
|
win_set_property_stale(w, ev->atom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for other atoms we are tracking
|
// Check for other atoms we are tracking
|
||||||
for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
|
for (latom_t *platom = ps->track_atom_lst; platom; platom = platom->next) {
|
||||||
if (platom->atom == ev->atom) {
|
if (platom->atom == ev->atom) {
|
||||||
@@ -586,16 +598,28 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
|
|||||||
region_t parts;
|
region_t parts;
|
||||||
pixman_region32_init(&parts);
|
pixman_region32_init(&parts);
|
||||||
|
|
||||||
|
// If this is the first time this window is damaged, we would redraw the
|
||||||
|
// whole window, so we don't need to fetch the damage region. But we still need
|
||||||
|
// to make sure the X server receives the DamageSubtract request, hence the
|
||||||
|
// `xcb_request_check` here.
|
||||||
|
// Otherwise, we fetch the damage regions. That means we will receive a reply
|
||||||
|
// from the X server, which implies it has received our DamageSubtract request.
|
||||||
if (!w->ever_damaged) {
|
if (!w->ever_damaged) {
|
||||||
win_extents(w, &parts);
|
auto e = xcb_request_check(
|
||||||
if (!ps->o.show_all_xerrors) {
|
ps->c.c, xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE, XCB_NONE));
|
||||||
set_ignore_cookie(&ps->c, xcb_damage_subtract(ps->c.c, w->damage,
|
if (e) {
|
||||||
XCB_NONE, XCB_NONE));
|
if (ps->o.show_all_xerrors) {
|
||||||
|
x_print_error(e->sequence, e->major_code, e->minor_code,
|
||||||
|
e->error_code);
|
||||||
|
}
|
||||||
|
free(e);
|
||||||
}
|
}
|
||||||
|
win_extents(w, &parts);
|
||||||
} else {
|
} else {
|
||||||
|
auto cookie =
|
||||||
|
xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE, ps->damaged_region);
|
||||||
if (!ps->o.show_all_xerrors) {
|
if (!ps->o.show_all_xerrors) {
|
||||||
set_ignore_cookie(&ps->c, xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE,
|
set_ignore_cookie(&ps->c, cookie);
|
||||||
ps->damaged_region));
|
|
||||||
}
|
}
|
||||||
x_fetch_region(&ps->c, ps->damaged_region, &parts);
|
x_fetch_region(&ps->c, ps->damaged_region, &parts);
|
||||||
pixman_region32_translate(&parts, w->g.x + w->g.border_width,
|
pixman_region32_translate(&parts, w->g.x + w->g.border_width,
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ bool file_watch_add(void *_fwr, const char *filename, file_watch_cb_t cb, void *
|
|||||||
fflags |= NOTE_CLOSE_WRITE;
|
fflags |= NOTE_CLOSE_WRITE;
|
||||||
#else
|
#else
|
||||||
// NOTE_WRITE will receive notification more frequent than necessary, so is less
|
// NOTE_WRITE will receive notification more frequent than necessary, so is less
|
||||||
// preferrable
|
// preferable
|
||||||
fflags |= NOTE_WRITE;
|
fflags |= NOTE_WRITE;
|
||||||
#endif
|
#endif
|
||||||
struct kevent ev = {
|
struct kevent ev = {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ static inline double estimate_first_row_sum(double size, double r) {
|
|||||||
// `a` is gaussian at (size, 0)
|
// `a` is gaussian at (size, 0)
|
||||||
double a = exp(-0.5 * size * size / (r * r)) / sqrt(2 * M_PI) / r;
|
double a = exp(-0.5 * size * size / (r * r)) / sqrt(2 * M_PI) / r;
|
||||||
// The sum of the whole kernel is normalized to 1, i.e. each element is divided by
|
// The sum of the whole kernel is normalized to 1, i.e. each element is divided by
|
||||||
// factor sqaured. So the sum of the first row is a * factor / factor^2 = a /
|
// factor squared. So the sum of the first row is a * factor / factor^2 = a /
|
||||||
// factor
|
// factor
|
||||||
return a / factor;
|
return a / factor;
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/list.h
13
src/list.h
@@ -2,18 +2,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
/**
|
#include "utils.h"
|
||||||
* container_of - cast a member of a structure out to the containing structure
|
|
||||||
* @ptr: the pointer to the member.
|
|
||||||
* @type: the type of the container struct this is embedded in.
|
|
||||||
* @member: the name of the member within the struct.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define container_of(ptr, type, member) \
|
|
||||||
({ \
|
|
||||||
const __typeof__(((type *)0)->member) *__mptr = (ptr); \
|
|
||||||
(type *)((char *)__mptr - offsetof(type, member)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
struct list_node {
|
struct list_node {
|
||||||
struct list_node *next, *prev;
|
struct list_node *next, *prev;
|
||||||
|
|||||||
31
src/log.c
31
src/log.c
@@ -9,7 +9,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
#include <GL/gl.h>
|
#include <epoxy/gl.h>
|
||||||
#include "backend/gl/gl_common.h"
|
#include "backend/gl/gl_common.h"
|
||||||
#include "backend/gl/glx.h"
|
#include "backend/gl/glx.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -338,21 +338,14 @@ struct log_target *stderr_logger_new(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
/// An opengl logger that can be used for logging into opengl debugging tools,
|
|
||||||
/// such as apitrace
|
|
||||||
struct gl_string_marker_logger {
|
|
||||||
struct log_target tgt;
|
|
||||||
PFNGLSTRINGMARKERGREMEDYPROC gl_string_marker;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
static void gl_string_marker_logger_write(struct log_target *tgt attr_unused,
|
||||||
gl_string_marker_logger_write(struct log_target *tgt, const char *str, size_t len) {
|
const char *str, size_t len) {
|
||||||
auto g = (struct gl_string_marker_logger *)tgt;
|
|
||||||
// strip newlines at the end of the string
|
// strip newlines at the end of the string
|
||||||
while (len > 0 && str[len - 1] == '\n') {
|
while (len > 0 && str[len - 1] == '\n') {
|
||||||
len--;
|
len--;
|
||||||
}
|
}
|
||||||
g->gl_string_marker((GLsizei)len, str);
|
glStringMarkerGREMEDY((GLsizei)len, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct log_ops gl_string_marker_logger_ops = {
|
static const struct log_ops gl_string_marker_logger_ops = {
|
||||||
@@ -361,20 +354,16 @@ static const struct log_ops gl_string_marker_logger_ops = {
|
|||||||
.destroy = logger_trivial_destroy,
|
.destroy = logger_trivial_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Create an opengl logger that can be used for logging into opengl debugging tools,
|
||||||
|
/// such as apitrace
|
||||||
struct log_target *gl_string_marker_logger_new(void) {
|
struct log_target *gl_string_marker_logger_new(void) {
|
||||||
if (!gl_has_extension("GL_GREMEDY_string_marker")) {
|
if (!epoxy_has_gl_extension("GL_GREMEDY_string_marker")) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *fnptr = glXGetProcAddress((GLubyte *)"glStringMarkerGREMEDY");
|
auto ret = cmalloc(struct log_target);
|
||||||
if (!fnptr) {
|
ret->ops = &gl_string_marker_logger_ops;
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
auto ret = cmalloc(struct gl_string_marker_logger);
|
|
||||||
ret->tgt.ops = &gl_string_marker_logger_ops;
|
|
||||||
ret->gl_string_marker = fnptr;
|
|
||||||
return &ret->tgt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ required_xcb_packages = [
|
|||||||
# Some XCB packages are here because their versioning differs (see check below).
|
# Some XCB packages are here because their versioning differs (see check below).
|
||||||
required_packages = [
|
required_packages = [
|
||||||
'pixman-1', 'x11', 'x11-xcb', 'xcb-image', 'xcb-renderutil', 'xcb-util',
|
'pixman-1', 'x11', 'x11-xcb', 'xcb-image', 'xcb-renderutil', 'xcb-util',
|
||||||
'xext'
|
'xext', 'threads',
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach i : required_packages
|
foreach i : required_packages
|
||||||
@@ -58,8 +58,8 @@ if get_option('vsync_drm')
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if get_option('opengl')
|
if get_option('opengl')
|
||||||
cflags += ['-DCONFIG_OPENGL', '-DGL_GLEXT_PROTOTYPES']
|
cflags += ['-DCONFIG_OPENGL']
|
||||||
deps += [dependency('gl', required: true), dependency('egl', required: true), dependency('threads', required:true)]
|
deps += [dependency('epoxy', required: true)]
|
||||||
srcs += [ 'opengl.c' ]
|
srcs += [ 'opengl.c' ]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ bool glx_init(session_t *ps, bool need_render) {
|
|||||||
// must precede FBConfig fetching
|
// must precede FBConfig fetching
|
||||||
if (need_render) {
|
if (need_render) {
|
||||||
psglx->has_texture_non_power_of_two =
|
psglx->has_texture_non_power_of_two =
|
||||||
gl_has_extension("GL_ARB_texture_non_power_of_two");
|
epoxy_has_gl_extension("GL_ARB_texture_non_power_of_two");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render preparations
|
// Render preparations
|
||||||
@@ -279,7 +279,7 @@ void glx_destroy(session_t *ps) {
|
|||||||
|
|
||||||
free(ps->psglx);
|
free(ps->psglx);
|
||||||
ps->psglx = NULL;
|
ps->psglx = NULL;
|
||||||
ps->argb_fbconfig = NULL;
|
ps->argb_fbconfig = (struct glx_fbconfig_info){0};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1129,7 +1129,7 @@ glx_blur_dst_end:
|
|||||||
// TODO(bhagwan) this is a mess and needs a more consistent way of getting the border
|
// TODO(bhagwan) this is a mess and needs a more consistent way of getting the border
|
||||||
// pixel I tried looking for a notify event for XCB_CW_BORDER_PIXEL (in
|
// pixel I tried looking for a notify event for XCB_CW_BORDER_PIXEL (in
|
||||||
// xcb_create_window()) or a way to get the pixels from xcb_render_picture_t but the
|
// xcb_create_window()) or a way to get the pixels from xcb_render_picture_t but the
|
||||||
// documentation for the xcb_xrender extension is literaly non existent...
|
// documentation for the xcb_xrender extension is literally non existent...
|
||||||
//
|
//
|
||||||
// NOTE(yshui) There is no consistent way to get the "border" color of a X window. From
|
// NOTE(yshui) There is no consistent way to get the "border" color of a X window. From
|
||||||
// the WM's perspective there are multiple ways to implement window borders. Using
|
// the WM's perspective there are multiple ways to implement window borders. Using
|
||||||
|
|||||||
15
src/opengl.h
15
src/opengl.h
@@ -18,9 +18,9 @@
|
|||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "win.h"
|
#include "win.h"
|
||||||
|
|
||||||
#include <GL/gl.h>
|
|
||||||
#include <GL/glx.h>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <epoxy/gl.h>
|
||||||
|
#include <epoxy/glx.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -75,7 +75,7 @@ typedef struct glx_session {
|
|||||||
glx_round_pass_t *round_passes;
|
glx_round_pass_t *round_passes;
|
||||||
} glx_session_t;
|
} glx_session_t;
|
||||||
|
|
||||||
/// @brief Wrapper of a binded GLX texture.
|
/// @brief Wrapper of a bound GLX texture.
|
||||||
typedef struct _glx_texture {
|
typedef struct _glx_texture {
|
||||||
GLuint texture;
|
GLuint texture;
|
||||||
GLXPixmap glpixmap;
|
GLXPixmap glpixmap;
|
||||||
@@ -121,9 +121,9 @@ bool glx_bind_texture(session_t *ps, glx_texture_t **pptex, int x, int y, int wi
|
|||||||
void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2);
|
void glx_paint_pre(session_t *ps, region_t *preg) attr_nonnull(1, 2);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a texture is binded, or is binded to the given pixmap.
|
* Check if a texture is bound, or is bound to the given pixmap.
|
||||||
*/
|
*/
|
||||||
static inline bool glx_tex_binded(const glx_texture_t *ptex, xcb_pixmap_t pixmap) {
|
static inline bool glx_tex_bound(const glx_texture_t *ptex, xcb_pixmap_t pixmap) {
|
||||||
return ptex && ptex->glpixmap && ptex->texture && (!pixmap || pixmap == ptex->pixmap);
|
return ptex && ptex->glpixmap && ptex->texture && (!pixmap || pixmap == ptex->pixmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,10 +228,7 @@ 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);
|
||||||
#ifdef CONFIG_OPENGL
|
ppaint->fbcfg = (struct glx_fbconfig_info){0};
|
||||||
free(ppaint->fbcfg);
|
|
||||||
#endif
|
|
||||||
ppaint->fbcfg = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ static void usage(const char *argv0, int ret) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *shortopts = "D:I:O:r:o:m:l:t:i:e:hscnfFCazGb";
|
static const char *shortopts = "D:I:O:r:o:m:l:t:i:e:hscnfCazGb";
|
||||||
|
|
||||||
/// Get config options that are needed to parse the rest of the options
|
/// Get config options that are needed to parse the rest of the options
|
||||||
/// Return true if we should quit
|
/// Return true if we should quit
|
||||||
@@ -325,9 +325,9 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all
|
|||||||
|
|
||||||
int o = 0, longopt_idx = -1;
|
int o = 0, longopt_idx = -1;
|
||||||
|
|
||||||
// Pre-parse the commandline arguments to check for --config and invalid
|
// Pre-parse the command line arguments to check for --config and invalid
|
||||||
// switches
|
// switches
|
||||||
// Must reset optind to 0 here in case we reread the commandline
|
// Must reset optind to 0 here in case we reread the command line
|
||||||
// arguments
|
// arguments
|
||||||
optind = 1;
|
optind = 1;
|
||||||
*config_file = NULL;
|
*config_file = NULL;
|
||||||
@@ -379,7 +379,7 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
|||||||
// instead of commas in atof().
|
// instead of commas in atof().
|
||||||
setlocale(LC_NUMERIC, "C");
|
setlocale(LC_NUMERIC, "C");
|
||||||
|
|
||||||
// Parse commandline arguments. Range checking will be done later.
|
// Parse command line arguments. Range checking will be done later.
|
||||||
|
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
const char *deprecation_message attr_unused =
|
const char *deprecation_message attr_unused =
|
||||||
@@ -435,7 +435,6 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
|||||||
opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = tmp;
|
opt->wintype_option[WINTYPE_DROPDOWN_MENU].opacity = tmp;
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
case 'F':
|
|
||||||
fading_enable = true;
|
fading_enable = true;
|
||||||
break;
|
break;
|
||||||
P_CASEINT('r', shadow_radius);
|
P_CASEINT('r', shadow_radius);
|
||||||
@@ -520,8 +519,9 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
|||||||
"--crop-shadow-to-monitor instead.");
|
"--crop-shadow-to-monitor instead.");
|
||||||
break;
|
break;
|
||||||
case 274:
|
case 274:
|
||||||
log_warn("--sw-opti has been deprecated, please remove it from the "
|
log_error("--sw-opti has been deprecated, please remove it from the "
|
||||||
"command line options");
|
"command line options");
|
||||||
|
failed = true;
|
||||||
break;
|
break;
|
||||||
case 275:
|
case 275:
|
||||||
// --vsync-aggressive
|
// --vsync-aggressive
|
||||||
@@ -532,9 +532,10 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
|||||||
P_CASEBOOL(276, use_ewmh_active_win);
|
P_CASEBOOL(276, use_ewmh_active_win);
|
||||||
case 277:
|
case 277:
|
||||||
// --respect-prop-shadow
|
// --respect-prop-shadow
|
||||||
log_warn("--respect-prop-shadow option has been deprecated, its "
|
log_error("--respect-prop-shadow option has been deprecated, its "
|
||||||
"functionality will always be enabled. Please remove it "
|
"functionality will always be enabled. Please remove it "
|
||||||
"from the command line options");
|
"from the command line options");
|
||||||
|
failed = true;
|
||||||
break;
|
break;
|
||||||
P_CASEBOOL(278, unredir_if_possible);
|
P_CASEBOOL(278, unredir_if_possible);
|
||||||
case 279:
|
case 279:
|
||||||
@@ -730,7 +731,7 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
|||||||
opt->blur_strength = atoi(optarg);
|
opt->blur_strength = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 333:
|
case 333:
|
||||||
// --cornor-radius
|
// --corner-radius
|
||||||
opt->corner_radius = atoi(optarg);
|
opt->corner_radius = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 334:
|
case 334:
|
||||||
|
|||||||
273
src/picom.c
273
src/picom.c
@@ -1,10 +1,12 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
/*
|
/*
|
||||||
* Compton - a compositor for X11
|
* picom - a compositor for X11
|
||||||
*
|
*
|
||||||
|
* Based on `compton` - Copyright (c) 2011-2013, Christopher Jeffrey
|
||||||
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
||||||
*
|
*
|
||||||
* Copyright (c) 2011-2013, Christopher Jeffrey
|
* Copyright (c) 2019-2023, Yuxuan Shui
|
||||||
|
*
|
||||||
* See LICENSE-mit for more information.
|
* See LICENSE-mit for more information.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -16,6 +18,8 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -31,6 +35,7 @@
|
|||||||
#include <xcb/randr.h>
|
#include <xcb/randr.h>
|
||||||
#include <xcb/render.h>
|
#include <xcb/render.h>
|
||||||
#include <xcb/sync.h>
|
#include <xcb/sync.h>
|
||||||
|
#include <xcb/xcb_aux.h>
|
||||||
#include <xcb/xfixes.h>
|
#include <xcb/xfixes.h>
|
||||||
|
|
||||||
#include <ev.h>
|
#include <ev.h>
|
||||||
@@ -144,10 +149,10 @@ static inline struct managed_win *find_win_all(session_t *ps, const xcb_window_t
|
|||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_render_finish(struct vblank_event *e attr_unused, void *ud) {
|
enum vblank_callback_action check_render_finish(struct vblank_event *e attr_unused, void *ud) {
|
||||||
auto ps = (session_t *)ud;
|
auto ps = (session_t *)ud;
|
||||||
if (!ps->backend_busy) {
|
if (!ps->backend_busy) {
|
||||||
return;
|
return VBLANK_CALLBACK_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec render_time;
|
struct timespec render_time;
|
||||||
@@ -159,8 +164,7 @@ void check_render_finish(struct vblank_event *e attr_unused, void *ud) {
|
|||||||
log_debug("Last render did not complete during vblank, msc: "
|
log_debug("Last render did not complete during vblank, msc: "
|
||||||
"%" PRIu64,
|
"%" PRIu64,
|
||||||
ps->last_msc);
|
ps->last_msc);
|
||||||
vblank_scheduler_schedule(ps->vblank_scheduler, check_render_finish, ud);
|
return VBLANK_CALLBACK_AGAIN;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The frame has been finished and presented, record its render time.
|
// The frame has been finished and presented, record its render time.
|
||||||
@@ -174,22 +178,20 @@ void check_render_finish(struct vblank_event *e attr_unused, void *ud) {
|
|||||||
render_time_us, (int)ps->last_schedule_delay, ps->last_msc);
|
render_time_us, (int)ps->last_schedule_delay, ps->last_msc);
|
||||||
}
|
}
|
||||||
ps->backend_busy = false;
|
ps->backend_busy = false;
|
||||||
|
return VBLANK_CALLBACK_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void collect_vblank_interval_statistics(struct vblank_event *e, void *ud) {
|
enum vblank_callback_action
|
||||||
|
collect_vblank_interval_statistics(struct vblank_event *e, void *ud) {
|
||||||
auto ps = (session_t *)ud;
|
auto ps = (session_t *)ud;
|
||||||
|
double vblank_interval = NAN;
|
||||||
assert(ps->frame_pacing);
|
assert(ps->frame_pacing);
|
||||||
assert(ps->vblank_scheduler);
|
assert(ps->vblank_scheduler);
|
||||||
|
|
||||||
if (ps->last_msc == e->msc) {
|
|
||||||
// Already collected statistics for this vblank
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ps->o.debug_options.smart_frame_pacing) {
|
if (!ps->o.debug_options.smart_frame_pacing) {
|
||||||
// We don't need to collect statistics if we are not doing smart frame
|
// We don't need to collect statistics if we are not doing smart frame
|
||||||
// pacing.
|
// pacing.
|
||||||
return;
|
return VBLANK_CALLBACK_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(yshui): this naive method of estimating vblank interval does not handle
|
// TODO(yshui): this naive method of estimating vblank interval does not handle
|
||||||
@@ -200,29 +202,44 @@ void collect_vblank_interval_statistics(struct vblank_event *e, void *ud) {
|
|||||||
// samples when the monitor is off, but I had a hard time to get it
|
// samples when the monitor is off, but I had a hard time to get it
|
||||||
// working reliably, there are just too many corner cases.
|
// working reliably, there are just too many corner cases.
|
||||||
|
|
||||||
if (ps->last_msc_instant != 0) {
|
// Don't add sample again if we already collected statistics for this vblank
|
||||||
auto frame_count = e->msc - ps->last_msc;
|
if (ps->last_msc < e->msc) {
|
||||||
int frame_time = (int)((e->ust - ps->last_msc_instant) / frame_count);
|
if (ps->last_msc_instant != 0) {
|
||||||
if (frame_count == 1) {
|
auto frame_count = e->msc - ps->last_msc;
|
||||||
render_statistics_add_vblank_time_sample(&ps->render_stats, frame_time);
|
auto frame_time =
|
||||||
log_trace("Frame count %lu, frame time: %d us, ust: %" PRIu64 "",
|
(int)((e->ust - ps->last_msc_instant) / frame_count);
|
||||||
frame_count, frame_time, e->ust);
|
if (frame_count == 1) {
|
||||||
} else {
|
render_statistics_add_vblank_time_sample(
|
||||||
log_trace("Frame count %lu, frame time: %d us, msc: %" PRIu64
|
&ps->render_stats, frame_time);
|
||||||
", not adding sample.",
|
log_trace("Frame count %" PRIu64 ", frame time: %d us, "
|
||||||
frame_count, frame_time, e->ust);
|
"ust: "
|
||||||
|
"%" PRIu64,
|
||||||
|
frame_count, frame_time, e->ust);
|
||||||
|
} else {
|
||||||
|
log_trace("Frame count %" PRIu64 ", frame time: %d us, "
|
||||||
|
"msc: "
|
||||||
|
"%" PRIu64 ", not adding sample.",
|
||||||
|
frame_count, frame_time, e->ust);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ps->last_msc_instant = e->ust;
|
||||||
|
ps->last_msc = e->msc;
|
||||||
|
} else if (ps->last_msc > e->msc) {
|
||||||
|
log_warn("PresentCompleteNotify msc is going backwards, last_msc: "
|
||||||
|
"%" PRIu64 ", current msc: %" PRIu64,
|
||||||
|
ps->last_msc, e->msc);
|
||||||
|
ps->last_msc_instant = 0;
|
||||||
|
ps->last_msc = 0;
|
||||||
}
|
}
|
||||||
ps->last_msc_instant = e->ust;
|
|
||||||
ps->last_msc = e->msc;
|
vblank_interval = render_statistics_get_vblank_time(&ps->render_stats);
|
||||||
double vblank_interval = render_statistics_get_vblank_time(&ps->render_stats);
|
|
||||||
log_trace("Vblank interval estimate: %f us", vblank_interval);
|
log_trace("Vblank interval estimate: %f us", vblank_interval);
|
||||||
if (vblank_interval == 0) {
|
if (vblank_interval == 0) {
|
||||||
// We don't have enough data for vblank interval estimate, schedule
|
// We don't have enough data for vblank interval estimate, schedule
|
||||||
// another vblank event.
|
// another vblank event.
|
||||||
vblank_scheduler_schedule(ps->vblank_scheduler,
|
return VBLANK_CALLBACK_AGAIN;
|
||||||
collect_vblank_interval_statistics, ud);
|
|
||||||
}
|
}
|
||||||
|
return VBLANK_CALLBACK_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void schedule_render(session_t *ps, bool triggered_by_vblank);
|
void schedule_render(session_t *ps, bool triggered_by_vblank);
|
||||||
@@ -230,7 +247,7 @@ void schedule_render(session_t *ps, bool triggered_by_vblank);
|
|||||||
/// vblank callback scheduled by schedule_render, when a render is ongoing.
|
/// vblank callback scheduled by schedule_render, when a render is ongoing.
|
||||||
///
|
///
|
||||||
/// Check if previously queued render has finished, and reschedule render if it has.
|
/// Check if previously queued render has finished, and reschedule render if it has.
|
||||||
void reschedule_render_at_vblank(struct vblank_event *e, void *ud) {
|
enum vblank_callback_action reschedule_render_at_vblank(struct vblank_event *e, void *ud) {
|
||||||
auto ps = (session_t *)ud;
|
auto ps = (session_t *)ud;
|
||||||
assert(ps->frame_pacing);
|
assert(ps->frame_pacing);
|
||||||
assert(ps->render_queued);
|
assert(ps->render_queued);
|
||||||
@@ -242,12 +259,11 @@ void reschedule_render_at_vblank(struct vblank_event *e, void *ud) {
|
|||||||
check_render_finish(e, ud);
|
check_render_finish(e, ud);
|
||||||
|
|
||||||
if (ps->backend_busy) {
|
if (ps->backend_busy) {
|
||||||
vblank_scheduler_schedule(ps->vblank_scheduler,
|
return VBLANK_CALLBACK_AGAIN;
|
||||||
reschedule_render_at_vblank, ud);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_render(ps, false);
|
schedule_render(ps, false);
|
||||||
|
return VBLANK_CALLBACK_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How many seconds into the future should we start rendering the next frame.
|
/// How many seconds into the future should we start rendering the next frame.
|
||||||
@@ -258,7 +274,7 @@ void reschedule_render_at_vblank(struct vblank_event *e, void *ud) {
|
|||||||
/// is no render currently scheduled. i.e. render_queued == false.
|
/// is no render currently scheduled. i.e. render_queued == false.
|
||||||
/// 2. then, we need to figure out the best time to start rendering. we need to
|
/// 2. then, we need to figure out the best time to start rendering. we need to
|
||||||
/// at least know when the next vblank will start, as we can't start render
|
/// at least know when the next vblank will start, as we can't start render
|
||||||
/// before the current rendered frame is diplayed on screen. we have this
|
/// before the current rendered frame is displayed on screen. we have this
|
||||||
/// information from the vblank scheduler, it will notify us when that happens.
|
/// information from the vblank scheduler, it will notify us when that happens.
|
||||||
/// we might also want to delay the rendering even further to reduce latency,
|
/// we might also want to delay the rendering even further to reduce latency,
|
||||||
/// this is discussed below, in FUTURE WORKS.
|
/// this is discussed below, in FUTURE WORKS.
|
||||||
@@ -463,27 +479,29 @@ static double fade_timeout(session_t *ps) {
|
|||||||
*/
|
*/
|
||||||
static bool run_fade(session_t *ps, struct managed_win **_w, long long steps) {
|
static bool run_fade(session_t *ps, struct managed_win **_w, long long steps) {
|
||||||
auto w = *_w;
|
auto w = *_w;
|
||||||
|
log_trace("Process fading for window %s (%#010x), steps: %lld", w->name,
|
||||||
|
w->base.id, steps);
|
||||||
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
|
if (w->state == WSTATE_MAPPED || w->state == WSTATE_UNMAPPED) {
|
||||||
// We are not fading
|
// We are not fading
|
||||||
assert(w->opacity_target == w->opacity);
|
assert(w->opacity_target == w->opacity);
|
||||||
|
log_trace("|- not fading");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!win_should_fade(ps, w)) {
|
if (!win_should_fade(ps, w)) {
|
||||||
log_debug("Window %#010x %s doesn't need fading", w->base.id, w->name);
|
log_trace("|- in transition but doesn't need fading");
|
||||||
w->opacity = w->opacity_target;
|
w->opacity = w->opacity_target;
|
||||||
}
|
}
|
||||||
if (w->opacity == w->opacity_target) {
|
if (w->opacity == w->opacity_target) {
|
||||||
// We have reached target opacity.
|
// We have reached target opacity.
|
||||||
// We don't call win_check_fade_finished here because that could destroy
|
// We don't call win_check_fade_finished here because that could destroy
|
||||||
// the window, but we still need the damage info from this window
|
// the window, but we still need the damage info from this window
|
||||||
log_debug("Fading finished for window %#010x %s", w->base.id, w->name);
|
log_trace("|- was fading but finished");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log_trace("|- fading, opacity: %lf", w->opacity);
|
||||||
if (steps) {
|
if (steps) {
|
||||||
log_trace("Window %#010x (%s) opacity was: %lf", w->base.id, w->name,
|
|
||||||
w->opacity);
|
|
||||||
if (w->opacity < w->opacity_target) {
|
if (w->opacity < w->opacity_target) {
|
||||||
w->opacity = clamp(w->opacity + ps->o.fade_in_step * (double)steps,
|
w->opacity = clamp(w->opacity + ps->o.fade_in_step * (double)steps,
|
||||||
0.0, w->opacity_target);
|
0.0, w->opacity_target);
|
||||||
@@ -491,7 +509,7 @@ static bool run_fade(session_t *ps, struct managed_win **_w, long long steps) {
|
|||||||
w->opacity = clamp(w->opacity - ps->o.fade_out_step * (double)steps,
|
w->opacity = clamp(w->opacity - ps->o.fade_out_step * (double)steps,
|
||||||
w->opacity_target, 1);
|
w->opacity_target, 1);
|
||||||
}
|
}
|
||||||
log_trace("... updated to: %lf", w->opacity);
|
log_trace("|- opacity updated: %lf", w->opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note even if opacity == opacity_target here, we still want to run preprocess
|
// Note even if opacity == opacity_target here, we still want to run preprocess
|
||||||
@@ -773,6 +791,8 @@ err:
|
|||||||
|
|
||||||
/// Handle configure event of the root window
|
/// Handle configure event of the root window
|
||||||
static void configure_root(session_t *ps) {
|
static void configure_root(session_t *ps) {
|
||||||
|
// TODO(yshui) re-initializing backend should be done outside of the
|
||||||
|
// critical section. Probably set a flag and do it in draw_callback_impl.
|
||||||
auto r = XCB_AWAIT(xcb_get_geometry, ps->c.c, ps->c.screen_info->root);
|
auto r = XCB_AWAIT(xcb_get_geometry, ps->c.c, ps->c.screen_info->root);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
log_fatal("Failed to fetch root geometry");
|
log_fatal("Failed to fetch root geometry");
|
||||||
@@ -812,6 +832,13 @@ static void configure_root(session_t *ps) {
|
|||||||
top_w->reg_ignore_valid = false;
|
top_w->reg_ignore_valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether a window is fullscreen depends on the new screen
|
||||||
|
// size. So we need to refresh the fullscreen state of all
|
||||||
|
// windows.
|
||||||
|
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||||
|
win_update_is_fullscreen(ps, w);
|
||||||
|
}
|
||||||
|
|
||||||
if (ps->redirected) {
|
if (ps->redirected) {
|
||||||
for (int i = 0; i < ps->ndamage; i++) {
|
for (int i = 0; i < ps->ndamage; i++) {
|
||||||
pixman_region32_clear(&ps->damage_ring[i]);
|
pixman_region32_clear(&ps->damage_ring[i]);
|
||||||
@@ -1045,8 +1072,10 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
|
|||||||
pixman_region32_init_rect(&w->bounding_shape, 0, 0,
|
pixman_region32_init_rect(&w->bounding_shape, 0, 0,
|
||||||
(uint)w->widthb, (uint)w->heightb);
|
(uint)w->widthb, (uint)w->heightb);
|
||||||
|
|
||||||
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING && w->state != WSTATE_UNMAPPING) {
|
||||||
win_process_image_flags(ps, w);
|
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
||||||
|
win_process_image_flags(ps, w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Mark new window region with damage
|
// Mark new window region with damage
|
||||||
if (was_painted && geometry_changed) {
|
if (was_painted && geometry_changed) {
|
||||||
@@ -1132,13 +1161,19 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
|
log_trace("Checking whether window %#010x (%s) should be painted",
|
||||||
|
w->base.id, 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
|
||||||
// pixmap is gone (for example due to a ConfigureNotify), or when it's
|
// pixmap is gone (for example due to a ConfigureNotify), or when it's
|
||||||
// excluded
|
// excluded
|
||||||
if (w->state == WSTATE_UNMAPPED ||
|
if (w->state == WSTATE_UNMAPPED) {
|
||||||
unlikely(w->base.id == ps->debug_window ||
|
log_trace("|- is unmapped");
|
||||||
w->client_win == ps->debug_window)) {
|
to_paint = false;
|
||||||
|
} else if (unlikely(ps->debug_window != XCB_NONE) &&
|
||||||
|
(w->base.id == ps->debug_window ||
|
||||||
|
w->client_win == ps->debug_window)) {
|
||||||
|
log_trace("|- is the debug window");
|
||||||
to_paint = false;
|
to_paint = false;
|
||||||
} else if (!w->ever_damaged && w->state != WSTATE_UNMAPPING &&
|
} else if (!w->ever_damaged && w->state != WSTATE_UNMAPPING &&
|
||||||
w->state != WSTATE_DESTROYING) {
|
w->state != WSTATE_DESTROYING) {
|
||||||
@@ -1146,33 +1181,23 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
|
|||||||
// is fading out means it must have been damaged when it was still
|
// is fading out means it must have been damaged when it was still
|
||||||
// mapped (because unmap_win_start will skip fading if it wasn't),
|
// mapped (because unmap_win_start will skip fading if it wasn't),
|
||||||
// so we still need to paint it.
|
// so we still need to paint it.
|
||||||
log_trace("Window %#010x (%s) will not be painted because it has "
|
log_trace("|- has not received any damages");
|
||||||
"not received any damages",
|
|
||||||
w->base.id, w->name);
|
|
||||||
to_paint = false;
|
to_paint = false;
|
||||||
} else if (unlikely(w->g.x + w->g.width < 1 || w->g.y + w->g.height < 1 ||
|
} else if (unlikely(w->g.x + w->g.width < 1 || w->g.y + w->g.height < 1 ||
|
||||||
w->g.x >= ps->root_width || w->g.y >= ps->root_height)) {
|
w->g.x >= ps->root_width || w->g.y >= ps->root_height)) {
|
||||||
log_trace("Window %#010x (%s) will not be painted because it is "
|
log_trace("|- is positioned outside of the screen");
|
||||||
"positioned outside of the screen",
|
|
||||||
w->base.id, w->name);
|
|
||||||
to_paint = false;
|
to_paint = false;
|
||||||
} else if (unlikely((double)w->opacity * MAX_ALPHA < 1 && !w->blur_background)) {
|
} else if (unlikely((double)w->opacity * MAX_ALPHA < 1 && !w->blur_background)) {
|
||||||
/* TODO(yshui) for consistency, even a window has 0 opacity, we
|
/* TODO(yshui) for consistency, even a window has 0 opacity, we
|
||||||
* still probably need to blur its background, so to_paint
|
* still probably need to blur its background, so to_paint
|
||||||
* shouldn't be false for them. */
|
* shouldn't be false for them. */
|
||||||
log_trace("Window %#010x (%s) will not be painted because it has "
|
log_trace("|- has 0 opacity");
|
||||||
"0 opacity",
|
|
||||||
w->base.id, w->name);
|
|
||||||
to_paint = false;
|
to_paint = false;
|
||||||
} else if (w->paint_excluded) {
|
} else if (w->paint_excluded) {
|
||||||
log_trace("Window %#010x (%s) will not be painted because it is "
|
log_trace("|- is excluded from painting");
|
||||||
"excluded from painting",
|
|
||||||
w->base.id, w->name);
|
|
||||||
to_paint = false;
|
to_paint = false;
|
||||||
} else if (unlikely((w->flags & WIN_FLAGS_IMAGE_ERROR) != 0)) {
|
} else if (unlikely((w->flags & WIN_FLAGS_IMAGE_ERROR) != 0)) {
|
||||||
log_trace("Window %#010x (%s) will not be painted because it has "
|
log_trace("|- has image errors");
|
||||||
"image errors",
|
|
||||||
w->base.id, w->name);
|
|
||||||
to_paint = false;
|
to_paint = false;
|
||||||
}
|
}
|
||||||
// log_trace("%s %d %d %d", w->name, to_paint, w->opacity,
|
// log_trace("%s %d %d %d", w->name, to_paint, w->opacity,
|
||||||
@@ -1187,10 +1212,12 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
|
|||||||
|
|
||||||
// to_paint will never change after this point
|
// to_paint will never change after this point
|
||||||
if (!to_paint) {
|
if (!to_paint) {
|
||||||
|
log_trace("|- will not be painted");
|
||||||
goto skip_window;
|
goto skip_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_trace("Window %#010x (%s) will be painted", w->base.id, w->name);
|
log_trace("|- will be painted");
|
||||||
|
log_verbose("Window %#010x (%s) will be painted", w->base.id, w->name);
|
||||||
|
|
||||||
// Calculate shadow opacity
|
// Calculate shadow opacity
|
||||||
w->shadow_opacity = ps->o.shadow_opacity * w->opacity * ps->o.frame_opacity;
|
w->shadow_opacity = ps->o.shadow_opacity * w->opacity * ps->o.frame_opacity;
|
||||||
@@ -1229,7 +1256,7 @@ paint_preprocess(session_t *ps, bool *fade_running, bool *animation_running) {
|
|||||||
// is not correctly set.
|
// is not correctly set.
|
||||||
if (ps->o.unredir_if_possible && is_highest) {
|
if (ps->o.unredir_if_possible && is_highest) {
|
||||||
if (w->mode == WMODE_SOLID && !ps->o.force_win_blend &&
|
if (w->mode == WMODE_SOLID && !ps->o.force_win_blend &&
|
||||||
win_is_fullscreen(ps, w) && !w->unredir_if_possible_excluded) {
|
w->is_fullscreen && !w->unredir_if_possible_excluded) {
|
||||||
unredir_possible = true;
|
unredir_possible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1318,14 +1345,43 @@ void root_damaged(session_t *ps) {
|
|||||||
}
|
}
|
||||||
auto pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms);
|
auto pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms);
|
||||||
if (pixmap != XCB_NONE) {
|
if (pixmap != XCB_NONE) {
|
||||||
|
xcb_get_geometry_reply_t *r = xcb_get_geometry_reply(
|
||||||
|
ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL);
|
||||||
|
if (!r) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We used to assume that pixmaps pointed by the root background
|
||||||
|
// pixmap atoms are owned by the root window and have the same
|
||||||
|
// depth and hence the same visual that we can use to bind them.
|
||||||
|
// However, some applications break this assumption, e.g. the
|
||||||
|
// Xfce's desktop manager xfdesktop that sets the _XROOTPMAP_ID
|
||||||
|
// atom to a pixmap owned by it that seems to always have 32 bpp
|
||||||
|
// depth when the common root window's depth is 24 bpp. So use the
|
||||||
|
// root window's visual only if the root background pixmap's depth
|
||||||
|
// matches the root window's depth. Otherwise, find a suitable
|
||||||
|
// visual for the root background pixmap's depth and use it.
|
||||||
|
//
|
||||||
|
// We can't obtain a suitable visual for the root background
|
||||||
|
// pixmap the same way as the win_bind_pixmap function because it
|
||||||
|
// requires a window and we have only a pixmap. We also can't not
|
||||||
|
// bind the root background pixmap in case of depth mismatch
|
||||||
|
// because some options rely on it's content, e.g.
|
||||||
|
// transparent-clipping.
|
||||||
|
xcb_visualid_t visual =
|
||||||
|
r->depth == ps->c.screen_info->root_depth
|
||||||
|
? ps->c.screen_info->root_visual
|
||||||
|
: x_get_visual_for_depth(ps->c.screen_info, r->depth);
|
||||||
|
free(r);
|
||||||
|
|
||||||
ps->root_image = ps->backend_data->ops->bind_pixmap(
|
ps->root_image = ps->backend_data->ops->bind_pixmap(
|
||||||
ps->backend_data, pixmap,
|
ps->backend_data, pixmap, x_get_visual_info(&ps->c, visual), false);
|
||||||
x_get_visual_info(&ps->c, ps->c.screen_info->root_visual), false);
|
|
||||||
if (ps->root_image) {
|
if (ps->root_image) {
|
||||||
ps->backend_data->ops->set_image_property(
|
ps->backend_data->ops->set_image_property(
|
||||||
ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE,
|
ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE,
|
||||||
ps->root_image, (int[]){ps->root_width, ps->root_height});
|
ps->root_image, (int[]){ps->root_width, ps->root_height});
|
||||||
} else {
|
} else {
|
||||||
|
err:
|
||||||
log_error("Failed to bind root back pixmap");
|
log_error("Failed to bind root back pixmap");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1445,11 +1501,10 @@ static int register_cm(session_t *ps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set COMPTON_VERSION
|
// Set COMPTON_VERSION
|
||||||
e = xcb_request_check(
|
e = xcb_request_check(ps->c.c, xcb_change_property_checked(
|
||||||
ps->c.c, xcb_change_property_checked(
|
ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
||||||
ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
ps->atoms->aCOMPTON_VERSION, XCB_ATOM_STRING, 8,
|
||||||
get_atom(ps->atoms, "COMPTON_VERSION"), XCB_ATOM_STRING, 8,
|
(uint32_t)strlen(PICOM_VERSION), PICOM_VERSION));
|
||||||
(uint32_t)strlen(PICOM_VERSION), PICOM_VERSION));
|
|
||||||
if (e) {
|
if (e) {
|
||||||
log_error_x_error(e, "Failed to set COMPTON_VERSION.");
|
log_error_x_error(e, "Failed to set COMPTON_VERSION.");
|
||||||
free(e);
|
free(e);
|
||||||
@@ -1465,7 +1520,7 @@ static int register_cm(session_t *ps) {
|
|||||||
log_fatal("Failed to allocate memory");
|
log_fatal("Failed to allocate memory");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
atom = get_atom(ps->atoms, buf);
|
atom = get_atom(ps->atoms, buf, ps->c.c);
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
||||||
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(
|
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(
|
||||||
@@ -1629,7 +1684,7 @@ static bool redirect_start(session_t *ps) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
x_sync(&ps->c);
|
xcb_aux_sync(ps->c.c);
|
||||||
|
|
||||||
if (!initialize_backend(ps)) {
|
if (!initialize_backend(ps)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1648,8 +1703,7 @@ static bool redirect_start(session_t *ps) {
|
|||||||
pixman_region32_init(&ps->damage_ring[i]);
|
pixman_region32_init(&ps->damage_ring[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ps->frame_pacing = !ps->o.no_frame_pacing;
|
ps->frame_pacing = !ps->o.no_frame_pacing && ps->o.vsync;
|
||||||
// if ((ps->o.legacy_backends || ps->o.animations || ps->o.benchmark || !ps->backend_data->ops->last_render_time) &&
|
|
||||||
if ((ps->o.legacy_backends || ps->o.benchmark || !ps->backend_data->ops->last_render_time) &&
|
if ((ps->o.legacy_backends || ps->o.benchmark || !ps->backend_data->ops->last_render_time) &&
|
||||||
ps->frame_pacing) {
|
ps->frame_pacing) {
|
||||||
// Disable frame pacing if we are using a legacy backend or if we are in
|
// Disable frame pacing if we are using a legacy backend or if we are in
|
||||||
@@ -1689,7 +1743,7 @@ static bool redirect_start(session_t *ps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Must call XSync() here
|
// Must call XSync() here
|
||||||
x_sync(&ps->c);
|
xcb_aux_sync(ps->c.c);
|
||||||
|
|
||||||
ps->redirected = true;
|
ps->redirected = true;
|
||||||
ps->first_frame = true;
|
ps->first_frame = true;
|
||||||
@@ -1732,15 +1786,38 @@ static void unredirect(session_t *ps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Must call XSync() here
|
// Must call XSync() here
|
||||||
x_sync(&ps->c);
|
xcb_aux_sync(ps->c.c);
|
||||||
|
|
||||||
ps->redirected = false;
|
ps->redirected = false;
|
||||||
log_debug("Screen unredirected.");
|
log_debug("Screen unredirected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle queued events before we go to sleep
|
/// Handle queued events before we go to sleep.
|
||||||
|
///
|
||||||
|
/// This function is called by ev_prepare watcher, which is called just before
|
||||||
|
/// the event loop goes to sleep. X damage events are incremental, which means
|
||||||
|
/// if we don't handle the ones X server already sent us, we won't get new ones.
|
||||||
|
/// And if we don't get new ones, we won't render, i.e. we would freeze. libxcb
|
||||||
|
/// keeps an internal queue of events, so we have to be 100% sure no events are
|
||||||
|
/// left in that queue before we go to sleep.
|
||||||
static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents attr_unused) {
|
static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents attr_unused) {
|
||||||
session_t *ps = session_ptr(w, event_check);
|
session_t *ps = session_ptr(w, event_check);
|
||||||
|
// Flush because if we go into sleep when there is still requests in the
|
||||||
|
// outgoing buffer, they will not be sent for an indefinite amount of
|
||||||
|
// time. Use XFlush here too, we might still use some Xlib functions
|
||||||
|
// because OpenGL.
|
||||||
|
//
|
||||||
|
// Also note, after we have flushed here, we won't flush again in this
|
||||||
|
// function before going into sleep. This is because `xcb_flush`/`XFlush`
|
||||||
|
// may _read_ more events from the server (yes, this is ridiculous, I
|
||||||
|
// know). And we can't have that, see the comments above this function.
|
||||||
|
//
|
||||||
|
// This means if functions called ev_handle need to send some events,
|
||||||
|
// they need to carefully make sure those events are flushed, one way or
|
||||||
|
// another.
|
||||||
|
XFlush(ps->c.dpy);
|
||||||
|
xcb_flush(ps->c.c);
|
||||||
|
|
||||||
if (ps->vblank_scheduler) {
|
if (ps->vblank_scheduler) {
|
||||||
vblank_handle_x_events(ps->vblank_scheduler);
|
vblank_handle_x_events(ps->vblank_scheduler);
|
||||||
}
|
}
|
||||||
@@ -1750,13 +1827,6 @@ static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents
|
|||||||
ev_handle(ps, ev);
|
ev_handle(ps, ev);
|
||||||
free(ev);
|
free(ev);
|
||||||
};
|
};
|
||||||
// Flush because if we go into sleep when there is still
|
|
||||||
// requests in the outgoing buffer, they will not be sent
|
|
||||||
// for an indefinite amount of time.
|
|
||||||
// Use XFlush here too, we might still use some Xlib functions
|
|
||||||
// because OpenGL.
|
|
||||||
XFlush(ps->c.dpy);
|
|
||||||
xcb_flush(ps->c.c);
|
|
||||||
int err = xcb_connection_has_error(ps->c.c);
|
int err = xcb_connection_has_error(ps->c.c);
|
||||||
if (err) {
|
if (err) {
|
||||||
log_fatal("X11 server connection broke (error %d)", err);
|
log_fatal("X11 server connection broke (error %d)", err);
|
||||||
@@ -1999,12 +2069,9 @@ static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
|||||||
|
|
||||||
ps->render_queued = false;
|
ps->render_queued = false;
|
||||||
|
|
||||||
if (ps->vblank_scheduler) {
|
// TODO(yshui) Investigate how big the X critical section needs to be. There are
|
||||||
// Even if we might not want to render during next vblank, we want to keep
|
// suggestions that rendering should be in the critical section as well.
|
||||||
// `backend_busy` up to date, so when the next render comes, we can
|
|
||||||
// immediately know if we can render.
|
|
||||||
vblank_scheduler_schedule(ps->vblank_scheduler, check_render_finish, ps);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_callback(EV_P_ ev_timer *w, int revents) {
|
static void draw_callback(EV_P_ ev_timer *w, int revents) {
|
||||||
@@ -2032,7 +2099,7 @@ static void x_event_callback(EV_P attr_unused, ev_io *w, int revents attr_unused
|
|||||||
/**
|
/**
|
||||||
* Turn on the program reset flag.
|
* Turn on the program reset flag.
|
||||||
*
|
*
|
||||||
* This will result in the compostior resetting itself after next paint.
|
* This will result in the compositor resetting itself after next paint.
|
||||||
*/
|
*/
|
||||||
static void reset_enable(EV_P_ ev_signal *w attr_unused, int revents attr_unused) {
|
static void reset_enable(EV_P_ ev_signal *w attr_unused, int revents attr_unused) {
|
||||||
log_info("picom is resetting...");
|
log_info("picom is resetting...");
|
||||||
@@ -2109,11 +2176,11 @@ static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data)
|
|||||||
/**
|
/**
|
||||||
* Initialize a session.
|
* Initialize a session.
|
||||||
*
|
*
|
||||||
* @param argc number of commandline arguments
|
* @param argc number of command line arguments
|
||||||
* @param argv commandline arguments
|
* @param argv command line arguments
|
||||||
* @param dpy the X Display
|
* @param dpy the X Display
|
||||||
* @param config_file the path to the config file
|
* @param config_file the path to the config file
|
||||||
* @param all_xerros whether we should report all X errors
|
* @param all_xerrors whether we should report all X errors
|
||||||
* @param fork whether we will fork after initialization
|
* @param fork whether we will fork after initialization
|
||||||
*/
|
*/
|
||||||
static session_t *session_init(int argc, char **argv, Display *dpy,
|
static session_t *session_init(int argc, char **argv, Display *dpy,
|
||||||
@@ -2387,6 +2454,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||||||
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.rounded_corners_blacklist) &&
|
||||||
c2_list_postprocess(ps, ps->o.focus_blacklist) &&
|
c2_list_postprocess(ps, ps->o.focus_blacklist) &&
|
||||||
|
c2_list_postprocess(ps, ps->o.corner_radius_rules) &&
|
||||||
c2_list_postprocess(ps, ps->o.animation_blacklist))) {
|
c2_list_postprocess(ps, ps->o.animation_blacklist))) {
|
||||||
log_error("Post-processing of conditionals failed, some of your rules "
|
log_error("Post-processing of conditionals failed, some of your rules "
|
||||||
"might not work");
|
"might not work");
|
||||||
@@ -2665,7 +2733,7 @@ static session_t *session_init(int argc, char **argv, Display *dpy,
|
|||||||
ps->server_grabbed = true;
|
ps->server_grabbed = true;
|
||||||
|
|
||||||
// We are going to pull latest information from X server now, events sent by X
|
// We are going to pull latest information from X server now, events sent by X
|
||||||
// earlier is irrelavant at this point.
|
// earlier is irrelevant at this point.
|
||||||
// A better solution is probably grabbing the server from the very start. But I
|
// A better solution is probably grabbing the server from the very start. But I
|
||||||
// think there still could be race condition that mandates discarding the events.
|
// think there still could be race condition that mandates discarding the events.
|
||||||
x_discard_events(&ps->c);
|
x_discard_events(&ps->c);
|
||||||
@@ -2726,21 +2794,24 @@ void set_rr_scheduling(void) {
|
|||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
struct sched_param param;
|
struct sched_param param;
|
||||||
|
int old_policy;
|
||||||
ret = sched_getparam(0, ¶m);
|
ret = pthread_getschedparam(pthread_self(), &old_policy, ¶m);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
log_debug("Failed to get old scheduling priority");
|
log_debug("Failed to get old scheduling priority");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
param.sched_priority = priority;
|
param.sched_priority = priority;
|
||||||
ret = sched_setscheduler(0, SCHED_RR, ¶m);
|
|
||||||
|
ret = pthread_setschedparam(pthread_self(), SCHED_RR, ¶m);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
log_info("Failed to set real-time scheduling priority to %d. Consider "
|
log_info("Failed to set real-time scheduling priority to %d. Consider "
|
||||||
"giving picom the CAP_SYS_NICE capability",
|
"giving picom the CAP_SYS_NICE capability or equivalent "
|
||||||
|
"support.",
|
||||||
priority);
|
priority);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("Set real-time scheduling priority to %d", priority);
|
log_info("Set real-time scheduling priority to %d", priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2757,11 +2828,6 @@ static void session_destroy(session_t *ps) {
|
|||||||
unredirect(ps);
|
unredirect(ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
|
||||||
free(ps->argb_fbconfig);
|
|
||||||
ps->argb_fbconfig = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
file_watch_destroy(ps->loop, ps->file_watch_handle);
|
file_watch_destroy(ps->loop, ps->file_watch_handle);
|
||||||
ps->file_watch_handle = NULL;
|
ps->file_watch_handle = NULL;
|
||||||
|
|
||||||
@@ -2804,6 +2870,7 @@ static void session_destroy(session_t *ps) {
|
|||||||
c2_list_free(&ps->o.paint_blacklist, NULL);
|
c2_list_free(&ps->o.paint_blacklist, NULL);
|
||||||
c2_list_free(&ps->o.unredir_if_possible_blacklist, NULL);
|
c2_list_free(&ps->o.unredir_if_possible_blacklist, NULL);
|
||||||
c2_list_free(&ps->o.rounded_corners_blacklist, NULL);
|
c2_list_free(&ps->o.rounded_corners_blacklist, NULL);
|
||||||
|
c2_list_free(&ps->o.corner_radius_rules, NULL);
|
||||||
c2_list_free(&ps->o.window_shader_fg_rules, free);
|
c2_list_free(&ps->o.window_shader_fg_rules, free);
|
||||||
|
|
||||||
// Free tracked atom list
|
// Free tracked atom list
|
||||||
@@ -2905,7 +2972,7 @@ static void session_destroy(session_t *ps) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Flush all events
|
// Flush all events
|
||||||
x_sync(&ps->c);
|
xcb_aux_sync(ps->c.c);
|
||||||
ev_io_stop(ps->loop, &ps->xiow);
|
ev_io_stop(ps->loop, &ps->xiow);
|
||||||
if (ps->o.legacy_backends) {
|
if (ps->o.legacy_backends) {
|
||||||
free_conv((conv *)ps->shadow_context);
|
free_conv((conv *)ps->shadow_context);
|
||||||
|
|||||||
60
src/render.c
60
src/render.c
@@ -6,6 +6,7 @@
|
|||||||
#include <xcb/composite.h>
|
#include <xcb/composite.h>
|
||||||
#include <xcb/render.h>
|
#include <xcb/render.h>
|
||||||
#include <xcb/sync.h>
|
#include <xcb/sync.h>
|
||||||
|
#include <xcb/xcb_aux.h>
|
||||||
#include <xcb/xcb_image.h>
|
#include <xcb/xcb_image.h>
|
||||||
#include <xcb/xcb_renderutil.h>
|
#include <xcb/xcb_renderutil.h>
|
||||||
|
|
||||||
@@ -55,19 +56,20 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
|
|||||||
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 (!ps->argb_fbconfig.cfg) {
|
||||||
ps->argb_fbconfig = glx_find_fbconfig(
|
glx_find_fbconfig(&ps->c,
|
||||||
&ps->c, (struct xvisual_info){.red_size = 8,
|
(struct xvisual_info){.red_size = 8,
|
||||||
.green_size = 8,
|
.green_size = 8,
|
||||||
.blue_size = 8,
|
.blue_size = 8,
|
||||||
.alpha_size = 8,
|
.alpha_size = 8,
|
||||||
.visual_depth = 32});
|
.visual_depth = 32},
|
||||||
|
&ps->argb_fbconfig);
|
||||||
}
|
}
|
||||||
if (!ps->argb_fbconfig) {
|
if (!ps->argb_fbconfig.cfg) {
|
||||||
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 = &ps->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) {
|
||||||
@@ -79,17 +81,17 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ppaint->fbcfg) {
|
if (!ppaint->fbcfg.cfg) {
|
||||||
ppaint->fbcfg = glx_find_fbconfig(&ps->c, m);
|
glx_find_fbconfig(&ps->c, m, &ppaint->fbcfg);
|
||||||
}
|
}
|
||||||
if (!ppaint->fbcfg) {
|
if (!ppaint->fbcfg.cfg) {
|
||||||
log_error("Failed to find appropriate FBConfig for X pixmap");
|
log_error("Failed to find appropriate FBConfig for X pixmap");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fbcfg = ppaint->fbcfg;
|
fbcfg = &ppaint->fbcfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) {
|
if (force || !glx_tex_bound(ppaint->ptex, ppaint->pixmap)) {
|
||||||
return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei,
|
return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei,
|
||||||
repeat, fbcfg);
|
repeat, fbcfg);
|
||||||
}
|
}
|
||||||
@@ -378,7 +380,7 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, XCB_NONE)) {
|
if (BKEND_GLX == ps->o.backend && !glx_tex_bound(ppaint->ptex, XCB_NONE)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -604,20 +606,27 @@ static bool get_root_tile(session_t *ps) {
|
|||||||
bool fill = false;
|
bool fill = false;
|
||||||
xcb_pixmap_t pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms);
|
xcb_pixmap_t pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms);
|
||||||
|
|
||||||
// Make sure the pixmap we got is valid
|
xcb_get_geometry_reply_t *r;
|
||||||
if (pixmap && !x_validate_pixmap(&ps->c, pixmap)) {
|
if (pixmap) {
|
||||||
pixmap = XCB_NONE;
|
r = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a pixmap if there isn't any
|
// Create a pixmap if there isn't any
|
||||||
if (!pixmap) {
|
xcb_visualid_t visual;
|
||||||
|
if (!pixmap || !r) {
|
||||||
pixmap =
|
pixmap =
|
||||||
x_create_pixmap(&ps->c, (uint8_t)ps->c.screen_info->root_depth, 1, 1);
|
x_create_pixmap(&ps->c, (uint8_t)ps->c.screen_info->root_depth, 1, 1);
|
||||||
if (pixmap == XCB_NONE) {
|
if (pixmap == XCB_NONE) {
|
||||||
log_error("Failed to create pixmaps for root tile.");
|
log_error("Failed to create pixmaps for root tile.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
visual = ps->c.screen_info->root_visual;
|
||||||
fill = true;
|
fill = true;
|
||||||
|
} else {
|
||||||
|
visual = r->depth == ps->c.screen_info->root_depth
|
||||||
|
? ps->c.screen_info->root_visual
|
||||||
|
: x_get_visual_for_depth(ps->c.screen_info, r->depth);
|
||||||
|
free(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Picture
|
// Create Picture
|
||||||
@@ -625,7 +634,7 @@ static bool get_root_tile(session_t *ps) {
|
|||||||
.repeat = true,
|
.repeat = true,
|
||||||
};
|
};
|
||||||
ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap(
|
ps->root_tile_paint.pict = x_create_picture_with_visual_and_pixmap(
|
||||||
&ps->c, ps->c.screen_info->root_visual, pixmap, XCB_RENDER_CP_REPEAT, &pa);
|
&ps->c, visual, pixmap, XCB_RENDER_CP_REPEAT, &pa);
|
||||||
|
|
||||||
// Fill pixmap if needed
|
// Fill pixmap if needed
|
||||||
if (fill) {
|
if (fill) {
|
||||||
@@ -646,8 +655,7 @@ static bool get_root_tile(session_t *ps) {
|
|||||||
ps->root_tile_paint.pixmap = pixmap;
|
ps->root_tile_paint.pixmap = pixmap;
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
if (BKEND_GLX == ps->o.backend) {
|
if (BKEND_GLX == ps->o.backend) {
|
||||||
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0,
|
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, visual, false);
|
||||||
ps->c.screen_info->root_visual, false);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1223,7 +1231,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
|||||||
if (ps->o.vsync) {
|
if (ps->o.vsync) {
|
||||||
// Make sure all previous requests are processed to achieve best
|
// Make sure all previous requests are processed to achieve best
|
||||||
// effect
|
// effect
|
||||||
x_sync(&ps->c);
|
xcb_aux_sync(ps->c.c);
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
if (glx_has_context(ps)) {
|
if (glx_has_context(ps)) {
|
||||||
if (ps->o.vsync_use_glfinish) {
|
if (ps->o.vsync_use_glfinish) {
|
||||||
@@ -1282,7 +1290,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
|||||||
break;
|
break;
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
case BKEND_XR_GLX_HYBRID:
|
case BKEND_XR_GLX_HYBRID:
|
||||||
x_sync(&ps->c);
|
xcb_aux_sync(ps->c.c);
|
||||||
if (ps->o.vsync_use_glfinish) {
|
if (ps->o.vsync_use_glfinish) {
|
||||||
glFinish();
|
glFinish();
|
||||||
} else {
|
} else {
|
||||||
@@ -1307,7 +1315,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
|||||||
default: assert(0);
|
default: assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
x_sync(&ps->c);
|
xcb_aux_sync(ps->c.c);
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
if (glx_has_context(ps)) {
|
if (glx_has_context(ps)) {
|
||||||
@@ -1522,7 +1530,7 @@ void deinit_render(session_t *ps) {
|
|||||||
free_root_tile(ps);
|
free_root_tile(ps);
|
||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
free(ps->root_tile_paint.fbcfg);
|
ps->root_tile_paint.fbcfg = (struct glx_fbconfig_info){0};
|
||||||
if (bkend_use_glx(ps)) {
|
if (bkend_use_glx(ps)) {
|
||||||
glx_destroy(ps);
|
glx_destroy(ps);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ typedef struct paint {
|
|||||||
xcb_render_picture_t pict;
|
xcb_render_picture_t pict;
|
||||||
glx_texture_t *ptex;
|
glx_texture_t *ptex;
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
struct glx_fbconfig_info *fbcfg;
|
struct glx_fbconfig_info fbcfg;
|
||||||
#endif
|
#endif
|
||||||
} paint_t;
|
} paint_t;
|
||||||
|
|
||||||
|
|||||||
@@ -51,10 +51,6 @@ void render_statistics_add_render_time_sample(struct render_statistics *rs, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// How much time budget we should give to the backend for rendering, in microseconds.
|
/// How much time budget we should give to the backend for rendering, in microseconds.
|
||||||
///
|
|
||||||
/// A `divisor` is also returned, indicating the target framerate. The divisor is
|
|
||||||
/// the number of vblanks we should wait between each frame. A divisor of 1 means
|
|
||||||
/// full framerate, 2 means half framerate, etc.
|
|
||||||
unsigned int render_statistics_get_budget(struct render_statistics *rs) {
|
unsigned int render_statistics_get_budget(struct render_statistics *rs) {
|
||||||
if (rs->render_times.nelem < rs->render_times.window_size) {
|
if (rs->render_times.nelem < rs->render_times.window_size) {
|
||||||
// No valid render time estimates yet. Assume maximum budget.
|
// No valid render time estimates yet. Assume maximum budget.
|
||||||
|
|||||||
@@ -141,10 +141,10 @@ void rolling_max_pop_front(struct rolling_max *rm, int front) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void rolling_max_push_back(struct rolling_max *rm, int val) {
|
void rolling_max_push_back(struct rolling_max *rm, int val) {
|
||||||
// Update the prority queue.
|
// Update the priority queue.
|
||||||
// Remove all elements smaller than the new element from the queue. Because
|
// Remove all elements smaller than the new element from the queue. Because
|
||||||
// the new element will become the maximum element before them, and since they
|
// the new element will become the maximum element before them, and since they
|
||||||
// come b1efore the new element, they will have been popped before the new
|
// come before the new element, they will have been popped before the new
|
||||||
// element, so they will never become the maximum element.
|
// element, so they will never become the maximum element.
|
||||||
while (rm->np) {
|
while (rm->np) {
|
||||||
int p_tail = IDX(rm->p_head + rm->np - 1);
|
int p_tail = IDX(rm->p_head + rm->np - 1);
|
||||||
|
|||||||
29
src/utils.h
29
src/utils.h
@@ -59,11 +59,11 @@ safe_isnan(double a) {
|
|||||||
/// being always true or false.
|
/// being always true or false.
|
||||||
#define ASSERT_IN_RANGE(var, lower, upper) \
|
#define ASSERT_IN_RANGE(var, lower, upper) \
|
||||||
do { \
|
do { \
|
||||||
auto __tmp attr_unused = (var); \
|
auto __assert_in_range_tmp attr_unused = (var); \
|
||||||
_Pragma("GCC diagnostic push"); \
|
_Pragma("GCC diagnostic push"); \
|
||||||
_Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
|
_Pragma("GCC diagnostic ignored \"-Wtype-limits\""); \
|
||||||
assert(__tmp >= lower); \
|
assert(__assert_in_range_tmp >= lower); \
|
||||||
assert(__tmp <= upper); \
|
assert(__assert_in_range_tmp <= upper); \
|
||||||
_Pragma("GCC diagnostic pop"); \
|
_Pragma("GCC diagnostic pop"); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
@@ -113,11 +113,26 @@ safe_isnan(double a) {
|
|||||||
#define to_u32_checked(val) \
|
#define to_u32_checked(val) \
|
||||||
({ \
|
({ \
|
||||||
auto __to_tmp = (val); \
|
auto __to_tmp = (val); \
|
||||||
int64_t max attr_unused = UINT32_MAX; /* silence clang tautological \
|
int64_t __to_u32_max attr_unused = UINT32_MAX; /* silence clang \
|
||||||
comparison warning*/ \
|
tautological \
|
||||||
ASSERT_IN_RANGE(__to_tmp, 0, max); \
|
comparison warning */ \
|
||||||
|
ASSERT_IN_RANGE(__to_tmp, 0, __to_u32_max); \
|
||||||
(uint32_t) __to_tmp; \
|
(uint32_t) __to_tmp; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* container_of - cast a member of a structure out to the containing structure
|
||||||
|
* @ptr: the pointer to the member.
|
||||||
|
* @type: the type of the container struct this is embedded in.
|
||||||
|
* @member: the name of the member within the struct.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define container_of(ptr, type, member) \
|
||||||
|
({ \
|
||||||
|
const __typeof__(((type *)0)->member) *__mptr = (ptr); \
|
||||||
|
(type *)((char *)__mptr - offsetof(type, member)); \
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize an int value to a specific range.
|
* Normalize an int value to a specific range.
|
||||||
*
|
*
|
||||||
@@ -236,7 +251,7 @@ allocchk_(const char *func_name, const char *file, unsigned int line, void *ptr)
|
|||||||
((type *)allocchk(calloc((size_t)tmp, sizeof(type)))); \
|
((type *)allocchk(calloc((size_t)tmp, sizeof(type)))); \
|
||||||
})
|
})
|
||||||
|
|
||||||
/// @brief Wrapper of ealloc().
|
/// @brief Wrapper of realloc().
|
||||||
#define crealloc(ptr, nmemb) \
|
#define crealloc(ptr, nmemb) \
|
||||||
({ \
|
({ \
|
||||||
auto tmp = (nmemb); \
|
auto tmp = (nmemb); \
|
||||||
|
|||||||
127
src/vblank.c
127
src/vblank.c
@@ -11,14 +11,13 @@
|
|||||||
|
|
||||||
#ifdef CONFIG_OPENGL
|
#ifdef CONFIG_OPENGL
|
||||||
// Enable sgi_video_sync_vblank_scheduler
|
// Enable sgi_video_sync_vblank_scheduler
|
||||||
#include <GL/glx.h>
|
|
||||||
#include <X11/X.h>
|
#include <X11/X.h>
|
||||||
#include <X11/Xlib-xcb.h>
|
#include <X11/Xlib-xcb.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
|
#include <epoxy/glx.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
#include "backend/gl/glx.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
@@ -27,7 +26,7 @@
|
|||||||
#include "vblank.h"
|
#include "vblank.h"
|
||||||
#include "x.h"
|
#include "x.h"
|
||||||
|
|
||||||
struct vblank_callback {
|
struct vblank_closure {
|
||||||
vblank_callback_t fn;
|
vblank_callback_t fn;
|
||||||
void *user_data;
|
void *user_data;
|
||||||
};
|
};
|
||||||
@@ -37,7 +36,7 @@ struct vblank_callback {
|
|||||||
struct vblank_scheduler {
|
struct vblank_scheduler {
|
||||||
struct x_connection *c;
|
struct x_connection *c;
|
||||||
size_t callback_capacity, callback_count;
|
size_t callback_capacity, callback_count;
|
||||||
struct vblank_callback *callbacks;
|
struct vblank_closure *callbacks;
|
||||||
struct ev_loop *loop;
|
struct ev_loop *loop;
|
||||||
/// Request extra vblank events even when no callbacks are scheduled.
|
/// Request extra vblank events even when no callbacks are scheduled.
|
||||||
/// This is because when callbacks are scheduled too close to a vblank,
|
/// This is because when callbacks are scheduled too close to a vblank,
|
||||||
@@ -63,9 +62,9 @@ struct present_vblank_scheduler {
|
|||||||
|
|
||||||
struct vblank_scheduler_ops {
|
struct vblank_scheduler_ops {
|
||||||
size_t size;
|
size_t size;
|
||||||
void (*init)(struct vblank_scheduler *self);
|
bool (*init)(struct vblank_scheduler *self);
|
||||||
void (*deinit)(struct vblank_scheduler *self);
|
void (*deinit)(struct vblank_scheduler *self);
|
||||||
void (*schedule)(struct vblank_scheduler *self);
|
bool (*schedule)(struct vblank_scheduler *self);
|
||||||
bool (*handle_x_events)(struct vblank_scheduler *self);
|
bool (*handle_x_events)(struct vblank_scheduler *self);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,13 +77,14 @@ struct sgi_video_sync_vblank_scheduler {
|
|||||||
|
|
||||||
// Since glXWaitVideoSyncSGI blocks, we need to run it in a separate thread.
|
// Since glXWaitVideoSyncSGI blocks, we need to run it in a separate thread.
|
||||||
// ... and all the thread shenanigans that come with it.
|
// ... and all the thread shenanigans that come with it.
|
||||||
_Atomic unsigned int last_msc;
|
_Atomic unsigned int current_msc;
|
||||||
_Atomic uint64_t last_ust;
|
_Atomic uint64_t current_ust;
|
||||||
ev_async notify;
|
ev_async notify;
|
||||||
pthread_t sync_thread;
|
pthread_t sync_thread;
|
||||||
bool running, error;
|
bool running, error;
|
||||||
|
unsigned int last_msc;
|
||||||
|
|
||||||
/// Protects `running`, `error` and `base.vblank_event_requested`
|
/// Protects `running`, and `base.vblank_event_requested`
|
||||||
pthread_mutex_t vblank_requested_mtx;
|
pthread_mutex_t vblank_requested_mtx;
|
||||||
pthread_cond_t vblank_requested_cnd;
|
pthread_cond_t vblank_requested_cnd;
|
||||||
};
|
};
|
||||||
@@ -110,11 +110,6 @@ static bool check_sgi_video_sync_extension(Display *dpy, int screen) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)(void *)glXGetProcAddress(
|
|
||||||
(const GLubyte *)"glXWaitVideoSyncSGI");
|
|
||||||
if (!glXWaitVideoSyncSGI) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,8 +202,8 @@ static void *sgi_video_sync_thread(void *data) {
|
|||||||
|
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
atomic_store(&self->last_msc, last_msc);
|
atomic_store(&self->current_msc, last_msc);
|
||||||
atomic_store(&self->last_ust,
|
atomic_store(&self->current_ust,
|
||||||
(uint64_t)(now.tv_sec * 1000000 + now.tv_nsec / 1000));
|
(uint64_t)(now.tv_sec * 1000000 + now.tv_nsec / 1000));
|
||||||
ev_async_send(self->base.loop, &self->notify);
|
ev_async_send(self->base.loop, &self->notify);
|
||||||
pthread_mutex_lock(&self->vblank_requested_mtx);
|
pthread_mutex_lock(&self->vblank_requested_mtx);
|
||||||
@@ -239,34 +234,30 @@ cleanup:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) {
|
static bool sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) {
|
||||||
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
|
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
|
||||||
log_verbose("Requesting vblank event for msc %d", self->last_msc + 1);
|
if (self->error) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log_verbose("Requesting vblank event for msc %d", self->current_msc + 1);
|
||||||
pthread_mutex_lock(&self->vblank_requested_mtx);
|
pthread_mutex_lock(&self->vblank_requested_mtx);
|
||||||
assert(!base->vblank_event_requested);
|
assert(!base->vblank_event_requested);
|
||||||
base->vblank_event_requested = true;
|
base->vblank_event_requested = true;
|
||||||
pthread_cond_signal(&self->vblank_requested_cnd);
|
pthread_cond_signal(&self->vblank_requested_cnd);
|
||||||
pthread_mutex_unlock(&self->vblank_requested_mtx);
|
pthread_mutex_unlock(&self->vblank_requested_mtx);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) {
|
sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents);
|
||||||
auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify);
|
|
||||||
auto event = (struct vblank_event){
|
|
||||||
.msc = atomic_load(&sched->last_msc),
|
|
||||||
.ust = atomic_load(&sched->last_ust),
|
|
||||||
};
|
|
||||||
sched->base.vblank_event_requested = false;
|
|
||||||
log_verbose("Received vblank event for msc %lu", event.msc);
|
|
||||||
vblank_scheduler_invoke_callbacks(&sched->base, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
|
static bool sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
|
||||||
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
|
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
|
||||||
auto args = (struct sgi_video_sync_thread_args){
|
auto args = (struct sgi_video_sync_thread_args){
|
||||||
.self = self,
|
.self = self,
|
||||||
.start_status = -1,
|
.start_status = -1,
|
||||||
};
|
};
|
||||||
|
bool succeeded = true;
|
||||||
pthread_mutex_init(&args.start_mtx, NULL);
|
pthread_mutex_init(&args.start_mtx, NULL);
|
||||||
pthread_cond_init(&args.start_cnd, NULL);
|
pthread_cond_init(&args.start_cnd, NULL);
|
||||||
|
|
||||||
@@ -286,11 +277,15 @@ static void sgi_video_sync_scheduler_init(struct vblank_scheduler *base) {
|
|||||||
if (args.start_status != 0) {
|
if (args.start_status != 0) {
|
||||||
log_fatal("Failed to start sgi_video_sync_thread, error code: %d",
|
log_fatal("Failed to start sgi_video_sync_thread, error code: %d",
|
||||||
args.start_status);
|
args.start_status);
|
||||||
abort();
|
succeeded = false;
|
||||||
|
} else {
|
||||||
|
log_info("Started sgi_video_sync_thread");
|
||||||
}
|
}
|
||||||
|
self->error = !succeeded;
|
||||||
|
self->last_msc = 0;
|
||||||
pthread_mutex_destroy(&args.start_mtx);
|
pthread_mutex_destroy(&args.start_mtx);
|
||||||
pthread_cond_destroy(&args.start_cnd);
|
pthread_cond_destroy(&args.start_cnd);
|
||||||
log_info("Started sgi_video_sync_thread");
|
return succeeded;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) {
|
static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) {
|
||||||
@@ -306,15 +301,45 @@ static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) {
|
|||||||
pthread_mutex_destroy(&self->vblank_requested_mtx);
|
pthread_mutex_destroy(&self->vblank_requested_mtx);
|
||||||
pthread_cond_destroy(&self->vblank_requested_cnd);
|
pthread_cond_destroy(&self->vblank_requested_cnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sgi_video_sync_scheduler_callback(EV_P attr_unused, ev_async *w, int attr_unused revents) {
|
||||||
|
auto sched = container_of(w, struct sgi_video_sync_vblank_scheduler, notify);
|
||||||
|
auto msc = atomic_load(&sched->current_msc);
|
||||||
|
if (sched->last_msc == msc) {
|
||||||
|
// NVIDIA spams us with duplicate vblank events after a suspend/resume
|
||||||
|
// cycle. Recreating the X connection and GLX context seems to fix this.
|
||||||
|
// Oh NVIDIA.
|
||||||
|
log_warn("Duplicate vblank event found with msc %d. Possible NVIDIA bug?", msc);
|
||||||
|
log_warn("Resetting the vblank scheduler");
|
||||||
|
sgi_video_sync_scheduler_deinit(&sched->base);
|
||||||
|
sched->base.vblank_event_requested = false;
|
||||||
|
if (!sgi_video_sync_scheduler_init(&sched->base)) {
|
||||||
|
log_error("Failed to reset the vblank scheduler");
|
||||||
|
} else {
|
||||||
|
sgi_video_sync_scheduler_schedule(&sched->base);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto event = (struct vblank_event){
|
||||||
|
.msc = msc,
|
||||||
|
.ust = atomic_load(&sched->current_ust),
|
||||||
|
};
|
||||||
|
sched->base.vblank_event_requested = false;
|
||||||
|
sched->last_msc = msc;
|
||||||
|
log_verbose("Received vblank event for msc %" PRIu64, event.msc);
|
||||||
|
vblank_scheduler_invoke_callbacks(&sched->base, &event);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void present_vblank_scheduler_schedule(struct vblank_scheduler *base) {
|
static bool present_vblank_scheduler_schedule(struct vblank_scheduler *base) {
|
||||||
auto self = (struct present_vblank_scheduler *)base;
|
auto self = (struct present_vblank_scheduler *)base;
|
||||||
log_verbose("Requesting vblank event for window 0x%08x, msc %" PRIu64,
|
log_verbose("Requesting vblank event for window 0x%08x, msc %" PRIu64,
|
||||||
base->target_window, self->last_msc + 1);
|
base->target_window, self->last_msc + 1);
|
||||||
assert(!base->vblank_event_requested);
|
assert(!base->vblank_event_requested);
|
||||||
x_request_vblank_event(base->c, base->target_window, self->last_msc + 1);
|
x_request_vblank_event(base->c, base->target_window, self->last_msc + 1);
|
||||||
base->vblank_event_requested = true;
|
base->vblank_event_requested = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void present_vblank_callback(EV_P attr_unused, ev_timer *w, int attr_unused revents) {
|
static void present_vblank_callback(EV_P attr_unused, ev_timer *w, int attr_unused revents) {
|
||||||
@@ -327,7 +352,7 @@ static void present_vblank_callback(EV_P attr_unused, ev_timer *w, int attr_unus
|
|||||||
vblank_scheduler_invoke_callbacks(&sched->base, &event);
|
vblank_scheduler_invoke_callbacks(&sched->base, &event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void present_vblank_scheduler_init(struct vblank_scheduler *base) {
|
static bool present_vblank_scheduler_init(struct vblank_scheduler *base) {
|
||||||
auto self = (struct present_vblank_scheduler *)base;
|
auto self = (struct present_vblank_scheduler *)base;
|
||||||
base->type = VBLANK_SCHEDULER_PRESENT;
|
base->type = VBLANK_SCHEDULER_PRESENT;
|
||||||
ev_timer_init(&self->callback_timer, present_vblank_callback, 0, 0);
|
ev_timer_init(&self->callback_timer, present_vblank_callback, 0, 0);
|
||||||
@@ -339,6 +364,7 @@ static void present_vblank_scheduler_init(struct vblank_scheduler *base) {
|
|||||||
set_cant_fail_cookie(base->c, select_input);
|
set_cant_fail_cookie(base->c, select_input);
|
||||||
self->event =
|
self->event =
|
||||||
xcb_register_for_special_xge(base->c->c, &xcb_present_id, self->event_id, NULL);
|
xcb_register_for_special_xge(base->c->c, &xcb_present_id, self->event_id, NULL);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void present_vblank_scheduler_deinit(struct vblank_scheduler *base) {
|
static void present_vblank_scheduler_deinit(struct vblank_scheduler *base) {
|
||||||
@@ -385,7 +411,7 @@ static void handle_present_complete_notify(struct present_vblank_scheduler *self
|
|||||||
auto now_us = (unsigned long)(now.tv_sec * 1000000L + now.tv_nsec / 1000);
|
auto now_us = (unsigned long)(now.tv_sec * 1000000L + now.tv_nsec / 1000);
|
||||||
double delay_sec = 0.0;
|
double delay_sec = 0.0;
|
||||||
if (now_us < cne->ust) {
|
if (now_us < cne->ust) {
|
||||||
log_trace("The end of this vblank is %lu us into the "
|
log_trace("The end of this vblank is %" PRIu64 " us into the "
|
||||||
"future",
|
"future",
|
||||||
cne->ust - now_us);
|
cne->ust - now_us);
|
||||||
delay_sec = (double)(cne->ust - now_us) / 1000000.0;
|
delay_sec = (double)(cne->ust - now_us) / 1000000.0;
|
||||||
@@ -439,17 +465,19 @@ static const struct vblank_scheduler_ops vblank_scheduler_ops[LAST_VBLANK_SCHEDU
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static void vblank_scheduler_schedule_internal(struct vblank_scheduler *self) {
|
static bool vblank_scheduler_schedule_internal(struct vblank_scheduler *self) {
|
||||||
assert(self->type < LAST_VBLANK_SCHEDULER);
|
assert(self->type < LAST_VBLANK_SCHEDULER);
|
||||||
auto fn = vblank_scheduler_ops[self->type].schedule;
|
auto fn = vblank_scheduler_ops[self->type].schedule;
|
||||||
assert(fn != NULL);
|
assert(fn != NULL);
|
||||||
fn(self);
|
return fn(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool vblank_scheduler_schedule(struct vblank_scheduler *self,
|
bool vblank_scheduler_schedule(struct vblank_scheduler *self,
|
||||||
vblank_callback_t vblank_callback, void *user_data) {
|
vblank_callback_t vblank_callback, void *user_data) {
|
||||||
if (self->callback_count == 0 && self->wind_down == 0) {
|
if (self->callback_count == 0 && self->wind_down == 0) {
|
||||||
vblank_scheduler_schedule_internal(self);
|
if (!vblank_scheduler_schedule_internal(self)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (self->callback_count == self->callback_capacity) {
|
if (self->callback_count == self->callback_capacity) {
|
||||||
size_t new_capacity =
|
size_t new_capacity =
|
||||||
@@ -462,7 +490,7 @@ bool vblank_scheduler_schedule(struct vblank_scheduler *self,
|
|||||||
self->callbacks = new_buffer;
|
self->callbacks = new_buffer;
|
||||||
self->callback_capacity = new_capacity;
|
self->callback_capacity = new_capacity;
|
||||||
}
|
}
|
||||||
self->callbacks[self->callback_count++] = (struct vblank_callback){
|
self->callbacks[self->callback_count++] = (struct vblank_closure){
|
||||||
.fn = vblank_callback,
|
.fn = vblank_callback,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
};
|
};
|
||||||
@@ -473,19 +501,30 @@ static void
|
|||||||
vblank_scheduler_invoke_callbacks(struct vblank_scheduler *self, struct vblank_event *event) {
|
vblank_scheduler_invoke_callbacks(struct vblank_scheduler *self, struct vblank_event *event) {
|
||||||
// callbacks might be added during callback invocation, so we need to
|
// callbacks might be added during callback invocation, so we need to
|
||||||
// copy the callback_count.
|
// copy the callback_count.
|
||||||
size_t count = self->callback_count;
|
size_t count = self->callback_count, write_head = 0;
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
self->wind_down--;
|
self->wind_down--;
|
||||||
} else {
|
} else {
|
||||||
self->wind_down = VBLANK_WIND_DOWN;
|
self->wind_down = VBLANK_WIND_DOWN;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
self->callbacks[i].fn(event, self->callbacks[i].user_data);
|
auto action = self->callbacks[i].fn(event, self->callbacks[i].user_data);
|
||||||
|
switch (action) {
|
||||||
|
case VBLANK_CALLBACK_AGAIN:
|
||||||
|
if (i != write_head) {
|
||||||
|
self->callbacks[write_head] = self->callbacks[i];
|
||||||
|
}
|
||||||
|
write_head++;
|
||||||
|
case VBLANK_CALLBACK_DONE:
|
||||||
|
default: // nothing to do
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// remove the callbacks that we have called, keep the newly added ones.
|
memset(self->callbacks + write_head, 0,
|
||||||
memmove(self->callbacks, self->callbacks + count,
|
(count - write_head) * sizeof(*self->callbacks));
|
||||||
(self->callback_count - count) * sizeof(*self->callbacks));
|
assert(count == self->callback_count && "callbacks should not be added when "
|
||||||
self->callback_count -= count;
|
"callbacks are being invoked.");
|
||||||
|
self->callback_count = write_head;
|
||||||
if (self->callback_count || self->wind_down) {
|
if (self->callback_count || self->wind_down) {
|
||||||
vblank_scheduler_schedule_internal(self);
|
vblank_scheduler_schedule_internal(self);
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/vblank.h
10
src/vblank.h
@@ -19,7 +19,15 @@ struct vblank_event {
|
|||||||
uint64_t ust;
|
uint64_t ust;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*vblank_callback_t)(struct vblank_event *event, void *user_data);
|
enum vblank_callback_action {
|
||||||
|
/// The callback should be called again in the next vblank.
|
||||||
|
VBLANK_CALLBACK_AGAIN,
|
||||||
|
/// The callback is done and should not be called again.
|
||||||
|
VBLANK_CALLBACK_DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum vblank_callback_action (*vblank_callback_t)(struct vblank_event *event,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
/// Schedule a vblank event.
|
/// Schedule a vblank event.
|
||||||
///
|
///
|
||||||
|
|||||||
191
src/win.c
191
src/win.c
@@ -73,6 +73,11 @@ static void
|
|||||||
win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client);
|
win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client);
|
||||||
static void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
|
static void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w);
|
||||||
static void win_update_prop_shadow(session_t *ps, struct managed_win *w);
|
static void win_update_prop_shadow(session_t *ps, struct managed_win *w);
|
||||||
|
/**
|
||||||
|
* Update window EWMH fullscreen state.
|
||||||
|
*/
|
||||||
|
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
|
||||||
|
struct managed_win *w);
|
||||||
/**
|
/**
|
||||||
* Update leader of a window.
|
* Update leader of a window.
|
||||||
*/
|
*/
|
||||||
@@ -134,10 +139,10 @@ static inline bool attr_pure win_is_real_visible(const struct managed_win *w) {
|
|||||||
* Update focused state of a window.
|
* Update focused state of a window.
|
||||||
*/
|
*/
|
||||||
static void win_update_focused(session_t *ps, struct managed_win *w) {
|
static void win_update_focused(session_t *ps, struct managed_win *w) {
|
||||||
if (UNSET != w->focused_force) {
|
if (w->focused_force != UNSET) {
|
||||||
w->focused = w->focused_force;
|
w->focused = w->focused_force;
|
||||||
} else {
|
} else {
|
||||||
w->focused = win_is_focused_raw(ps, w);
|
w->focused = win_is_focused_raw(w);
|
||||||
|
|
||||||
// Use wintype_focus, and treat WM windows and override-redirected
|
// Use wintype_focus, and treat WM windows and override-redirected
|
||||||
// windows specially
|
// windows specially
|
||||||
@@ -205,7 +210,7 @@ static inline bool group_is_focused(session_t *ps, xcb_window_t leader) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto mw = (struct managed_win *)w;
|
auto mw = (struct managed_win *)w;
|
||||||
if (win_get_leader(ps, mw) == leader && win_is_focused_raw(ps, mw)) {
|
if (win_get_leader(ps, mw) == leader && win_is_focused_raw(mw)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,6 +470,12 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
|
|||||||
win_update_prop_shadow(ps, w);
|
win_update_prop_shadow(ps, w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_STATE)) {
|
||||||
|
if (win_update_prop_fullscreen(&ps->c, ps->atoms, w)) {
|
||||||
|
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLIENT_LEADER) ||
|
if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLIENT_LEADER) ||
|
||||||
win_fetch_and_unset_property_stale(w, ps->atoms->aWM_TRANSIENT_FOR)) {
|
win_fetch_and_unset_property_stale(w, ps->atoms->aWM_TRANSIENT_FOR)) {
|
||||||
win_update_leader(ps, w);
|
win_update_leader(ps, w);
|
||||||
@@ -484,18 +495,19 @@ static void init_animation(session_t *ps, struct managed_win *w) {
|
|||||||
}
|
}
|
||||||
static double *anim_x, *anim_y, *anim_w, *anim_h;
|
static double *anim_x, *anim_y, *anim_w, *anim_h;
|
||||||
enum open_window_animation animation;
|
enum open_window_animation animation;
|
||||||
if (ps->o.wintype_option[w->window_type].animation != OPEN_WINDOW_ANIMATION_INVALID
|
|
||||||
&& !w->dwm_mask) {
|
|
||||||
animation = ps->o.wintype_option[w->window_type].animation;
|
animation = ps->o.animation_for_open_window;
|
||||||
}
|
|
||||||
else
|
|
||||||
animation = ps->o.animation_for_open_window;
|
|
||||||
|
|
||||||
if (w->window_type != WINTYPE_TOOLTIP &&
|
if (w->window_type != WINTYPE_TOOLTIP &&
|
||||||
wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR)) {
|
wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR)) {
|
||||||
animation = ps->o.animation_for_transient_window;
|
animation = ps->o.animation_for_transient_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps->o.wintype_option[w->window_type].animation != OPEN_WINDOW_ANIMATION_INVALID
|
||||||
|
&& !w->dwm_mask) {
|
||||||
|
animation = ps->o.wintype_option[w->window_type].animation;
|
||||||
|
}
|
||||||
|
|
||||||
anim_x = &w->animation_center_x, anim_y = &w->animation_center_y;
|
anim_x = &w->animation_center_x, anim_y = &w->animation_center_y;
|
||||||
anim_w = &w->animation_w, anim_h = &w->animation_h;
|
anim_w = &w->animation_w, anim_h = &w->animation_h;
|
||||||
@@ -644,8 +656,9 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
|||||||
// Whether the window was visible before we process the mapped flag. i.e.
|
// Whether the window was visible before we process the mapped flag. i.e.
|
||||||
// is the window just mapped.
|
// is the window just mapped.
|
||||||
bool was_visible = win_is_real_visible(w);
|
bool was_visible = win_is_real_visible(w);
|
||||||
log_trace("Processing flags for window %#010x (%s), was visible: %d", w->base.id,
|
log_trace("Processing flags for window %#010x (%s), was visible: %d, flags: "
|
||||||
w->name, was_visible);
|
"%#" PRIx64,
|
||||||
|
w->base.id, w->name, was_visible, w->flags);
|
||||||
|
|
||||||
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
|
if (win_check_flags_all(w, WIN_FLAGS_MAPPED)) {
|
||||||
map_win_start(ps, w);
|
map_win_start(ps, w);
|
||||||
@@ -753,6 +766,9 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
|||||||
w->g = w->pending_g;
|
w->g = w->pending_g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether a window is fullscreen changes based on its geometry
|
||||||
|
win_update_is_fullscreen(ps, w);
|
||||||
|
|
||||||
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
|
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
|
||||||
win_on_win_size_change(ps, w);
|
win_on_win_size_change(ps, w);
|
||||||
win_update_bounding_shape(ps, w);
|
win_update_bounding_shape(ps, w);
|
||||||
@@ -888,12 +904,14 @@ int win_update_name(session_t *ps, struct managed_win *w) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(wid_get_text_prop(ps, w->client_win, ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
|
if (!(wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
|
||||||
|
ps->atoms->a_NET_WM_NAME, &strlst, &nstr))) {
|
||||||
log_debug("(%#010x): _NET_WM_NAME unset, falling back to "
|
log_debug("(%#010x): _NET_WM_NAME unset, falling back to "
|
||||||
"WM_NAME.",
|
"WM_NAME.",
|
||||||
w->client_win);
|
w->client_win);
|
||||||
|
|
||||||
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_NAME, &strlst, &nstr)) {
|
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
|
||||||
|
ps->atoms->aWM_NAME, &strlst, &nstr)) {
|
||||||
log_debug("Unsetting window name for %#010x", w->client_win);
|
log_debug("Unsetting window name for %#010x", w->client_win);
|
||||||
free(w->name);
|
free(w->name);
|
||||||
w->name = NULL;
|
w->name = NULL;
|
||||||
@@ -920,7 +938,8 @@ static int win_update_role(session_t *ps, struct managed_win *w) {
|
|||||||
char **strlst = NULL;
|
char **strlst = NULL;
|
||||||
int nstr = 0;
|
int nstr = 0;
|
||||||
|
|
||||||
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
|
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win,
|
||||||
|
ps->atoms->aWM_WINDOW_ROLE, &strlst, &nstr)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,7 +1096,7 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
|
|||||||
} else {
|
} else {
|
||||||
// Respect active_opacity only when the window is physically
|
// Respect active_opacity only when the window is physically
|
||||||
// focused
|
// focused
|
||||||
if (win_is_focused_raw(ps, w)) {
|
if (win_is_focused_raw(w)) {
|
||||||
opacity = ps->o.active_opacity;
|
opacity = ps->o.active_opacity;
|
||||||
} else if (!w->focused) {
|
} else if (!w->focused) {
|
||||||
// Respect inactive_opacity in some cases
|
// Respect inactive_opacity in some cases
|
||||||
@@ -1221,7 +1240,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
|
// By setting WIN_FLAGS_SHADOW_STALE, we ask win_process_flags to
|
||||||
// re-create or release the shaodw in based on whether w->shadow
|
// re-create or release the shadow in based on whether w->shadow
|
||||||
// is set.
|
// is set.
|
||||||
win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
|
win_set_flags(w, WIN_FLAGS_SHADOW_STALE);
|
||||||
|
|
||||||
@@ -1278,6 +1297,30 @@ void win_update_prop_shadow(session_t *ps, struct managed_win *w) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update window EWMH fullscreen state.
|
||||||
|
*/
|
||||||
|
bool win_update_prop_fullscreen(struct x_connection *c, const struct atom *atoms,
|
||||||
|
struct managed_win *w) {
|
||||||
|
auto prop = x_get_prop(c, w->client_win, atoms->a_NET_WM_STATE, 12, XCB_ATOM_ATOM, 0);
|
||||||
|
if (!prop.nitems) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_fullscreen = false;
|
||||||
|
for (uint32_t i = 0; i < prop.nitems; i++) {
|
||||||
|
if (prop.atom[i] == atoms->a_NET_WM_STATE_FULLSCREEN) {
|
||||||
|
is_fullscreen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free_winprop(&prop);
|
||||||
|
|
||||||
|
bool changed = w->is_ewmh_fullscreen != is_fullscreen;
|
||||||
|
w->is_ewmh_fullscreen = is_fullscreen;
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
static void win_determine_clip_shadow_above(session_t *ps, struct managed_win *w) {
|
static void win_determine_clip_shadow_above(session_t *ps, struct managed_win *w) {
|
||||||
bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above ||
|
bool should_crop = (ps->o.wintype_option[w->window_type].clip_shadow_above ||
|
||||||
c2_match(ps, w, ps->o.shadow_clip_list, NULL));
|
c2_match(ps, w, ps->o.shadow_clip_list, NULL));
|
||||||
@@ -1406,19 +1449,19 @@ static void win_determine_blur_background(session_t *ps, struct managed_win *w)
|
|||||||
* Determine if a window should have rounded corners.
|
* Determine if a window should have rounded corners.
|
||||||
*/
|
*/
|
||||||
static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
|
static void win_determine_rounded_corners(session_t *ps, struct managed_win *w) {
|
||||||
if (ps->o.corner_radius == 0) {
|
|
||||||
w->corner_radius = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *radius_override = NULL;
|
void *radius_override = NULL;
|
||||||
if (c2_match(ps, w, ps->o.corner_radius_rules, &radius_override)) {
|
if (c2_match(ps, w, ps->o.corner_radius_rules, &radius_override)) {
|
||||||
log_debug("Matched corner rule! %d", w->corner_radius);
|
log_debug("Matched corner rule! %d", w->corner_radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps->o.corner_radius == 0 && !radius_override) {
|
||||||
|
w->corner_radius = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't round full screen windows & excluded windows,
|
// Don't round full screen windows & excluded windows,
|
||||||
// unless we find a corner override in corner_radius_rules
|
// unless we find a corner override in corner_radius_rules
|
||||||
if (!radius_override && ((w && win_is_fullscreen(ps, w)) ||
|
if (!radius_override && ((w && w->is_fullscreen) ||
|
||||||
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL))) {
|
c2_match(ps, w, ps->o.rounded_corners_blacklist, NULL))) {
|
||||||
w->corner_radius = 0;
|
w->corner_radius = 0;
|
||||||
log_debug("Not rounding corners for window %#010x", w->base.id);
|
log_debug("Not rounding corners for window %#010x", w->base.id);
|
||||||
@@ -1485,9 +1528,10 @@ void win_update_opacity_rule(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) {
|
||||||
log_debug("Window %#010x (%s) factor change", w->base.id, w->name);
|
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
|
// Focus and is_fullscreen needs to be updated first, as other rules might depend
|
||||||
// focused state of the window
|
// on the focused state of the window
|
||||||
win_update_focused(ps, w);
|
win_update_focused(ps, w);
|
||||||
|
win_update_is_fullscreen(ps, w);
|
||||||
|
|
||||||
win_determine_shadow(ps, w);
|
win_determine_shadow(ps, w);
|
||||||
win_determine_clip_shadow_above(ps, w);
|
win_determine_clip_shadow_above(ps, w);
|
||||||
@@ -1520,6 +1564,10 @@ 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.
|
||||||
*/
|
*/
|
||||||
void win_on_win_size_change(session_t *ps, struct managed_win *w) {
|
void win_on_win_size_change(session_t *ps, struct managed_win *w) {
|
||||||
|
log_trace("Window %#010x (%s) size changed, was %dx%d, now %dx%d", w->base.id,
|
||||||
|
w->name, w->widthb, w->heightb, w->g.width + w->g.border_width * 2,
|
||||||
|
w->g.height + w->g.border_width * 2);
|
||||||
|
|
||||||
w->widthb = w->g.width + w->g.border_width * 2;
|
w->widthb = w->g.width + w->g.border_width * 2;
|
||||||
w->heightb = w->g.height + w->g.border_width * 2;
|
w->heightb = w->g.height + w->g.border_width * 2;
|
||||||
w->shadow_dx = ps->o.shadow_offset_x;
|
w->shadow_dx = ps->o.shadow_offset_x;
|
||||||
@@ -1533,8 +1581,12 @@ void win_on_win_size_change(session_t *ps, struct managed_win *w) {
|
|||||||
w->state != WSTATE_UNMAPPING);
|
w->state != WSTATE_UNMAPPING);
|
||||||
|
|
||||||
// Invalidate the shadow we built
|
// Invalidate the shadow we built
|
||||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
// Do not set flags if window is unmapping and animation is running
|
||||||
win_release_mask(ps->backend_data, w);
|
if (w->state != WSTATE_UNMAPPED && w->state != WSTATE_DESTROYING &&
|
||||||
|
w->state != WSTATE_UNMAPPING) {
|
||||||
|
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
||||||
|
win_release_mask(ps->backend_data, w);
|
||||||
|
}
|
||||||
ps->pending_updates = true;
|
ps->pending_updates = true;
|
||||||
free_paint(ps, &w->shadow_paint);
|
free_paint(ps, &w->shadow_paint);
|
||||||
}
|
}
|
||||||
@@ -1991,6 +2043,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
|||||||
ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS,
|
ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS,
|
||||||
ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW,
|
ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW,
|
||||||
ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR,
|
ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR,
|
||||||
|
ps->atoms->a_NET_WM_STATE,
|
||||||
};
|
};
|
||||||
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
|
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
|
||||||
|
|
||||||
@@ -2020,7 +2073,7 @@ static inline void win_set_leader(session_t *ps, struct managed_win *w, xcb_wind
|
|||||||
// Update the old and new window group and active_leader if the
|
// Update the old and new window group and active_leader if the
|
||||||
// window could affect their state.
|
// window could affect their state.
|
||||||
xcb_window_t cache_leader = win_get_leader(ps, w);
|
xcb_window_t cache_leader = win_get_leader(ps, w);
|
||||||
if (win_is_focused_raw(ps, w) && cache_leader_old != cache_leader) {
|
if (win_is_focused_raw(w) && cache_leader_old != cache_leader) {
|
||||||
ps->active_leader = cache_leader;
|
ps->active_leader = cache_leader;
|
||||||
|
|
||||||
group_on_factor_change(ps, cache_leader_old);
|
group_on_factor_change(ps, cache_leader_old);
|
||||||
@@ -2104,7 +2157,8 @@ bool win_update_class(session_t *ps, struct managed_win *w) {
|
|||||||
w->class_general = NULL;
|
w->class_general = NULL;
|
||||||
|
|
||||||
// Retrieve the property string list
|
// Retrieve the property string list
|
||||||
if (!wid_get_text_prop(ps, w->client_win, ps->atoms->aWM_CLASS, &strlst, &nstr)) {
|
if (!wid_get_text_prop(&ps->c, ps->atoms, w->client_win, ps->atoms->aWM_CLASS,
|
||||||
|
&strlst, &nstr)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2133,7 +2187,7 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) {
|
|||||||
xcb_window_t leader = win_get_leader(ps, w);
|
xcb_window_t leader = win_get_leader(ps, w);
|
||||||
|
|
||||||
// If the window gets focused, replace the old active_leader
|
// If the window gets focused, replace the old active_leader
|
||||||
if (win_is_focused_raw(ps, w) && leader != ps->active_leader) {
|
if (win_is_focused_raw(w) && leader != ps->active_leader) {
|
||||||
xcb_window_t active_leader_old = ps->active_leader;
|
xcb_window_t active_leader_old = ps->active_leader;
|
||||||
|
|
||||||
ps->active_leader = leader;
|
ps->active_leader = leader;
|
||||||
@@ -2142,7 +2196,7 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) {
|
|||||||
group_on_factor_change(ps, leader);
|
group_on_factor_change(ps, leader);
|
||||||
}
|
}
|
||||||
// If the group get unfocused, remove it from active_leader
|
// If the group get unfocused, remove it from active_leader
|
||||||
else if (!win_is_focused_raw(ps, w) && leader &&
|
else if (!win_is_focused_raw(w) && leader &&
|
||||||
leader == ps->active_leader && !group_is_focused(ps, leader)) {
|
leader == ps->active_leader && !group_is_focused(ps, leader)) {
|
||||||
ps->active_leader = XCB_NONE;
|
ps->active_leader = XCB_NONE;
|
||||||
group_on_factor_change(ps, leader);
|
group_on_factor_change(ps, leader);
|
||||||
@@ -2155,7 +2209,7 @@ static void win_on_focus_change(session_t *ps, struct managed_win *w) {
|
|||||||
#ifdef CONFIG_DBUS
|
#ifdef CONFIG_DBUS
|
||||||
// Send D-Bus signal
|
// Send D-Bus signal
|
||||||
if (ps->o.dbus) {
|
if (ps->o.dbus) {
|
||||||
if (win_is_focused_raw(ps, w)) {
|
if (win_is_focused_raw(w)) {
|
||||||
cdbus_ev_win_focusin(ps, &w->base);
|
cdbus_ev_win_focusin(ps, &w->base);
|
||||||
} else {
|
} else {
|
||||||
cdbus_ev_win_focusout(ps, &w->base);
|
cdbus_ev_win_focusout(ps, &w->base);
|
||||||
@@ -2173,15 +2227,18 @@ void win_set_focused(session_t *ps, struct managed_win *w) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (win_is_focused_raw(ps, w)) {
|
if (w->is_ewmh_focused) {
|
||||||
|
assert(ps->active_win == w);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto old_active_win = ps->active_win;
|
auto old_active_win = ps->active_win;
|
||||||
ps->active_win = w;
|
ps->active_win = w;
|
||||||
assert(win_is_focused_raw(ps, w));
|
w->is_ewmh_focused = true;
|
||||||
|
|
||||||
if (old_active_win) {
|
if (old_active_win) {
|
||||||
|
assert(old_active_win->is_ewmh_focused);
|
||||||
|
old_active_win->is_ewmh_focused = false;
|
||||||
win_on_focus_change(ps, old_active_win);
|
win_on_focus_change(ps, old_active_win);
|
||||||
}
|
}
|
||||||
win_on_focus_change(ps, w);
|
win_on_focus_change(ps, w);
|
||||||
@@ -2254,7 +2311,7 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
|
|||||||
|
|
||||||
// Add border width because we are using a different origin.
|
// Add border width because we are using a different origin.
|
||||||
// X thinks the top left of the inner window is the origin
|
// X thinks the top left of the inner window is the origin
|
||||||
// (for the bounding shape, althought xcb_get_geometry thinks
|
// (for the bounding shape, although xcb_get_geometry thinks
|
||||||
// the outer top left (outer means outside of the window
|
// the outer top left (outer means outside of the window
|
||||||
// border) is the origin),
|
// border) is the origin),
|
||||||
// We think the top left of the border is the origin
|
// We think the top left of the border is the origin
|
||||||
@@ -2274,14 +2331,12 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
|
|||||||
|
|
||||||
// Window shape changed, we should free old wpaint and shadow pict
|
// Window shape changed, we should free old wpaint and shadow pict
|
||||||
// log_trace("free out dated pict");
|
// log_trace("free out dated pict");
|
||||||
win_set_flags(w, WIN_FLAGS_IMAGES_STALE);
|
win_set_flags(w, WIN_FLAGS_IMAGES_STALE | WIN_FLAGS_FACTOR_CHANGED);
|
||||||
win_release_mask(ps->backend_data, w);
|
win_release_mask(ps->backend_data, w);
|
||||||
ps->pending_updates = true;
|
ps->pending_updates = true;
|
||||||
|
|
||||||
free_paint(ps, &w->paint);
|
free_paint(ps, &w->paint);
|
||||||
free_paint(ps, &w->shadow_paint);
|
free_paint(ps, &w->shadow_paint);
|
||||||
|
|
||||||
win_on_factor_change(ps, w);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2597,7 +2652,7 @@ bool destroy_win_start(session_t *ps, struct win *w) {
|
|||||||
HASH_DEL(ps->windows, w);
|
HASH_DEL(ps->windows, w);
|
||||||
|
|
||||||
if (!w->managed || mw->state == WSTATE_UNMAPPED) {
|
if (!w->managed || mw->state == WSTATE_UNMAPPED) {
|
||||||
// Window is already unmapped, or is an unmanged window, just
|
// Window is already unmapped, or is an unmanaged window, just
|
||||||
// destroy it
|
// destroy it
|
||||||
destroy_win_finish(ps, w);
|
destroy_win_finish(ps, w);
|
||||||
return true;
|
return true;
|
||||||
@@ -2622,6 +2677,13 @@ bool destroy_win_start(session_t *ps, struct win *w) {
|
|||||||
add_damage_from_win(ps, mw);
|
add_damage_from_win(ps, mw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (win_check_flags_all(mw, WIN_FLAGS_CLIENT_STALE)) {
|
||||||
|
mw->client_win = mw->base.id;
|
||||||
|
mw->wmwin = !mw->a.override_redirect;
|
||||||
|
log_debug("(%#010x): client self (%s)", mw->base.id,
|
||||||
|
(mw->wmwin ? "wmwin" : "override-redirected"));
|
||||||
|
}
|
||||||
|
|
||||||
// Clear some flags about stale window information. Because now
|
// Clear some flags about stale window information. Because now
|
||||||
// the window is destroyed, we can't update them anyway.
|
// the window is destroyed, we can't update them anyway.
|
||||||
win_clear_flags(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE |
|
win_clear_flags(mw, WIN_FLAGS_SIZE_STALE | WIN_FLAGS_POSITION_STALE |
|
||||||
@@ -3011,41 +3073,6 @@ struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wi
|
|||||||
return (struct managed_win *)w;
|
return (struct managed_win *)w;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a rectangle includes the whole screen.
|
|
||||||
*/
|
|
||||||
static inline bool rect_is_fullscreen(const session_t *ps, int x, int y, int wid, int hei) {
|
|
||||||
return (x <= 0 && y <= 0 && (x + wid) >= ps->root_width && (y + hei) >= ps->root_height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a window is fulscreen using EWMH
|
|
||||||
*
|
|
||||||
* TODO(yshui) cache this property
|
|
||||||
*/
|
|
||||||
static inline bool
|
|
||||||
win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_window_t w) {
|
|
||||||
xcb_get_property_cookie_t prop =
|
|
||||||
xcb_get_property(c, 0, w, a->a_NET_WM_STATE, XCB_ATOM_ATOM, 0, 12);
|
|
||||||
xcb_get_property_reply_t *reply = xcb_get_property_reply(c, prop, NULL);
|
|
||||||
if (!reply) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reply->length) {
|
|
||||||
xcb_atom_t *val = xcb_get_property_value(reply);
|
|
||||||
for (uint32_t i = 0; i < reply->length; i++) {
|
|
||||||
if (val[i] != a->a_NET_WM_STATE_FULLSCREEN) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
free(reply);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(reply);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set flags on a window. Some sanity checks are performed
|
/// Set flags on a window. Some sanity checks are performed
|
||||||
void win_set_flags(struct managed_win *w, uint64_t flags) {
|
void win_set_flags(struct managed_win *w, uint64_t flags) {
|
||||||
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
|
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
|
||||||
@@ -3130,13 +3157,15 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
|
|||||||
*
|
*
|
||||||
* It's not using w->border_size for performance measures.
|
* It's not using w->border_size for performance measures.
|
||||||
*/
|
*/
|
||||||
bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) {
|
void win_update_is_fullscreen(const session_t *ps, struct managed_win *w) {
|
||||||
if (!ps->o.no_ewmh_fullscreen &&
|
if (!ps->o.no_ewmh_fullscreen && w->is_ewmh_fullscreen) {
|
||||||
win_is_fullscreen_xcb(ps->c.c, ps->atoms, w->client_win)) {
|
w->is_fullscreen = true;
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) &&
|
w->is_fullscreen = w->g.x <= 0 && w->g.y <= 0 &&
|
||||||
(!w->bounding_shaped || w->rounded_corners);
|
(w->g.x + w->widthb) >= ps->root_width &&
|
||||||
|
(w->g.y + w->heightb) >= ps->root_height &&
|
||||||
|
(!w->bounding_shaped || w->rounded_corners);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3162,8 +3191,8 @@ bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *
|
|||||||
* Check if a window is focused, without using any focus rules or forced focus
|
* Check if a window is focused, without using any focus rules or forced focus
|
||||||
* settings
|
* settings
|
||||||
*/
|
*/
|
||||||
bool win_is_focused_raw(const session_t *ps, const struct managed_win *w) {
|
bool win_is_focused_raw(const struct managed_win *w) {
|
||||||
return w->a.map_state == XCB_MAP_STATE_VIEWABLE && ps->active_win == w;
|
return w->a.map_state == XCB_MAP_STATE_VIEWABLE && w->is_ewmh_focused;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the managed window immediately below `i` in the window stack
|
// Find the managed window immediately below `i` in the window stack
|
||||||
|
|||||||
35
src/win.h
35
src/win.h
@@ -113,10 +113,10 @@ struct managed_win {
|
|||||||
struct win base;
|
struct win base;
|
||||||
/// backend data attached to this window. Only available when
|
/// backend data attached to this window. Only available when
|
||||||
/// `state` is not UNMAPPED
|
/// `state` is not UNMAPPED
|
||||||
void *win_image;
|
image_handle win_image;
|
||||||
void *old_win_image; // Old window image for interpolating window contents during animations
|
image_handle old_win_image; // Old window image for interpolating window contents during animations
|
||||||
void *shadow_image;
|
image_handle shadow_image;
|
||||||
void *mask_image;
|
image_handle mask_image;
|
||||||
/// Pointer to the next higher window to paint.
|
/// Pointer to the next higher window to paint.
|
||||||
struct managed_win *prev_trans;
|
struct managed_win *prev_trans;
|
||||||
/// Number of windows above this window
|
/// Number of windows above this window
|
||||||
@@ -233,6 +233,14 @@ struct managed_win {
|
|||||||
char *class_general;
|
char *class_general;
|
||||||
/// <code>WM_WINDOW_ROLE</code> value of the window.
|
/// <code>WM_WINDOW_ROLE</code> value of the window.
|
||||||
char *role;
|
char *role;
|
||||||
|
/// Whether the window sets the EWMH fullscreen property.
|
||||||
|
bool is_ewmh_fullscreen;
|
||||||
|
/// Whether the window should be considered fullscreen. Based on
|
||||||
|
/// `is_ewmh_fullscreen`, or the windows spatial relation with the
|
||||||
|
/// root window. Which one is used is determined by user configuration.
|
||||||
|
bool is_fullscreen;
|
||||||
|
/// Whether the window is the EWMH active window.
|
||||||
|
bool is_ewmh_focused;
|
||||||
|
|
||||||
// Opacity-related members
|
// Opacity-related members
|
||||||
/// Current window opacity.
|
/// Current window opacity.
|
||||||
@@ -278,13 +286,13 @@ struct managed_win {
|
|||||||
switch_t shadow_force;
|
switch_t shadow_force;
|
||||||
/// Opacity of the shadow. Affected by window opacity and frame opacity.
|
/// Opacity of the shadow. Affected by window opacity and frame opacity.
|
||||||
double shadow_opacity;
|
double shadow_opacity;
|
||||||
/// X offset of shadow. Affected by commandline argument.
|
/// X offset of shadow. Affected by command line argument.
|
||||||
int shadow_dx;
|
int shadow_dx;
|
||||||
/// Y offset of shadow. Affected by commandline argument.
|
/// Y offset of shadow. Affected by command line argument.
|
||||||
int shadow_dy;
|
int shadow_dy;
|
||||||
/// Width of shadow. Affected by window size and commandline argument.
|
/// Width of shadow. Affected by window size and command line argument.
|
||||||
int shadow_width;
|
int shadow_width;
|
||||||
/// Height of shadow. Affected by window size and commandline argument.
|
/// Height of shadow. Affected by window size and command line argument.
|
||||||
int shadow_height;
|
int shadow_height;
|
||||||
/// Picture to render shadow. Affected by window size.
|
/// Picture to render shadow. Affected by window size.
|
||||||
paint_t shadow_paint;
|
paint_t shadow_paint;
|
||||||
@@ -383,6 +391,8 @@ void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw);
|
|||||||
*/
|
*/
|
||||||
// XXX was win_border_size
|
// XXX was win_border_size
|
||||||
void win_update_bounding_shape(session_t *ps, struct managed_win *w);
|
void win_update_bounding_shape(session_t *ps, struct managed_win *w);
|
||||||
|
/// Recheck if a window is fullscreen
|
||||||
|
void win_update_is_fullscreen(const session_t *ps, struct managed_win *w);
|
||||||
/**
|
/**
|
||||||
* Check if a window has BYPASS_COMPOSITOR property set
|
* Check if a window has BYPASS_COMPOSITOR property set
|
||||||
*/
|
*/
|
||||||
@@ -458,17 +468,10 @@ struct managed_win *find_toplevel(session_t *ps, xcb_window_t id);
|
|||||||
*/
|
*/
|
||||||
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid);
|
struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wid);
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a window is a fullscreen window.
|
|
||||||
*
|
|
||||||
* It's not using w->border_size for performance measures.
|
|
||||||
*/
|
|
||||||
bool attr_pure win_is_fullscreen(const session_t *ps, const struct managed_win *w);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a window is focused, without using any focus rules or forced focus settings
|
* Check if a window is focused, without using any focus rules or forced focus settings
|
||||||
*/
|
*/
|
||||||
bool attr_pure win_is_focused_raw(const session_t *ps, const struct managed_win *w);
|
bool attr_pure win_is_focused_raw(const struct managed_win *w);
|
||||||
|
|
||||||
/// check if window has ARGB visual
|
/// check if window has ARGB visual
|
||||||
bool attr_pure win_has_alpha(const struct managed_win *w);
|
bool attr_pure win_has_alpha(const struct managed_win *w);
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ typedef enum {
|
|||||||
} winstate_t;
|
} winstate_t;
|
||||||
|
|
||||||
enum win_flags {
|
enum win_flags {
|
||||||
// Note: *_NONE flags are mostly redudant and meant for detecting logical errors
|
// Note: *_NONE flags are mostly redundant and meant for detecting logical errors
|
||||||
// in the code
|
// in the code
|
||||||
|
|
||||||
/// pixmap is out of date, will be update in win_process_flags
|
/// pixmap is out of date, will be update in win_process_flags
|
||||||
|
|||||||
114
src/x.c
114
src/x.c
@@ -16,6 +16,7 @@
|
|||||||
#include <xcb/render.h>
|
#include <xcb/render.h>
|
||||||
#include <xcb/sync.h>
|
#include <xcb/sync.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/xcb_aux.h>
|
||||||
#include <xcb/xcb_renderutil.h>
|
#include <xcb/xcb_renderutil.h>
|
||||||
#include <xcb/xfixes.h>
|
#include <xcb/xfixes.h>
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ void x_connection_init(struct x_connection *c, Display *dpy) {
|
|||||||
c->previous_xerror_handler = XSetErrorHandler(xerror);
|
c->previous_xerror_handler = XSetErrorHandler(xerror);
|
||||||
|
|
||||||
c->screen = DefaultScreen(dpy);
|
c->screen = DefaultScreen(dpy);
|
||||||
c->screen_info = x_screen_of_display(c, c->screen);
|
c->screen_info = xcb_aux_get_screen(c->c, c->screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -181,10 +182,9 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
|
|||||||
/**
|
/**
|
||||||
* Get the value of a text property of a window.
|
* Get the value of a text property of a window.
|
||||||
*/
|
*/
|
||||||
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
|
bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
|
||||||
int *pnstr) {
|
xcb_atom_t prop, char ***pstrlst, int *pnstr) {
|
||||||
assert(ps->server_grabbed);
|
auto prop_info = x_get_prop_info(c, wid, prop);
|
||||||
auto prop_info = x_get_prop_info(&ps->c, wid, prop);
|
|
||||||
auto type = prop_info.type;
|
auto type = prop_info.type;
|
||||||
auto format = prop_info.format;
|
auto format = prop_info.format;
|
||||||
auto length = prop_info.length;
|
auto length = prop_info.length;
|
||||||
@@ -193,8 +193,7 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type != XCB_ATOM_STRING && type != ps->atoms->aUTF8_STRING &&
|
if (type != XCB_ATOM_STRING && type != atoms->aUTF8_STRING && type != atoms->aC_STRING) {
|
||||||
type != ps->atoms->aC_STRING) {
|
|
||||||
log_warn("Text property %d of window %#010x has unsupported type: %d",
|
log_warn("Text property %d of window %#010x has unsupported type: %d",
|
||||||
prop, wid, type);
|
prop, wid, type);
|
||||||
return false;
|
return false;
|
||||||
@@ -209,7 +208,7 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
|
|||||||
xcb_generic_error_t *e = NULL;
|
xcb_generic_error_t *e = NULL;
|
||||||
auto word_count = (length + 4 - 1) / 4;
|
auto word_count = (length + 4 - 1) / 4;
|
||||||
auto r = xcb_get_property_reply(
|
auto r = xcb_get_property_reply(
|
||||||
ps->c.c, xcb_get_property(ps->c.c, 0, wid, prop, type, 0, word_count), &e);
|
c->c, xcb_get_property(c->c, 0, wid, prop, type, 0, word_count), &e);
|
||||||
if (!r) {
|
if (!r) {
|
||||||
log_debug_x_error(e, "Failed to get window property for %#010x", wid);
|
log_debug_x_error(e, "Failed to get window property for %#010x", wid);
|
||||||
free(e);
|
free(e);
|
||||||
@@ -321,6 +320,17 @@ xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standa
|
|||||||
return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
|
return x_get_visual_for_pictfmt(g_pictfmts, pictfmt->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xcb_visualid_t x_get_visual_for_depth(xcb_screen_t *screen, uint8_t depth) {
|
||||||
|
xcb_depth_iterator_t depth_it = xcb_screen_allowed_depths_iterator(screen);
|
||||||
|
for (; depth_it.rem; xcb_depth_next(&depth_it)) {
|
||||||
|
if (depth_it.data->depth == depth) {
|
||||||
|
return xcb_depth_visuals_iterator(depth_it.data).data->visual_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return XCB_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
xcb_render_pictformat_t
|
xcb_render_pictformat_t
|
||||||
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
|
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
|
||||||
x_get_server_pictfmts(c);
|
x_get_server_pictfmts(c);
|
||||||
@@ -330,24 +340,6 @@ x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
|
|||||||
return pictfmt->id;
|
return pictfmt->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
int x_get_visual_depth(struct x_connection *c, xcb_visualid_t visual) {
|
|
||||||
auto setup = xcb_get_setup(c->c);
|
|
||||||
for (auto screen = xcb_setup_roots_iterator(setup); screen.rem;
|
|
||||||
xcb_screen_next(&screen)) {
|
|
||||||
for (auto depth = xcb_screen_allowed_depths_iterator(screen.data);
|
|
||||||
depth.rem; xcb_depth_next(&depth)) {
|
|
||||||
const int len = xcb_depth_visuals_length(depth.data);
|
|
||||||
const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
if (visual == visuals[i].visual_id) {
|
|
||||||
return depth.data->depth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_render_picture_t
|
xcb_render_picture_t
|
||||||
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *c,
|
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *c,
|
||||||
const xcb_render_pictforminfo_t *pictfmt,
|
const xcb_render_pictforminfo_t *pictfmt,
|
||||||
@@ -459,6 +451,34 @@ bool x_fetch_region(struct x_connection *c, xcb_xfixes_region_t r, pixman_region
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool x_set_region(struct x_connection *c, xcb_xfixes_region_t dst, const region_t *src) {
|
||||||
|
if (!src || dst == XCB_NONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t nrects = 0;
|
||||||
|
const rect_t *rects = pixman_region32_rectangles((region_t *)src, &nrects);
|
||||||
|
if (!rects || nrects < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_rectangle_t *xrects = ccalloc(nrects, xcb_rectangle_t);
|
||||||
|
for (int32_t i = 0; i < nrects; i++) {
|
||||||
|
xrects[i] =
|
||||||
|
(xcb_rectangle_t){.x = to_i16_checked(rects[i].x1),
|
||||||
|
.y = to_i16_checked(rects[i].y1),
|
||||||
|
.width = to_u16_checked(rects[i].x2 - rects[i].x1),
|
||||||
|
.height = to_u16_checked(rects[i].y2 - rects[i].y1)};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success =
|
||||||
|
XCB_AWAIT_VOID(xcb_xfixes_set_region, c->c, dst, to_u32_checked(nrects), xrects);
|
||||||
|
|
||||||
|
free(xrects);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t x_create_region(struct x_connection *c, const region_t *reg) {
|
uint32_t x_create_region(struct x_connection *c, const region_t *reg) {
|
||||||
if (!reg) {
|
if (!reg) {
|
||||||
return XCB_NONE;
|
return XCB_NONE;
|
||||||
@@ -562,9 +582,7 @@ _x_strerror(unsigned long serial, uint8_t major, uint16_t minor, uint8_t error_c
|
|||||||
const char *name = "Unknown";
|
const char *name = "Unknown";
|
||||||
|
|
||||||
#define CASESTRRET(s) \
|
#define CASESTRRET(s) \
|
||||||
case s: \
|
case s: name = #s; break
|
||||||
name = #s; \
|
|
||||||
break
|
|
||||||
|
|
||||||
#define CASESTRRET2(s) \
|
#define CASESTRRET2(s) \
|
||||||
case XCB_##s: name = #s; break
|
case XCB_##s: name = #s; break
|
||||||
@@ -689,27 +707,6 @@ xcb_pixmap_t x_create_pixmap(struct x_connection *c, uint8_t depth, int width, i
|
|||||||
return XCB_NONE;
|
return XCB_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate a pixmap.
|
|
||||||
*
|
|
||||||
* Detect whether the pixmap is valid with XGetGeometry. Well, maybe there
|
|
||||||
* are better ways.
|
|
||||||
*/
|
|
||||||
bool x_validate_pixmap(struct x_connection *c, xcb_pixmap_t pixmap) {
|
|
||||||
if (pixmap == XCB_NONE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto r = xcb_get_geometry_reply(c->c, xcb_get_geometry(c->c, pixmap), NULL);
|
|
||||||
if (!r) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ret = r->width && r->height;
|
|
||||||
free(r);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We don't use the _XSETROOT_ID root window property as a source of the background
|
/// We don't use the _XSETROOT_ID root window property as a source of the background
|
||||||
/// pixmap because it most likely points to a dummy pixmap used to keep the colormap
|
/// pixmap because it most likely points to a dummy pixmap used to keep the colormap
|
||||||
/// associated with the background pixmap alive but we listen for it's changes and update
|
/// associated with the background pixmap alive but we listen for it's changes and update
|
||||||
@@ -856,8 +853,8 @@ void x_create_convolution_kernel(const conv *kernel, double center,
|
|||||||
/// Returns {-1, -1, -1, -1, -1, 0} on failure
|
/// Returns {-1, -1, -1, -1, -1, 0} on failure
|
||||||
struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t visual) {
|
struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t visual) {
|
||||||
auto pictfmt = x_get_pictform_for_visual(c, visual);
|
auto pictfmt = x_get_pictform_for_visual(c, visual);
|
||||||
auto depth = x_get_visual_depth(c, visual);
|
auto depth = xcb_aux_get_depth_of_visual(c->screen_info, visual);
|
||||||
if (!pictfmt || depth == -1) {
|
if (!pictfmt || depth == 0) {
|
||||||
log_error("Invalid visual %#03x", visual);
|
log_error("Invalid visual %#03x", visual);
|
||||||
return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
|
return (struct xvisual_info){-1, -1, -1, -1, -1, 0};
|
||||||
}
|
}
|
||||||
@@ -882,19 +879,6 @@ struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t vis
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb_screen_t *x_screen_of_display(struct x_connection *c, int screen) {
|
|
||||||
xcb_screen_iterator_t iter;
|
|
||||||
|
|
||||||
iter = xcb_setup_roots_iterator(xcb_get_setup(c->c));
|
|
||||||
for (; iter.rem; --screen, xcb_screen_next(&iter)) {
|
|
||||||
if (screen == 0) {
|
|
||||||
return iter.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void x_update_monitors(struct x_connection *c, struct x_monitors *m) {
|
void x_update_monitors(struct x_connection *c, struct x_monitors *m) {
|
||||||
x_free_monitor_info(m);
|
x_free_monitor_info(m);
|
||||||
|
|
||||||
|
|||||||
26
src/x.h
26
src/x.h
@@ -27,6 +27,7 @@ typedef struct winprop {
|
|||||||
int16_t *p16;
|
int16_t *p16;
|
||||||
int32_t *p32;
|
int32_t *p32;
|
||||||
uint32_t *c32; // 32bit cardinal
|
uint32_t *c32; // 32bit cardinal
|
||||||
|
xcb_atom_t *atom;
|
||||||
};
|
};
|
||||||
unsigned long nitems;
|
unsigned long nitems;
|
||||||
xcb_atom_t type;
|
xcb_atom_t type;
|
||||||
@@ -207,17 +208,6 @@ void x_discard_pending(struct x_connection *c, uint32_t sequence);
|
|||||||
/// This function logs X errors, or aborts the program based on severity of the error.
|
/// This function logs X errors, or aborts the program based on severity of the error.
|
||||||
void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev);
|
void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev);
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a request to X server and get the reply to make sure all previous
|
|
||||||
* requests are processed, and their replies received
|
|
||||||
*
|
|
||||||
* xcb_get_input_focus is used here because it is the same request used by
|
|
||||||
* libX11
|
|
||||||
*/
|
|
||||||
static inline void x_sync(struct x_connection *c) {
|
|
||||||
free(xcb_get_input_focus_reply(c->c, xcb_get_input_focus(c->c), NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a specific attribute of a window.
|
* Get a specific attribute of a window.
|
||||||
*
|
*
|
||||||
@@ -271,12 +261,11 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
|
|||||||
* array
|
* array
|
||||||
* @param[out] pnstr Number of strings in the array
|
* @param[out] pnstr Number of strings in the array
|
||||||
*/
|
*/
|
||||||
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
|
bool wid_get_text_prop(struct x_connection *c, struct atom *atoms, xcb_window_t wid,
|
||||||
int *pnstr);
|
xcb_atom_t prop, char ***pstrlst, int *pnstr);
|
||||||
|
|
||||||
const xcb_render_pictforminfo_t *
|
const xcb_render_pictforminfo_t *
|
||||||
x_get_pictform_for_visual(struct x_connection *, xcb_visualid_t);
|
x_get_pictform_for_visual(struct x_connection *, xcb_visualid_t);
|
||||||
int x_get_visual_depth(struct x_connection *, xcb_visualid_t);
|
|
||||||
|
|
||||||
xcb_render_picture_t
|
xcb_render_picture_t
|
||||||
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *,
|
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *,
|
||||||
@@ -321,6 +310,9 @@ x_create_picture_with_visual(struct x_connection *, int w, int h, xcb_visualid_t
|
|||||||
/// Fetch a X region and store it in a pixman region
|
/// Fetch a X region and store it in a pixman region
|
||||||
bool x_fetch_region(struct x_connection *, xcb_xfixes_region_t r, region_t *res);
|
bool x_fetch_region(struct x_connection *, xcb_xfixes_region_t r, region_t *res);
|
||||||
|
|
||||||
|
/// Set an X region to a pixman region
|
||||||
|
bool x_set_region(struct x_connection *c, xcb_xfixes_region_t dst, const region_t *src);
|
||||||
|
|
||||||
/// Create a X region from a pixman region
|
/// Create a X region from a pixman region
|
||||||
uint32_t x_create_region(struct x_connection *c, const region_t *reg);
|
uint32_t x_create_region(struct x_connection *c, const region_t *reg);
|
||||||
|
|
||||||
@@ -356,8 +348,6 @@ const char *x_strerror(xcb_generic_error_t *e);
|
|||||||
|
|
||||||
xcb_pixmap_t x_create_pixmap(struct x_connection *, uint8_t depth, int width, int height);
|
xcb_pixmap_t x_create_pixmap(struct x_connection *, uint8_t depth, int width, int height);
|
||||||
|
|
||||||
bool x_validate_pixmap(struct x_connection *, xcb_pixmap_t pxmap);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a <code>winprop_t</code>.
|
* Free a <code>winprop_t</code>.
|
||||||
*
|
*
|
||||||
@@ -408,11 +398,11 @@ struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t vis
|
|||||||
|
|
||||||
xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standard_t std);
|
xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standard_t std);
|
||||||
|
|
||||||
|
xcb_visualid_t x_get_visual_for_depth(xcb_screen_t *screen, uint8_t depth);
|
||||||
|
|
||||||
xcb_render_pictformat_t
|
xcb_render_pictformat_t
|
||||||
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std);
|
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std);
|
||||||
|
|
||||||
xcb_screen_t *x_screen_of_display(struct x_connection *c, int screen);
|
|
||||||
|
|
||||||
/// Populates a `struct x_monitors` with the current monitor configuration.
|
/// Populates a `struct x_monitors` with the current monitor configuration.
|
||||||
void x_update_monitors(struct x_connection *, struct x_monitors *);
|
void x_update_monitors(struct x_connection *, struct x_monitors *);
|
||||||
/// Free memory allocated for a `struct x_monitors`.
|
/// Free memory allocated for a `struct x_monitors`.
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ struct test_file_metadata __attribute__((weak)) * test_file_head;
|
|||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define TEST_NOTEQUAL(a, b) \
|
||||||
|
do { \
|
||||||
|
if ((a) == (b)) { \
|
||||||
|
SET_FAILURE(#a " == " #b, false); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TEST_TRUE(a) \
|
#define TEST_TRUE(a) \
|
||||||
do { \
|
do { \
|
||||||
if (!(a)) { \
|
if (!(a)) { \
|
||||||
@@ -69,11 +77,13 @@ struct test_file_metadata __attribute__((weak)) * test_file_head;
|
|||||||
#define TEST_STREQUAL(a, b) \
|
#define TEST_STREQUAL(a, b) \
|
||||||
do { \
|
do { \
|
||||||
if (strcmp(a, b) != 0) { \
|
if (strcmp(a, b) != 0) { \
|
||||||
const char *part2 = " != " #b; \
|
const char *test_strequal__part2 = " != " #b; \
|
||||||
size_t len = strlen(a) + strlen(part2) + 3; \
|
size_t test_strequal__len = \
|
||||||
char *buf = malloc(len); \
|
strlen(a) + strlen(test_strequal__part2) + 3; \
|
||||||
snprintf(buf, len, "\"%s\"%s", a, part2); \
|
char *test_strequal__buf = malloc(test_strequal__len); \
|
||||||
SET_FAILURE(buf, true); \
|
snprintf(test_strequal__buf, test_strequal__len, "\"%s\"%s", a, \
|
||||||
|
test_strequal__part2); \
|
||||||
|
SET_FAILURE(test_strequal__buf, true); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
@@ -81,11 +91,27 @@ struct test_file_metadata __attribute__((weak)) * test_file_head;
|
|||||||
#define TEST_STRNEQUAL(a, b, len) \
|
#define TEST_STRNEQUAL(a, b, len) \
|
||||||
do { \
|
do { \
|
||||||
if (strncmp(a, b, len) != 0) { \
|
if (strncmp(a, b, len) != 0) { \
|
||||||
const char *part2 = " != " #b; \
|
const char *test_strnequal__part2 = " != " #b; \
|
||||||
size_t len2 = len + strlen(part2) + 3; \
|
size_t test_strnequal__len2 = \
|
||||||
char *buf = malloc(len2); \
|
len + strlen(test_strnequal__part2) + 3; \
|
||||||
snprintf(buf, len2, "\"%.*s\"%s", (int)len, a, part2); \
|
char *test_strnequal__buf = malloc(test_strnequal__len2); \
|
||||||
SET_FAILURE(buf, true); \
|
snprintf(test_strnequal__buf, test_strnequal__len2, \
|
||||||
|
"\"%.*s\"%s", (int)len, a, test_strnequal__part2); \
|
||||||
|
SET_FAILURE(test_strnequal__buf, true); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TEST_STREQUAL3(str, expected, len) \
|
||||||
|
do { \
|
||||||
|
if (len != strlen(expected) || strncmp(str, expected, len) != 0) { \
|
||||||
|
const char *test_strequal3__part2 = " != " #expected; \
|
||||||
|
size_t test_strequal3__len2 = \
|
||||||
|
len + strlen(test_strequal3__part2) + 3; \
|
||||||
|
char *test_strequal3__buf = malloc(test_strequal3__len2); \
|
||||||
|
snprintf(test_strequal3__buf, test_strequal3__len2, \
|
||||||
|
"\"%.*s\"%s", (int)len, str, test_strequal3__part2); \
|
||||||
|
SET_FAILURE(test_strequal3__buf, true); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
@@ -199,6 +225,9 @@ static inline void __attribute__((constructor(102))) run_tests(void) {
|
|||||||
#define TEST_EQUAL(a, b) \
|
#define TEST_EQUAL(a, b) \
|
||||||
(void)(a); \
|
(void)(a); \
|
||||||
(void)(b)
|
(void)(b)
|
||||||
|
#define TEST_NOTEQUAL(a, b) \
|
||||||
|
(void)(a); \
|
||||||
|
(void)(b)
|
||||||
#define TEST_TRUE(a) (void)(a)
|
#define TEST_TRUE(a) (void)(a)
|
||||||
#define TEST_STREQUAL(a, b) \
|
#define TEST_STREQUAL(a, b) \
|
||||||
(void)(a); \
|
(void)(a); \
|
||||||
@@ -207,5 +236,8 @@ static inline void __attribute__((constructor(102))) run_tests(void) {
|
|||||||
(void)(a); \
|
(void)(a); \
|
||||||
(void)(b); \
|
(void)(b); \
|
||||||
(void)(len)
|
(void)(len)
|
||||||
|
#define TEST_STREQUAL3(str, expected, len) \
|
||||||
|
(void)(str); \
|
||||||
|
(void)(expected); \
|
||||||
|
(void)(len)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user